##// END OF EJS Templates
Fixed Windows installation based on work of Mantis406 fork: "Replace py-bcrypt to make Windows installation easier"...
marcink -
r1118:b0e2c949 beta
parent child Browse files
Show More
@@ -1,52 +1,53 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software; you can redistribute it and/or
14 # This program is free software; you can redistribute it and/or
15 # modify it under the terms of the GNU General Public License
15 # modify it under the terms of the GNU General Public License
16 # as published by the Free Software Foundation; version 2
16 # as published by the Free Software Foundation; version 2
17 # of the License or (at your opinion) any later version of the license.
17 # of the License or (at your opinion) any later version of the license.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program; if not, write to the Free Software
25 # along with this program; if not, write to the Free Software
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
27 # MA 02110-1301, USA.
27 # MA 02110-1301, USA.
28
28 import platform
29
29
30 VERSION = (1, 2, 0, 'beta')
30 VERSION = (1, 2, 0, 'beta')
31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
31 __version__ = '.'.join((str(each) for each in VERSION[:4]))
32 __dbversion__ = 3 #defines current db version for migrations
32 __dbversion__ = 3 #defines current db version for migrations
33 __platform__ = platform.system()
33
34
34 try:
35 try:
35 from rhodecode.lib.utils import get_current_revision
36 from rhodecode.lib.utils import get_current_revision
36 _rev = get_current_revision()
37 _rev = get_current_revision()
37 except ImportError:
38 except ImportError:
38 #this is needed when doing some setup.py operations
39 #this is needed when doing some setup.py operations
39 _rev = False
40 _rev = False
40
41
41 if len(VERSION) > 3 and _rev:
42 if len(VERSION) > 3 and _rev:
42 __version__ += ' [rev:%s]' % _rev[0]
43 __version__ += ' [rev:%s]' % _rev[0]
43
44
44 def get_version():
45 def get_version():
45 """Returns shorter version (digit parts only) as string."""
46 """Returns shorter version (digit parts only) as string."""
46
47
47 return '.'.join((str(each) for each in VERSION[:3]))
48 return '.'.join((str(each) for each in VERSION[:3]))
48
49
49 BACKENDS = {
50 BACKENDS = {
50 'hg': 'Mercurial repository',
51 'hg': 'Mercurial repository',
51 #'git': 'Git repository',
52 #'git': 'Git repository',
52 }
53 }
@@ -1,550 +1,591 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
11 """
12 # This program is free software; you can redistribute it and/or
12 # This program is free software; you can redistribute it and/or
13 # modify it under the terms of the GNU General Public License
13 # modify it under the terms of the GNU General Public License
14 # as published by the Free Software Foundation; version 2
14 # as published by the Free Software Foundation; version 2
15 # of the License or (at your opinion) any later version of the license.
15 # of the License or (at your opinion) any later version of the license.
16 #
16 #
17 # This program is distributed in the hope that it will be useful,
17 # This program is distributed in the hope that it will be useful,
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # GNU General Public License for more details.
20 # GNU General Public License for more details.
21 #
21 #
22 # You should have received a copy of the GNU General Public License
22 # You should have received a copy of the GNU General Public License
23 # along with this program; if not, write to the Free Software
23 # along with this program; if not, write to the Free Software
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # MA 02110-1301, USA.
25 # MA 02110-1301, USA.
26
26
27 import bcrypt
28 import random
27 import random
29 import logging
28 import logging
30 import traceback
29 import traceback
31 import hashlib
30 import hashlib
31
32 from tempfile import _RandomNameSequence
32 from tempfile import _RandomNameSequence
33 from decorator import decorator
33 from decorator import decorator
34
34
35 from pylons import config, session, url, request
35 from pylons import config, session, url, request
36 from pylons.controllers.util import abort, redirect
36 from pylons.controllers.util import abort, redirect
37 from pylons.i18n.translation import _
37 from pylons.i18n.translation import _
38
38
39 from rhodecode import __platform__
40
41 if __platform__ == 'Windows':
42 from hashlib import sha256
43 if __platform__ in ('Linux', 'Darwin'):
44 import bcrypt
45
46
39 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
40 from rhodecode.lib.utils import get_repo_slug
48 from rhodecode.lib.utils import get_repo_slug
41 from rhodecode.lib.auth_ldap import AuthLdap
49 from rhodecode.lib.auth_ldap import AuthLdap
42
50
43 from rhodecode.model import meta
51 from rhodecode.model import meta
44 from rhodecode.model.user import UserModel
52 from rhodecode.model.user import UserModel
45 from rhodecode.model.db import Permission
53 from rhodecode.model.db import Permission
46
54
47
55
48 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
49
57
50 class PasswordGenerator(object):
58 class PasswordGenerator(object):
51 """This is a simple class for generating password from
59 """This is a simple class for generating password from
52 different sets of characters
60 different sets of characters
53 usage:
61 usage:
54 passwd_gen = PasswordGenerator()
62 passwd_gen = PasswordGenerator()
55 #print 8-letter password containing only big and small letters of alphabet
63 #print 8-letter password containing only big and small letters of alphabet
56 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
57 """
65 """
58 ALPHABETS_NUM = r'''1234567890'''#[0]
66 ALPHABETS_NUM = r'''1234567890'''#[0]
59 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
67 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
60 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
68 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
61 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
69 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
62 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
70 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
63 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
64 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
65 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
66 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
67
75
68 def __init__(self, passwd=''):
76 def __init__(self, passwd=''):
69 self.passwd = passwd
77 self.passwd = passwd
70
78
71 def gen_password(self, len, type):
79 def gen_password(self, len, type):
72 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
73 return self.passwd
81 return self.passwd
74
82
83 class RhodeCodeCrypto(object):
84
85 @classmethod
86 def hash_string(cls, str_):
87 """
88 Cryptographic function used for password hashing based on pybcrypt
89 or pycrypto in windows
90
91 :param password: password to hash
92 """
93 if __platform__ == 'Windows':
94 return sha256(str_).hexdigest()
95 elif __platform__ in ('Linux', 'Darwin'):
96 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
97 else:
98 raise Exception('Unknown or unsupoprted platform %s' % __platform__)
99
100 @classmethod
101 def hash_check(cls, password, hashed):
102 """
103 Checks matching password with it's hashed value, runs different
104 implementation based on platform it runs on
105
106 :param password: password
107 :param hashed: password in hashed form
108 """
109
110 if __platform__ == 'Windows':
111 return sha256(password).hexdigest() == hashed
112 elif __platform__ in ('Linux', 'Darwin'):
113 return bcrypt.hashpw(password, hashed) == hashed
114 else:
115 raise Exception('Unknown or unsupoprted platform %s' % __platform__)
116
117
118
119
75
120
76 def get_crypt_password(password):
121 def get_crypt_password(password):
77 """Cryptographic function used for password hashing based on pybcrypt
122 return RhodeCodeCrypto.hash_string(password)
78
123
79 :param password: password to hash
124 def check_password(password, hashed):
80 """
125 return RhodeCodeCrypto.hash_check(password, hashed)
81 return bcrypt.hashpw(password, bcrypt.gensalt(10))
82
126
83 def generate_api_key(username, salt=None):
127 def generate_api_key(username, salt=None):
84 if salt is None:
128 if salt is None:
85 salt = _RandomNameSequence().next()
129 salt = _RandomNameSequence().next()
86
130
87 return hashlib.sha1(username + salt).hexdigest()
131 return hashlib.sha1(username + salt).hexdigest()
88
132
89 def check_password(password, hashed):
90 return bcrypt.hashpw(password, hashed) == hashed
91
92 def authfunc(environ, username, password):
133 def authfunc(environ, username, password):
93 """Dummy authentication function used in Mercurial/Git/ and access control,
134 """Dummy authentication function used in Mercurial/Git/ and access control,
94
135
95 :param environ: needed only for using in Basic auth
136 :param environ: needed only for using in Basic auth
96 """
137 """
97 return authenticate(username, password)
138 return authenticate(username, password)
98
139
99
140
100 def authenticate(username, password):
141 def authenticate(username, password):
101 """Authentication function used for access control,
142 """Authentication function used for access control,
102 firstly checks for db authentication then if ldap is enabled for ldap
143 firstly checks for db authentication then if ldap is enabled for ldap
103 authentication, also creates ldap user if not in database
144 authentication, also creates ldap user if not in database
104
145
105 :param username: username
146 :param username: username
106 :param password: password
147 :param password: password
107 """
148 """
108 user_model = UserModel()
149 user_model = UserModel()
109 user = user_model.get_by_username(username, cache=False)
150 user = user_model.get_by_username(username, cache=False)
110
151
111 log.debug('Authenticating user using RhodeCode account')
152 log.debug('Authenticating user using RhodeCode account')
112 if user is not None and not user.ldap_dn:
153 if user is not None and not user.ldap_dn:
113 if user.active:
154 if user.active:
114
155
115 if user.username == 'default' and user.active:
156 if user.username == 'default' and user.active:
116 log.info('user %s authenticated correctly as anonymous user',
157 log.info('user %s authenticated correctly as anonymous user',
117 username)
158 username)
118 return True
159 return True
119
160
120 elif user.username == username and check_password(password, user.password):
161 elif user.username == username and check_password(password, user.password):
121 log.info('user %s authenticated correctly', username)
162 log.info('user %s authenticated correctly', username)
122 return True
163 return True
123 else:
164 else:
124 log.warning('user %s is disabled', username)
165 log.warning('user %s is disabled', username)
125
166
126 else:
167 else:
127 log.debug('Regular authentication failed')
168 log.debug('Regular authentication failed')
128 user_obj = user_model.get_by_username(username, cache=False,
169 user_obj = user_model.get_by_username(username, cache=False,
129 case_insensitive=True)
170 case_insensitive=True)
130
171
131 if user_obj is not None and not user_obj.ldap_dn:
172 if user_obj is not None and not user_obj.ldap_dn:
132 log.debug('this user already exists as non ldap')
173 log.debug('this user already exists as non ldap')
133 return False
174 return False
134
175
135 from rhodecode.model.settings import SettingsModel
176 from rhodecode.model.settings import SettingsModel
136 ldap_settings = SettingsModel().get_ldap_settings()
177 ldap_settings = SettingsModel().get_ldap_settings()
137
178
138 #======================================================================
179 #======================================================================
139 # FALLBACK TO LDAP AUTH IF ENABLE
180 # FALLBACK TO LDAP AUTH IF ENABLE
140 #======================================================================
181 #======================================================================
141 if ldap_settings.get('ldap_active', False):
182 if ldap_settings.get('ldap_active', False):
142 log.debug("Authenticating user using ldap")
183 log.debug("Authenticating user using ldap")
143 kwargs = {
184 kwargs = {
144 'server':ldap_settings.get('ldap_host', ''),
185 'server':ldap_settings.get('ldap_host', ''),
145 'base_dn':ldap_settings.get('ldap_base_dn', ''),
186 'base_dn':ldap_settings.get('ldap_base_dn', ''),
146 'port':ldap_settings.get('ldap_port'),
187 'port':ldap_settings.get('ldap_port'),
147 'bind_dn':ldap_settings.get('ldap_dn_user'),
188 'bind_dn':ldap_settings.get('ldap_dn_user'),
148 'bind_pass':ldap_settings.get('ldap_dn_pass'),
189 'bind_pass':ldap_settings.get('ldap_dn_pass'),
149 'use_ldaps':ldap_settings.get('ldap_ldaps'),
190 'use_ldaps':ldap_settings.get('ldap_ldaps'),
150 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
191 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
151 'ldap_filter':ldap_settings.get('ldap_filter'),
192 'ldap_filter':ldap_settings.get('ldap_filter'),
152 'search_scope':ldap_settings.get('ldap_search_scope'),
193 'search_scope':ldap_settings.get('ldap_search_scope'),
153 'attr_login':ldap_settings.get('ldap_attr_login'),
194 'attr_login':ldap_settings.get('ldap_attr_login'),
154 'ldap_version':3,
195 'ldap_version':3,
155 }
196 }
156 log.debug('Checking for ldap authentication')
197 log.debug('Checking for ldap authentication')
157 try:
198 try:
158 aldap = AuthLdap(**kwargs)
199 aldap = AuthLdap(**kwargs)
159 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
200 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
160 log.debug('Got ldap DN response %s', user_dn)
201 log.debug('Got ldap DN response %s', user_dn)
161
202
162 user_attrs = {
203 user_attrs = {
163 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
204 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
164 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
205 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
165 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
206 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
166 }
207 }
167
208
168 if user_model.create_ldap(username, password, user_dn, user_attrs):
209 if user_model.create_ldap(username, password, user_dn, user_attrs):
169 log.info('created new ldap user %s', username)
210 log.info('created new ldap user %s', username)
170
211
171 return True
212 return True
172 except (LdapUsernameError, LdapPasswordError,):
213 except (LdapUsernameError, LdapPasswordError,):
173 pass
214 pass
174 except (Exception,):
215 except (Exception,):
175 log.error(traceback.format_exc())
216 log.error(traceback.format_exc())
176 pass
217 pass
177 return False
218 return False
178
219
179 class AuthUser(object):
220 class AuthUser(object):
180 """
221 """
181 A simple object that handles all attributes of user in RhodeCode
222 A simple object that handles all attributes of user in RhodeCode
182
223
183 It does lookup based on API key,given user, or user present in session
224 It does lookup based on API key,given user, or user present in session
184 Then it fills all required information for such user. It also checks if
225 Then it fills all required information for such user. It also checks if
185 anonymous access is enabled and if so, it returns default user as logged
226 anonymous access is enabled and if so, it returns default user as logged
186 in
227 in
187 """
228 """
188
229
189 def __init__(self, user_id=None, api_key=None):
230 def __init__(self, user_id=None, api_key=None):
190
231
191 self.user_id = user_id
232 self.user_id = user_id
192 self.api_key = api_key
233 self.api_key = api_key
193
234
194 self.username = 'None'
235 self.username = 'None'
195 self.name = ''
236 self.name = ''
196 self.lastname = ''
237 self.lastname = ''
197 self.email = ''
238 self.email = ''
198 self.is_authenticated = False
239 self.is_authenticated = False
199 self.admin = False
240 self.admin = False
200 self.permissions = {}
241 self.permissions = {}
201 self.propagate_data()
242 self.propagate_data()
202
243
203
244
204 def propagate_data(self):
245 def propagate_data(self):
205 user_model = UserModel()
246 user_model = UserModel()
206 if self.api_key:
247 if self.api_key:
207 #try go get user by api key
248 #try go get user by api key
208 log.debug('Auth User lookup by API KEY %s', self.api_key)
249 log.debug('Auth User lookup by API KEY %s', self.api_key)
209 user_model.fill_data(self, api_key=self.api_key)
250 user_model.fill_data(self, api_key=self.api_key)
210 else:
251 else:
211 log.debug('Auth User lookup by USER ID %s', self.user_id)
252 log.debug('Auth User lookup by USER ID %s', self.user_id)
212 self.anonymous_user = user_model.get_by_username('default', cache=True)
253 self.anonymous_user = user_model.get_by_username('default', cache=True)
213
254
214 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
255 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
215 user_model.fill_data(self, user_id=self.user_id)
256 user_model.fill_data(self, user_id=self.user_id)
216 else:
257 else:
217 if self.anonymous_user.active is True:
258 if self.anonymous_user.active is True:
218 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
259 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
219 #then we set this user is logged in
260 #then we set this user is logged in
220 self.is_authenticated = True
261 self.is_authenticated = True
221 else:
262 else:
222 self.is_authenticated = False
263 self.is_authenticated = False
223
264
224 log.debug('Auth User is now %s', self)
265 log.debug('Auth User is now %s', self)
225 user_model.fill_perms(self)
266 user_model.fill_perms(self)
226
267
227 @property
268 @property
228 def is_admin(self):
269 def is_admin(self):
229 return self.admin
270 return self.admin
230
271
231 def __repr__(self):
272 def __repr__(self):
232 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
273 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
233 self.is_authenticated)
274 self.is_authenticated)
234
275
235 def set_authenticated(self, authenticated=True):
276 def set_authenticated(self, authenticated=True):
236
277
237 if self.user_id != self.anonymous_user.user_id:
278 if self.user_id != self.anonymous_user.user_id:
238 self.is_authenticated = authenticated
279 self.is_authenticated = authenticated
239
280
240
281
241 def set_available_permissions(config):
282 def set_available_permissions(config):
242 """This function will propagate pylons globals with all available defined
283 """This function will propagate pylons globals with all available defined
243 permission given in db. We don't want to check each time from db for new
284 permission given in db. We don't want to check each time from db for new
244 permissions since adding a new permission also requires application restart
285 permissions since adding a new permission also requires application restart
245 ie. to decorate new views with the newly created permission
286 ie. to decorate new views with the newly created permission
246
287
247 :param config: current pylons config instance
288 :param config: current pylons config instance
248
289
249 """
290 """
250 log.info('getting information about all available permissions')
291 log.info('getting information about all available permissions')
251 try:
292 try:
252 sa = meta.Session()
293 sa = meta.Session()
253 all_perms = sa.query(Permission).all()
294 all_perms = sa.query(Permission).all()
254 except:
295 except:
255 pass
296 pass
256 finally:
297 finally:
257 meta.Session.remove()
298 meta.Session.remove()
258
299
259 config['available_permissions'] = [x.permission_name for x in all_perms]
300 config['available_permissions'] = [x.permission_name for x in all_perms]
260
301
261 #===============================================================================
302 #===============================================================================
262 # CHECK DECORATORS
303 # CHECK DECORATORS
263 #===============================================================================
304 #===============================================================================
264 class LoginRequired(object):
305 class LoginRequired(object):
265 """
306 """
266 Must be logged in to execute this function else
307 Must be logged in to execute this function else
267 redirect to login page
308 redirect to login page
268
309
269 :param api_access: if enabled this checks only for valid auth token
310 :param api_access: if enabled this checks only for valid auth token
270 and grants access based on valid token
311 and grants access based on valid token
271 """
312 """
272
313
273 def __init__(self, api_access=False):
314 def __init__(self, api_access=False):
274 self.api_access = api_access
315 self.api_access = api_access
275
316
276 def __call__(self, func):
317 def __call__(self, func):
277 return decorator(self.__wrapper, func)
318 return decorator(self.__wrapper, func)
278
319
279 def __wrapper(self, func, *fargs, **fkwargs):
320 def __wrapper(self, func, *fargs, **fkwargs):
280 cls = fargs[0]
321 cls = fargs[0]
281 user = cls.rhodecode_user
322 user = cls.rhodecode_user
282
323
283 api_access_ok = False
324 api_access_ok = False
284 if self.api_access:
325 if self.api_access:
285 log.debug('Checking API KEY access for %s', cls)
326 log.debug('Checking API KEY access for %s', cls)
286 if user.api_key == request.GET.get('api_key'):
327 if user.api_key == request.GET.get('api_key'):
287 api_access_ok = True
328 api_access_ok = True
288 else:
329 else:
289 log.debug("API KEY token not valid")
330 log.debug("API KEY token not valid")
290
331
291 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
332 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
292 if user.is_authenticated or api_access_ok:
333 if user.is_authenticated or api_access_ok:
293 log.debug('user %s is authenticated', user.username)
334 log.debug('user %s is authenticated', user.username)
294 return func(*fargs, **fkwargs)
335 return func(*fargs, **fkwargs)
295 else:
336 else:
296 log.warn('user %s NOT authenticated', user.username)
337 log.warn('user %s NOT authenticated', user.username)
297
338
298 p = ''
339 p = ''
299 if request.environ.get('SCRIPT_NAME') != '/':
340 if request.environ.get('SCRIPT_NAME') != '/':
300 p += request.environ.get('SCRIPT_NAME')
341 p += request.environ.get('SCRIPT_NAME')
301
342
302 p += request.environ.get('PATH_INFO')
343 p += request.environ.get('PATH_INFO')
303 if request.environ.get('QUERY_STRING'):
344 if request.environ.get('QUERY_STRING'):
304 p += '?' + request.environ.get('QUERY_STRING')
345 p += '?' + request.environ.get('QUERY_STRING')
305
346
306 log.debug('redirecting to login page with %s', p)
347 log.debug('redirecting to login page with %s', p)
307 return redirect(url('login_home', came_from=p))
348 return redirect(url('login_home', came_from=p))
308
349
309 class NotAnonymous(object):
350 class NotAnonymous(object):
310 """Must be logged in to execute this function else
351 """Must be logged in to execute this function else
311 redirect to login page"""
352 redirect to login page"""
312
353
313 def __call__(self, func):
354 def __call__(self, func):
314 return decorator(self.__wrapper, func)
355 return decorator(self.__wrapper, func)
315
356
316 def __wrapper(self, func, *fargs, **fkwargs):
357 def __wrapper(self, func, *fargs, **fkwargs):
317 cls = fargs[0]
358 cls = fargs[0]
318 self.user = cls.rhodecode_user
359 self.user = cls.rhodecode_user
319
360
320 log.debug('Checking if user is not anonymous @%s', cls)
361 log.debug('Checking if user is not anonymous @%s', cls)
321
362
322 anonymous = self.user.username == 'default'
363 anonymous = self.user.username == 'default'
323
364
324 if anonymous:
365 if anonymous:
325 p = ''
366 p = ''
326 if request.environ.get('SCRIPT_NAME') != '/':
367 if request.environ.get('SCRIPT_NAME') != '/':
327 p += request.environ.get('SCRIPT_NAME')
368 p += request.environ.get('SCRIPT_NAME')
328
369
329 p += request.environ.get('PATH_INFO')
370 p += request.environ.get('PATH_INFO')
330 if request.environ.get('QUERY_STRING'):
371 if request.environ.get('QUERY_STRING'):
331 p += '?' + request.environ.get('QUERY_STRING')
372 p += '?' + request.environ.get('QUERY_STRING')
332
373
333 import rhodecode.lib.helpers as h
374 import rhodecode.lib.helpers as h
334 h.flash(_('You need to be a registered user to perform this action'),
375 h.flash(_('You need to be a registered user to perform this action'),
335 category='warning')
376 category='warning')
336 return redirect(url('login_home', came_from=p))
377 return redirect(url('login_home', came_from=p))
337 else:
378 else:
338 return func(*fargs, **fkwargs)
379 return func(*fargs, **fkwargs)
339
380
340 class PermsDecorator(object):
381 class PermsDecorator(object):
341 """Base class for controller decorators"""
382 """Base class for controller decorators"""
342
383
343 def __init__(self, *required_perms):
384 def __init__(self, *required_perms):
344 available_perms = config['available_permissions']
385 available_perms = config['available_permissions']
345 for perm in required_perms:
386 for perm in required_perms:
346 if perm not in available_perms:
387 if perm not in available_perms:
347 raise Exception("'%s' permission is not defined" % perm)
388 raise Exception("'%s' permission is not defined" % perm)
348 self.required_perms = set(required_perms)
389 self.required_perms = set(required_perms)
349 self.user_perms = None
390 self.user_perms = None
350
391
351 def __call__(self, func):
392 def __call__(self, func):
352 return decorator(self.__wrapper, func)
393 return decorator(self.__wrapper, func)
353
394
354
395
355 def __wrapper(self, func, *fargs, **fkwargs):
396 def __wrapper(self, func, *fargs, **fkwargs):
356 cls = fargs[0]
397 cls = fargs[0]
357 self.user = cls.rhodecode_user
398 self.user = cls.rhodecode_user
358 self.user_perms = self.user.permissions
399 self.user_perms = self.user.permissions
359 log.debug('checking %s permissions %s for %s %s',
400 log.debug('checking %s permissions %s for %s %s',
360 self.__class__.__name__, self.required_perms, cls,
401 self.__class__.__name__, self.required_perms, cls,
361 self.user)
402 self.user)
362
403
363 if self.check_permissions():
404 if self.check_permissions():
364 log.debug('Permission granted for %s %s', cls, self.user)
405 log.debug('Permission granted for %s %s', cls, self.user)
365 return func(*fargs, **fkwargs)
406 return func(*fargs, **fkwargs)
366
407
367 else:
408 else:
368 log.warning('Permission denied for %s %s', cls, self.user)
409 log.warning('Permission denied for %s %s', cls, self.user)
369 #redirect with forbidden ret code
410 #redirect with forbidden ret code
370 return abort(403)
411 return abort(403)
371
412
372
413
373
414
374 def check_permissions(self):
415 def check_permissions(self):
375 """Dummy function for overriding"""
416 """Dummy function for overriding"""
376 raise Exception('You have to write this function in child class')
417 raise Exception('You have to write this function in child class')
377
418
378 class HasPermissionAllDecorator(PermsDecorator):
419 class HasPermissionAllDecorator(PermsDecorator):
379 """Checks for access permission for all given predicates. All of them
420 """Checks for access permission for all given predicates. All of them
380 have to be meet in order to fulfill the request
421 have to be meet in order to fulfill the request
381 """
422 """
382
423
383 def check_permissions(self):
424 def check_permissions(self):
384 if self.required_perms.issubset(self.user_perms.get('global')):
425 if self.required_perms.issubset(self.user_perms.get('global')):
385 return True
426 return True
386 return False
427 return False
387
428
388
429
389 class HasPermissionAnyDecorator(PermsDecorator):
430 class HasPermissionAnyDecorator(PermsDecorator):
390 """Checks for access permission for any of given predicates. In order to
431 """Checks for access permission for any of given predicates. In order to
391 fulfill the request any of predicates must be meet
432 fulfill the request any of predicates must be meet
392 """
433 """
393
434
394 def check_permissions(self):
435 def check_permissions(self):
395 if self.required_perms.intersection(self.user_perms.get('global')):
436 if self.required_perms.intersection(self.user_perms.get('global')):
396 return True
437 return True
397 return False
438 return False
398
439
399 class HasRepoPermissionAllDecorator(PermsDecorator):
440 class HasRepoPermissionAllDecorator(PermsDecorator):
400 """Checks for access permission for all given predicates for specific
441 """Checks for access permission for all given predicates for specific
401 repository. All of them have to be meet in order to fulfill the request
442 repository. All of them have to be meet in order to fulfill the request
402 """
443 """
403
444
404 def check_permissions(self):
445 def check_permissions(self):
405 repo_name = get_repo_slug(request)
446 repo_name = get_repo_slug(request)
406 try:
447 try:
407 user_perms = set([self.user_perms['repositories'][repo_name]])
448 user_perms = set([self.user_perms['repositories'][repo_name]])
408 except KeyError:
449 except KeyError:
409 return False
450 return False
410 if self.required_perms.issubset(user_perms):
451 if self.required_perms.issubset(user_perms):
411 return True
452 return True
412 return False
453 return False
413
454
414
455
415 class HasRepoPermissionAnyDecorator(PermsDecorator):
456 class HasRepoPermissionAnyDecorator(PermsDecorator):
416 """Checks for access permission for any of given predicates for specific
457 """Checks for access permission for any of given predicates for specific
417 repository. In order to fulfill the request any of predicates must be meet
458 repository. In order to fulfill the request any of predicates must be meet
418 """
459 """
419
460
420 def check_permissions(self):
461 def check_permissions(self):
421 repo_name = get_repo_slug(request)
462 repo_name = get_repo_slug(request)
422
463
423 try:
464 try:
424 user_perms = set([self.user_perms['repositories'][repo_name]])
465 user_perms = set([self.user_perms['repositories'][repo_name]])
425 except KeyError:
466 except KeyError:
426 return False
467 return False
427 if self.required_perms.intersection(user_perms):
468 if self.required_perms.intersection(user_perms):
428 return True
469 return True
429 return False
470 return False
430 #===============================================================================
471 #===============================================================================
431 # CHECK FUNCTIONS
472 # CHECK FUNCTIONS
432 #===============================================================================
473 #===============================================================================
433
474
434 class PermsFunction(object):
475 class PermsFunction(object):
435 """Base function for other check functions"""
476 """Base function for other check functions"""
436
477
437 def __init__(self, *perms):
478 def __init__(self, *perms):
438 available_perms = config['available_permissions']
479 available_perms = config['available_permissions']
439
480
440 for perm in perms:
481 for perm in perms:
441 if perm not in available_perms:
482 if perm not in available_perms:
442 raise Exception("'%s' permission in not defined" % perm)
483 raise Exception("'%s' permission in not defined" % perm)
443 self.required_perms = set(perms)
484 self.required_perms = set(perms)
444 self.user_perms = None
485 self.user_perms = None
445 self.granted_for = ''
486 self.granted_for = ''
446 self.repo_name = None
487 self.repo_name = None
447
488
448 def __call__(self, check_Location=''):
489 def __call__(self, check_Location=''):
449 user = session.get('rhodecode_user', False)
490 user = session.get('rhodecode_user', False)
450 if not user:
491 if not user:
451 return False
492 return False
452 self.user_perms = user.permissions
493 self.user_perms = user.permissions
453 self.granted_for = user
494 self.granted_for = user
454 log.debug('checking %s %s %s', self.__class__.__name__,
495 log.debug('checking %s %s %s', self.__class__.__name__,
455 self.required_perms, user)
496 self.required_perms, user)
456
497
457 if self.check_permissions():
498 if self.check_permissions():
458 log.debug('Permission granted %s @ %s', self.granted_for,
499 log.debug('Permission granted %s @ %s', self.granted_for,
459 check_Location or 'unspecified location')
500 check_Location or 'unspecified location')
460 return True
501 return True
461
502
462 else:
503 else:
463 log.warning('Permission denied for %s @ %s', self.granted_for,
504 log.warning('Permission denied for %s @ %s', self.granted_for,
464 check_Location or 'unspecified location')
505 check_Location or 'unspecified location')
465 return False
506 return False
466
507
467 def check_permissions(self):
508 def check_permissions(self):
468 """Dummy function for overriding"""
509 """Dummy function for overriding"""
469 raise Exception('You have to write this function in child class')
510 raise Exception('You have to write this function in child class')
470
511
471 class HasPermissionAll(PermsFunction):
512 class HasPermissionAll(PermsFunction):
472 def check_permissions(self):
513 def check_permissions(self):
473 if self.required_perms.issubset(self.user_perms.get('global')):
514 if self.required_perms.issubset(self.user_perms.get('global')):
474 return True
515 return True
475 return False
516 return False
476
517
477 class HasPermissionAny(PermsFunction):
518 class HasPermissionAny(PermsFunction):
478 def check_permissions(self):
519 def check_permissions(self):
479 if self.required_perms.intersection(self.user_perms.get('global')):
520 if self.required_perms.intersection(self.user_perms.get('global')):
480 return True
521 return True
481 return False
522 return False
482
523
483 class HasRepoPermissionAll(PermsFunction):
524 class HasRepoPermissionAll(PermsFunction):
484
525
485 def __call__(self, repo_name=None, check_Location=''):
526 def __call__(self, repo_name=None, check_Location=''):
486 self.repo_name = repo_name
527 self.repo_name = repo_name
487 return super(HasRepoPermissionAll, self).__call__(check_Location)
528 return super(HasRepoPermissionAll, self).__call__(check_Location)
488
529
489 def check_permissions(self):
530 def check_permissions(self):
490 if not self.repo_name:
531 if not self.repo_name:
491 self.repo_name = get_repo_slug(request)
532 self.repo_name = get_repo_slug(request)
492
533
493 try:
534 try:
494 self.user_perms = set([self.user_perms['repositories']\
535 self.user_perms = set([self.user_perms['repositories']\
495 [self.repo_name]])
536 [self.repo_name]])
496 except KeyError:
537 except KeyError:
497 return False
538 return False
498 self.granted_for = self.repo_name
539 self.granted_for = self.repo_name
499 if self.required_perms.issubset(self.user_perms):
540 if self.required_perms.issubset(self.user_perms):
500 return True
541 return True
501 return False
542 return False
502
543
503 class HasRepoPermissionAny(PermsFunction):
544 class HasRepoPermissionAny(PermsFunction):
504
545
505 def __call__(self, repo_name=None, check_Location=''):
546 def __call__(self, repo_name=None, check_Location=''):
506 self.repo_name = repo_name
547 self.repo_name = repo_name
507 return super(HasRepoPermissionAny, self).__call__(check_Location)
548 return super(HasRepoPermissionAny, self).__call__(check_Location)
508
549
509 def check_permissions(self):
550 def check_permissions(self):
510 if not self.repo_name:
551 if not self.repo_name:
511 self.repo_name = get_repo_slug(request)
552 self.repo_name = get_repo_slug(request)
512
553
513 try:
554 try:
514 self.user_perms = set([self.user_perms['repositories']\
555 self.user_perms = set([self.user_perms['repositories']\
515 [self.repo_name]])
556 [self.repo_name]])
516 except KeyError:
557 except KeyError:
517 return False
558 return False
518 self.granted_for = self.repo_name
559 self.granted_for = self.repo_name
519 if self.required_perms.intersection(self.user_perms):
560 if self.required_perms.intersection(self.user_perms):
520 return True
561 return True
521 return False
562 return False
522
563
523 #===============================================================================
564 #===============================================================================
524 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
565 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
525 #===============================================================================
566 #===============================================================================
526
567
527 class HasPermissionAnyMiddleware(object):
568 class HasPermissionAnyMiddleware(object):
528 def __init__(self, *perms):
569 def __init__(self, *perms):
529 self.required_perms = set(perms)
570 self.required_perms = set(perms)
530
571
531 def __call__(self, user, repo_name):
572 def __call__(self, user, repo_name):
532 usr = AuthUser(user.user_id)
573 usr = AuthUser(user.user_id)
533 try:
574 try:
534 self.user_perms = set([usr.permissions['repositories'][repo_name]])
575 self.user_perms = set([usr.permissions['repositories'][repo_name]])
535 except:
576 except:
536 self.user_perms = set()
577 self.user_perms = set()
537 self.granted_for = ''
578 self.granted_for = ''
538 self.username = user.username
579 self.username = user.username
539 self.repo_name = repo_name
580 self.repo_name = repo_name
540 return self.check_permissions()
581 return self.check_permissions()
541
582
542 def check_permissions(self):
583 def check_permissions(self):
543 log.debug('checking mercurial protocol '
584 log.debug('checking mercurial protocol '
544 'permissions %s for user:%s repository:%s', self.user_perms,
585 'permissions %s for user:%s repository:%s', self.user_perms,
545 self.username, self.repo_name)
586 self.username, self.repo_name)
546 if self.required_perms.intersection(self.user_perms):
587 if self.required_perms.intersection(self.user_perms):
547 log.debug('permission granted')
588 log.debug('permission granted')
548 return True
589 return True
549 log.debug('permission denied')
590 log.debug('permission denied')
550 return False
591 return False
@@ -1,108 +1,112 b''
1 import sys
1 import sys
2 from rhodecode import get_version
2 from rhodecode import get_version
3 from rhodecode import __platform__
3
4
4 py_version = sys.version_info
5 py_version = sys.version_info
5
6
6 requirements = [
7 requirements = [
7 "Pylons==1.0.0",
8 "Pylons==1.0.0",
8 "WebHelpers>=1.2",
9 "WebHelpers>=1.2",
9 "SQLAlchemy>=0.6.6",
10 "SQLAlchemy>=0.6.6",
10 "Mako>=0.4.0",
11 "Mako>=0.4.0",
11 "vcs>=0.2.0",
12 "vcs>=0.2.0",
12 "pygments>=1.4",
13 "pygments>=1.4",
13 "mercurial>=1.7.5",
14 "mercurial>=1.7.5",
14 "whoosh>=1.3.4",
15 "whoosh>=1.3.4",
15 "celery>=2.2.4",
16 "celery>=2.2.4",
16 "py-bcrypt",
17 "babel",
17 "babel",
18 ]
18 ]
19
19
20 classifiers = ['Development Status :: 4 - Beta',
20 classifiers = ['Development Status :: 4 - Beta',
21 'Environment :: Web Environment',
21 'Environment :: Web Environment',
22 'Framework :: Pylons',
22 'Framework :: Pylons',
23 'Intended Audience :: Developers',
23 'Intended Audience :: Developers',
24 'License :: OSI Approved :: BSD License',
24 'License :: OSI Approved :: BSD License',
25 'Operating System :: OS Independent',
25 'Operating System :: OS Independent',
26 'Programming Language :: Python', ]
26 'Programming Language :: Python', ]
27
27
28 if py_version < (2, 6):
28 if py_version < (2, 6):
29 requirements.append("simplejson")
29 requirements.append("simplejson")
30 requirements.append("pysqlite")
30 requirements.append("pysqlite")
31
31
32 if __platform__ in ('Linux', 'Darwin'):
33 requirements.append("py-bcrypt")
34
35
32 #additional files from project that goes somewhere in the filesystem
36 #additional files from project that goes somewhere in the filesystem
33 #relative to sys.prefix
37 #relative to sys.prefix
34 data_files = []
38 data_files = []
35
39
36 #additional files that goes into package itself
40 #additional files that goes into package itself
37 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
41 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
38
42
39 description = ('Mercurial repository browser/management with '
43 description = ('Mercurial repository browser/management with '
40 'build in push/pull server and full text search')
44 'build in push/pull server and full text search')
41 keywords = ' '.join (['rhodecode', 'rhodiumcode', 'mercurial', 'git',
45 keywords = ' '.join (['rhodecode', 'rhodiumcode', 'mercurial', 'git',
42 'repository management', 'hgweb replacement'
46 'repository management', 'hgweb replacement'
43 'hgwebdir', 'gitweb replacement', 'serving hgweb',
47 'hgwebdir', 'gitweb replacement', 'serving hgweb',
44 ])
48 ])
45 #long description
49 #long description
46 try:
50 try:
47 readme_file = 'README.rst'
51 readme_file = 'README.rst'
48 changelog_file = 'docs/changelog.rst'
52 changelog_file = 'docs/changelog.rst'
49 long_description = open(readme_file).read() + '\n\n' + \
53 long_description = open(readme_file).read() + '\n\n' + \
50 open(changelog_file).read()
54 open(changelog_file).read()
51
55
52 except IOError, err:
56 except IOError, err:
53 sys.stderr.write("[WARNING] Cannot find file specified as "
57 sys.stderr.write("[WARNING] Cannot find file specified as "
54 "long_description (%s)\n or changelog (%s) skipping that file" \
58 "long_description (%s)\n or changelog (%s) skipping that file" \
55 % (readme_file, changelog_file))
59 % (readme_file, changelog_file))
56 long_description = description
60 long_description = description
57
61
58
62
59 try:
63 try:
60 from setuptools import setup, find_packages
64 from setuptools import setup, find_packages
61 except ImportError:
65 except ImportError:
62 from ez_setup import use_setuptools
66 from ez_setup import use_setuptools
63 use_setuptools()
67 use_setuptools()
64 from setuptools import setup, find_packages
68 from setuptools import setup, find_packages
65 #packages
69 #packages
66 packages = find_packages(exclude=['ez_setup'])
70 packages = find_packages(exclude=['ez_setup'])
67
71
68 setup(
72 setup(
69 name='RhodeCode',
73 name='RhodeCode',
70 version=get_version(),
74 version=get_version(),
71 description=description,
75 description=description,
72 long_description=long_description,
76 long_description=long_description,
73 keywords=keywords,
77 keywords=keywords,
74 license='BSD',
78 license='BSD',
75 author='Marcin Kuzminski',
79 author='Marcin Kuzminski',
76 author_email='marcin@python-works.com',
80 author_email='marcin@python-works.com',
77 url='http://hg.python-works.com',
81 url='http://hg.python-works.com',
78 install_requires=requirements,
82 install_requires=requirements,
79 classifiers=classifiers,
83 classifiers=classifiers,
80 setup_requires=["PasteScript>=1.6.3"],
84 setup_requires=["PasteScript>=1.6.3"],
81 data_files=data_files,
85 data_files=data_files,
82 packages=packages,
86 packages=packages,
83 include_package_data=True,
87 include_package_data=True,
84 test_suite='nose.collector',
88 test_suite='nose.collector',
85 package_data=package_data,
89 package_data=package_data,
86 message_extractors={'rhodecode': [
90 message_extractors={'rhodecode': [
87 ('**.py', 'python', None),
91 ('**.py', 'python', None),
88 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
92 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
89 ('public/**', 'ignore', None)]},
93 ('public/**', 'ignore', None)]},
90 zip_safe=False,
94 zip_safe=False,
91 paster_plugins=['PasteScript', 'Pylons'],
95 paster_plugins=['PasteScript', 'Pylons'],
92 entry_points="""
96 entry_points="""
93 [paste.app_factory]
97 [paste.app_factory]
94 main = rhodecode.config.middleware:make_app
98 main = rhodecode.config.middleware:make_app
95
99
96 [paste.app_install]
100 [paste.app_install]
97 main = pylons.util:PylonsInstaller
101 main = pylons.util:PylonsInstaller
98
102
99 [paste.global_paster_command]
103 [paste.global_paster_command]
100 make-index = rhodecode.lib.indexers:MakeIndex
104 make-index = rhodecode.lib.indexers:MakeIndex
101 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
105 upgrade-db = rhodecode.lib.dbmigrate:UpgradeDb
102 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
106 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
103 celerybeat=rhodecode.lib.celerypylons.commands:CeleryBeatCommand
107 celerybeat=rhodecode.lib.celerypylons.commands:CeleryBeatCommand
104 camqadm=rhodecode.lib.celerypylons.commands:CAMQPAdminCommand
108 camqadm=rhodecode.lib.celerypylons.commands:CAMQPAdminCommand
105 celeryev=rhodecode.lib.celerypylons.commands:CeleryEventCommand
109 celeryev=rhodecode.lib.celerypylons.commands:CeleryEventCommand
106
110
107 """,
111 """,
108 )
112 )
General Comments 0
You need to be logged in to leave comments. Login now