##// END OF EJS Templates
merged freebsd support issue from default
marcink -
r1195:74251f80 beta
parent child Browse files
Show More
@@ -1,591 +1,588 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 random
27 import random
28 import logging
28 import logging
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31
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__
39 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
40
40
41 if __platform__ == 'Windows':
41 if __platform__ in PLATFORM_WIN:
42 from hashlib import sha256
42 from hashlib import sha256
43 if __platform__ in ('Linux', 'Darwin'):
43 if __platform__ in PLATFORM_OTHERS:
44 import bcrypt
44 import bcrypt
45
45
46 from rhodecode.lib import str2bool
46 from rhodecode.lib import str2bool
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
48 from rhodecode.lib.utils import get_repo_slug
48 from rhodecode.lib.utils import get_repo_slug
49 from rhodecode.lib.auth_ldap import AuthLdap
49 from rhodecode.lib.auth_ldap import AuthLdap
50
50
51 from rhodecode.model import meta
51 from rhodecode.model import meta
52 from rhodecode.model.user import UserModel
52 from rhodecode.model.user import UserModel
53 from rhodecode.model.db import Permission
53 from rhodecode.model.db import Permission
54
54
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58 class PasswordGenerator(object):
58 class PasswordGenerator(object):
59 """This is a simple class for generating password from
59 """This is a simple class for generating password from
60 different sets of characters
60 different sets of characters
61 usage:
61 usage:
62 passwd_gen = PasswordGenerator()
62 passwd_gen = PasswordGenerator()
63 #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
64 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
65 """
65 """
66 ALPHABETS_NUM = r'''1234567890'''#[0]
66 ALPHABETS_NUM = r'''1234567890'''#[0]
67 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
67 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
68 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
68 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
69 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
69 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
70 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
70 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
75
75
76 def __init__(self, passwd=''):
76 def __init__(self, passwd=''):
77 self.passwd = passwd
77 self.passwd = passwd
78
78
79 def gen_password(self, len, type):
79 def gen_password(self, len, type):
80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
81 return self.passwd
81 return self.passwd
82
82
83 class RhodeCodeCrypto(object):
83 class RhodeCodeCrypto(object):
84
84
85 @classmethod
85 @classmethod
86 def hash_string(cls, str_):
86 def hash_string(cls, str_):
87 """
87 """
88 Cryptographic function used for password hashing based on pybcrypt
88 Cryptographic function used for password hashing based on pybcrypt
89 or pycrypto in windows
89 or pycrypto in windows
90
90
91 :param password: password to hash
91 :param password: password to hash
92 """
92 """
93 if __platform__ == 'Windows':
93 if __platform__ in PLATFORM_WIN:
94 return sha256(str_).hexdigest()
94 return sha256(str_).hexdigest()
95 elif __platform__ in ('Linux', 'Darwin'):
95 elif __platform__ in PLATFORM_OTHERS:
96 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
96 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
97 else:
97 else:
98 raise Exception('Unknown or unsupoprted platform %s' % __platform__)
98 raise Exception('Unknown or unsupported platform %s' % __platform__)
99
99
100 @classmethod
100 @classmethod
101 def hash_check(cls, password, hashed):
101 def hash_check(cls, password, hashed):
102 """
102 """
103 Checks matching password with it's hashed value, runs different
103 Checks matching password with it's hashed value, runs different
104 implementation based on platform it runs on
104 implementation based on platform it runs on
105
105
106 :param password: password
106 :param password: password
107 :param hashed: password in hashed form
107 :param hashed: password in hashed form
108 """
108 """
109
109
110 if __platform__ == 'Windows':
110 if __platform__ in PLATFORM_WIN:
111 return sha256(password).hexdigest() == hashed
111 return sha256(password).hexdigest() == hashed
112 elif __platform__ in ('Linux', 'Darwin'):
112 elif __platform__ in PLATFORM_OTHERS:
113 return bcrypt.hashpw(password, hashed) == hashed
113 return bcrypt.hashpw(password, hashed) == hashed
114 else:
114 else:
115 raise Exception('Unknown or unsupoprted platform %s' % __platform__)
115 raise Exception('Unknown or unsupported platform %s' % __platform__)
116
117
118
119
116
120
117
121 def get_crypt_password(password):
118 def get_crypt_password(password):
122 return RhodeCodeCrypto.hash_string(password)
119 return RhodeCodeCrypto.hash_string(password)
123
120
124 def check_password(password, hashed):
121 def check_password(password, hashed):
125 return RhodeCodeCrypto.hash_check(password, hashed)
122 return RhodeCodeCrypto.hash_check(password, hashed)
126
123
127 def generate_api_key(username, salt=None):
124 def generate_api_key(username, salt=None):
128 if salt is None:
125 if salt is None:
129 salt = _RandomNameSequence().next()
126 salt = _RandomNameSequence().next()
130
127
131 return hashlib.sha1(username + salt).hexdigest()
128 return hashlib.sha1(username + salt).hexdigest()
132
129
133 def authfunc(environ, username, password):
130 def authfunc(environ, username, password):
134 """Dummy authentication function used in Mercurial/Git/ and access control,
131 """Dummy authentication function used in Mercurial/Git/ and access control,
135
132
136 :param environ: needed only for using in Basic auth
133 :param environ: needed only for using in Basic auth
137 """
134 """
138 return authenticate(username, password)
135 return authenticate(username, password)
139
136
140
137
141 def authenticate(username, password):
138 def authenticate(username, password):
142 """Authentication function used for access control,
139 """Authentication function used for access control,
143 firstly checks for db authentication then if ldap is enabled for ldap
140 firstly checks for db authentication then if ldap is enabled for ldap
144 authentication, also creates ldap user if not in database
141 authentication, also creates ldap user if not in database
145
142
146 :param username: username
143 :param username: username
147 :param password: password
144 :param password: password
148 """
145 """
149 user_model = UserModel()
146 user_model = UserModel()
150 user = user_model.get_by_username(username, cache=False)
147 user = user_model.get_by_username(username, cache=False)
151
148
152 log.debug('Authenticating user using RhodeCode account')
149 log.debug('Authenticating user using RhodeCode account')
153 if user is not None and not user.ldap_dn:
150 if user is not None and not user.ldap_dn:
154 if user.active:
151 if user.active:
155
152
156 if user.username == 'default' and user.active:
153 if user.username == 'default' and user.active:
157 log.info('user %s authenticated correctly as anonymous user',
154 log.info('user %s authenticated correctly as anonymous user',
158 username)
155 username)
159 return True
156 return True
160
157
161 elif user.username == username and check_password(password, user.password):
158 elif user.username == username and check_password(password, user.password):
162 log.info('user %s authenticated correctly', username)
159 log.info('user %s authenticated correctly', username)
163 return True
160 return True
164 else:
161 else:
165 log.warning('user %s is disabled', username)
162 log.warning('user %s is disabled', username)
166
163
167 else:
164 else:
168 log.debug('Regular authentication failed')
165 log.debug('Regular authentication failed')
169 user_obj = user_model.get_by_username(username, cache=False,
166 user_obj = user_model.get_by_username(username, cache=False,
170 case_insensitive=True)
167 case_insensitive=True)
171
168
172 if user_obj is not None and not user_obj.ldap_dn:
169 if user_obj is not None and not user_obj.ldap_dn:
173 log.debug('this user already exists as non ldap')
170 log.debug('this user already exists as non ldap')
174 return False
171 return False
175
172
176 from rhodecode.model.settings import SettingsModel
173 from rhodecode.model.settings import SettingsModel
177 ldap_settings = SettingsModel().get_ldap_settings()
174 ldap_settings = SettingsModel().get_ldap_settings()
178
175
179 #======================================================================
176 #======================================================================
180 # FALLBACK TO LDAP AUTH IF ENABLE
177 # FALLBACK TO LDAP AUTH IF ENABLE
181 #======================================================================
178 #======================================================================
182 if str2bool(ldap_settings.get('ldap_active')):
179 if str2bool(ldap_settings.get('ldap_active')):
183 log.debug("Authenticating user using ldap")
180 log.debug("Authenticating user using ldap")
184 kwargs = {
181 kwargs = {
185 'server':ldap_settings.get('ldap_host', ''),
182 'server':ldap_settings.get('ldap_host', ''),
186 'base_dn':ldap_settings.get('ldap_base_dn', ''),
183 'base_dn':ldap_settings.get('ldap_base_dn', ''),
187 'port':ldap_settings.get('ldap_port'),
184 'port':ldap_settings.get('ldap_port'),
188 'bind_dn':ldap_settings.get('ldap_dn_user'),
185 'bind_dn':ldap_settings.get('ldap_dn_user'),
189 'bind_pass':ldap_settings.get('ldap_dn_pass'),
186 'bind_pass':ldap_settings.get('ldap_dn_pass'),
190 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
187 'use_ldaps':str2bool(ldap_settings.get('ldap_ldaps')),
191 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
188 'tls_reqcert':ldap_settings.get('ldap_tls_reqcert'),
192 'ldap_filter':ldap_settings.get('ldap_filter'),
189 'ldap_filter':ldap_settings.get('ldap_filter'),
193 'search_scope':ldap_settings.get('ldap_search_scope'),
190 'search_scope':ldap_settings.get('ldap_search_scope'),
194 'attr_login':ldap_settings.get('ldap_attr_login'),
191 'attr_login':ldap_settings.get('ldap_attr_login'),
195 'ldap_version':3,
192 'ldap_version':3,
196 }
193 }
197 log.debug('Checking for ldap authentication')
194 log.debug('Checking for ldap authentication')
198 try:
195 try:
199 aldap = AuthLdap(**kwargs)
196 aldap = AuthLdap(**kwargs)
200 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
197 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username, password)
201 log.debug('Got ldap DN response %s', user_dn)
198 log.debug('Got ldap DN response %s', user_dn)
202
199
203 user_attrs = {
200 user_attrs = {
204 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
201 'name' : ldap_attrs[ldap_settings.get('ldap_attr_firstname')][0],
205 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
202 'lastname' : ldap_attrs[ldap_settings.get('ldap_attr_lastname')][0],
206 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
203 'email' : ldap_attrs[ldap_settings.get('ldap_attr_email')][0],
207 }
204 }
208
205
209 if user_model.create_ldap(username, password, user_dn, user_attrs):
206 if user_model.create_ldap(username, password, user_dn, user_attrs):
210 log.info('created new ldap user %s', username)
207 log.info('created new ldap user %s', username)
211
208
212 return True
209 return True
213 except (LdapUsernameError, LdapPasswordError,):
210 except (LdapUsernameError, LdapPasswordError,):
214 pass
211 pass
215 except (Exception,):
212 except (Exception,):
216 log.error(traceback.format_exc())
213 log.error(traceback.format_exc())
217 pass
214 pass
218 return False
215 return False
219
216
220 class AuthUser(object):
217 class AuthUser(object):
221 """
218 """
222 A simple object that handles all attributes of user in RhodeCode
219 A simple object that handles all attributes of user in RhodeCode
223
220
224 It does lookup based on API key,given user, or user present in session
221 It does lookup based on API key,given user, or user present in session
225 Then it fills all required information for such user. It also checks if
222 Then it fills all required information for such user. It also checks if
226 anonymous access is enabled and if so, it returns default user as logged
223 anonymous access is enabled and if so, it returns default user as logged
227 in
224 in
228 """
225 """
229
226
230 def __init__(self, user_id=None, api_key=None):
227 def __init__(self, user_id=None, api_key=None):
231
228
232 self.user_id = user_id
229 self.user_id = user_id
233 self.api_key = None
230 self.api_key = None
234
231
235 self.username = 'None'
232 self.username = 'None'
236 self.name = ''
233 self.name = ''
237 self.lastname = ''
234 self.lastname = ''
238 self.email = ''
235 self.email = ''
239 self.is_authenticated = False
236 self.is_authenticated = False
240 self.admin = False
237 self.admin = False
241 self.permissions = {}
238 self.permissions = {}
242 self._api_key = api_key
239 self._api_key = api_key
243 self.propagate_data()
240 self.propagate_data()
244
241
245
242
246 def propagate_data(self):
243 def propagate_data(self):
247 user_model = UserModel()
244 user_model = UserModel()
248 self.anonymous_user = user_model.get_by_username('default', cache=True)
245 self.anonymous_user = user_model.get_by_username('default', cache=True)
249 if self._api_key and self._api_key != self.anonymous_user.api_key:
246 if self._api_key and self._api_key != self.anonymous_user.api_key:
250 #try go get user by api key
247 #try go get user by api key
251 log.debug('Auth User lookup by API KEY %s', self._api_key)
248 log.debug('Auth User lookup by API KEY %s', self._api_key)
252 user_model.fill_data(self, api_key=self._api_key)
249 user_model.fill_data(self, api_key=self._api_key)
253 else:
250 else:
254 log.debug('Auth User lookup by USER ID %s', self.user_id)
251 log.debug('Auth User lookup by USER ID %s', self.user_id)
255 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
252 if self.user_id is not None and self.user_id != self.anonymous_user.user_id:
256 user_model.fill_data(self, user_id=self.user_id)
253 user_model.fill_data(self, user_id=self.user_id)
257 else:
254 else:
258 if self.anonymous_user.active is True:
255 if self.anonymous_user.active is True:
259 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
256 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
260 #then we set this user is logged in
257 #then we set this user is logged in
261 self.is_authenticated = True
258 self.is_authenticated = True
262 else:
259 else:
263 self.is_authenticated = False
260 self.is_authenticated = False
264
261
265 log.debug('Auth User is now %s', self)
262 log.debug('Auth User is now %s', self)
266 user_model.fill_perms(self)
263 user_model.fill_perms(self)
267
264
268 @property
265 @property
269 def is_admin(self):
266 def is_admin(self):
270 return self.admin
267 return self.admin
271
268
272 def __repr__(self):
269 def __repr__(self):
273 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
270 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
274 self.is_authenticated)
271 self.is_authenticated)
275
272
276 def set_authenticated(self, authenticated=True):
273 def set_authenticated(self, authenticated=True):
277
274
278 if self.user_id != self.anonymous_user.user_id:
275 if self.user_id != self.anonymous_user.user_id:
279 self.is_authenticated = authenticated
276 self.is_authenticated = authenticated
280
277
281
278
282 def set_available_permissions(config):
279 def set_available_permissions(config):
283 """This function will propagate pylons globals with all available defined
280 """This function will propagate pylons globals with all available defined
284 permission given in db. We don't want to check each time from db for new
281 permission given in db. We don't want to check each time from db for new
285 permissions since adding a new permission also requires application restart
282 permissions since adding a new permission also requires application restart
286 ie. to decorate new views with the newly created permission
283 ie. to decorate new views with the newly created permission
287
284
288 :param config: current pylons config instance
285 :param config: current pylons config instance
289
286
290 """
287 """
291 log.info('getting information about all available permissions')
288 log.info('getting information about all available permissions')
292 try:
289 try:
293 sa = meta.Session()
290 sa = meta.Session()
294 all_perms = sa.query(Permission).all()
291 all_perms = sa.query(Permission).all()
295 except:
292 except:
296 pass
293 pass
297 finally:
294 finally:
298 meta.Session.remove()
295 meta.Session.remove()
299
296
300 config['available_permissions'] = [x.permission_name for x in all_perms]
297 config['available_permissions'] = [x.permission_name for x in all_perms]
301
298
302 #===============================================================================
299 #===============================================================================
303 # CHECK DECORATORS
300 # CHECK DECORATORS
304 #===============================================================================
301 #===============================================================================
305 class LoginRequired(object):
302 class LoginRequired(object):
306 """
303 """
307 Must be logged in to execute this function else
304 Must be logged in to execute this function else
308 redirect to login page
305 redirect to login page
309
306
310 :param api_access: if enabled this checks only for valid auth token
307 :param api_access: if enabled this checks only for valid auth token
311 and grants access based on valid token
308 and grants access based on valid token
312 """
309 """
313
310
314 def __init__(self, api_access=False):
311 def __init__(self, api_access=False):
315 self.api_access = api_access
312 self.api_access = api_access
316
313
317 def __call__(self, func):
314 def __call__(self, func):
318 return decorator(self.__wrapper, func)
315 return decorator(self.__wrapper, func)
319
316
320 def __wrapper(self, func, *fargs, **fkwargs):
317 def __wrapper(self, func, *fargs, **fkwargs):
321 cls = fargs[0]
318 cls = fargs[0]
322 user = cls.rhodecode_user
319 user = cls.rhodecode_user
323
320
324 api_access_ok = False
321 api_access_ok = False
325 if self.api_access:
322 if self.api_access:
326 log.debug('Checking API KEY access for %s', cls)
323 log.debug('Checking API KEY access for %s', cls)
327 if user.api_key == request.GET.get('api_key'):
324 if user.api_key == request.GET.get('api_key'):
328 api_access_ok = True
325 api_access_ok = True
329 else:
326 else:
330 log.debug("API KEY token not valid")
327 log.debug("API KEY token not valid")
331
328
332 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
329 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
333 if user.is_authenticated or api_access_ok:
330 if user.is_authenticated or api_access_ok:
334 log.debug('user %s is authenticated', user.username)
331 log.debug('user %s is authenticated', user.username)
335 return func(*fargs, **fkwargs)
332 return func(*fargs, **fkwargs)
336 else:
333 else:
337 log.warn('user %s NOT authenticated', user.username)
334 log.warn('user %s NOT authenticated', user.username)
338
335
339 p = ''
336 p = ''
340 if request.environ.get('SCRIPT_NAME') != '/':
337 if request.environ.get('SCRIPT_NAME') != '/':
341 p += request.environ.get('SCRIPT_NAME')
338 p += request.environ.get('SCRIPT_NAME')
342
339
343 p += request.environ.get('PATH_INFO')
340 p += request.environ.get('PATH_INFO')
344 if request.environ.get('QUERY_STRING'):
341 if request.environ.get('QUERY_STRING'):
345 p += '?' + request.environ.get('QUERY_STRING')
342 p += '?' + request.environ.get('QUERY_STRING')
346
343
347 log.debug('redirecting to login page with %s', p)
344 log.debug('redirecting to login page with %s', p)
348 return redirect(url('login_home', came_from=p))
345 return redirect(url('login_home', came_from=p))
349
346
350 class NotAnonymous(object):
347 class NotAnonymous(object):
351 """Must be logged in to execute this function else
348 """Must be logged in to execute this function else
352 redirect to login page"""
349 redirect to login page"""
353
350
354 def __call__(self, func):
351 def __call__(self, func):
355 return decorator(self.__wrapper, func)
352 return decorator(self.__wrapper, func)
356
353
357 def __wrapper(self, func, *fargs, **fkwargs):
354 def __wrapper(self, func, *fargs, **fkwargs):
358 cls = fargs[0]
355 cls = fargs[0]
359 self.user = cls.rhodecode_user
356 self.user = cls.rhodecode_user
360
357
361 log.debug('Checking if user is not anonymous @%s', cls)
358 log.debug('Checking if user is not anonymous @%s', cls)
362
359
363 anonymous = self.user.username == 'default'
360 anonymous = self.user.username == 'default'
364
361
365 if anonymous:
362 if anonymous:
366 p = ''
363 p = ''
367 if request.environ.get('SCRIPT_NAME') != '/':
364 if request.environ.get('SCRIPT_NAME') != '/':
368 p += request.environ.get('SCRIPT_NAME')
365 p += request.environ.get('SCRIPT_NAME')
369
366
370 p += request.environ.get('PATH_INFO')
367 p += request.environ.get('PATH_INFO')
371 if request.environ.get('QUERY_STRING'):
368 if request.environ.get('QUERY_STRING'):
372 p += '?' + request.environ.get('QUERY_STRING')
369 p += '?' + request.environ.get('QUERY_STRING')
373
370
374 import rhodecode.lib.helpers as h
371 import rhodecode.lib.helpers as h
375 h.flash(_('You need to be a registered user to perform this action'),
372 h.flash(_('You need to be a registered user to perform this action'),
376 category='warning')
373 category='warning')
377 return redirect(url('login_home', came_from=p))
374 return redirect(url('login_home', came_from=p))
378 else:
375 else:
379 return func(*fargs, **fkwargs)
376 return func(*fargs, **fkwargs)
380
377
381 class PermsDecorator(object):
378 class PermsDecorator(object):
382 """Base class for controller decorators"""
379 """Base class for controller decorators"""
383
380
384 def __init__(self, *required_perms):
381 def __init__(self, *required_perms):
385 available_perms = config['available_permissions']
382 available_perms = config['available_permissions']
386 for perm in required_perms:
383 for perm in required_perms:
387 if perm not in available_perms:
384 if perm not in available_perms:
388 raise Exception("'%s' permission is not defined" % perm)
385 raise Exception("'%s' permission is not defined" % perm)
389 self.required_perms = set(required_perms)
386 self.required_perms = set(required_perms)
390 self.user_perms = None
387 self.user_perms = None
391
388
392 def __call__(self, func):
389 def __call__(self, func):
393 return decorator(self.__wrapper, func)
390 return decorator(self.__wrapper, func)
394
391
395
392
396 def __wrapper(self, func, *fargs, **fkwargs):
393 def __wrapper(self, func, *fargs, **fkwargs):
397 cls = fargs[0]
394 cls = fargs[0]
398 self.user = cls.rhodecode_user
395 self.user = cls.rhodecode_user
399 self.user_perms = self.user.permissions
396 self.user_perms = self.user.permissions
400 log.debug('checking %s permissions %s for %s %s',
397 log.debug('checking %s permissions %s for %s %s',
401 self.__class__.__name__, self.required_perms, cls,
398 self.__class__.__name__, self.required_perms, cls,
402 self.user)
399 self.user)
403
400
404 if self.check_permissions():
401 if self.check_permissions():
405 log.debug('Permission granted for %s %s', cls, self.user)
402 log.debug('Permission granted for %s %s', cls, self.user)
406 return func(*fargs, **fkwargs)
403 return func(*fargs, **fkwargs)
407
404
408 else:
405 else:
409 log.warning('Permission denied for %s %s', cls, self.user)
406 log.warning('Permission denied for %s %s', cls, self.user)
410 #redirect with forbidden ret code
407 #redirect with forbidden ret code
411 return abort(403)
408 return abort(403)
412
409
413
410
414
411
415 def check_permissions(self):
412 def check_permissions(self):
416 """Dummy function for overriding"""
413 """Dummy function for overriding"""
417 raise Exception('You have to write this function in child class')
414 raise Exception('You have to write this function in child class')
418
415
419 class HasPermissionAllDecorator(PermsDecorator):
416 class HasPermissionAllDecorator(PermsDecorator):
420 """Checks for access permission for all given predicates. All of them
417 """Checks for access permission for all given predicates. All of them
421 have to be meet in order to fulfill the request
418 have to be meet in order to fulfill the request
422 """
419 """
423
420
424 def check_permissions(self):
421 def check_permissions(self):
425 if self.required_perms.issubset(self.user_perms.get('global')):
422 if self.required_perms.issubset(self.user_perms.get('global')):
426 return True
423 return True
427 return False
424 return False
428
425
429
426
430 class HasPermissionAnyDecorator(PermsDecorator):
427 class HasPermissionAnyDecorator(PermsDecorator):
431 """Checks for access permission for any of given predicates. In order to
428 """Checks for access permission for any of given predicates. In order to
432 fulfill the request any of predicates must be meet
429 fulfill the request any of predicates must be meet
433 """
430 """
434
431
435 def check_permissions(self):
432 def check_permissions(self):
436 if self.required_perms.intersection(self.user_perms.get('global')):
433 if self.required_perms.intersection(self.user_perms.get('global')):
437 return True
434 return True
438 return False
435 return False
439
436
440 class HasRepoPermissionAllDecorator(PermsDecorator):
437 class HasRepoPermissionAllDecorator(PermsDecorator):
441 """Checks for access permission for all given predicates for specific
438 """Checks for access permission for all given predicates for specific
442 repository. All of them have to be meet in order to fulfill the request
439 repository. All of them have to be meet in order to fulfill the request
443 """
440 """
444
441
445 def check_permissions(self):
442 def check_permissions(self):
446 repo_name = get_repo_slug(request)
443 repo_name = get_repo_slug(request)
447 try:
444 try:
448 user_perms = set([self.user_perms['repositories'][repo_name]])
445 user_perms = set([self.user_perms['repositories'][repo_name]])
449 except KeyError:
446 except KeyError:
450 return False
447 return False
451 if self.required_perms.issubset(user_perms):
448 if self.required_perms.issubset(user_perms):
452 return True
449 return True
453 return False
450 return False
454
451
455
452
456 class HasRepoPermissionAnyDecorator(PermsDecorator):
453 class HasRepoPermissionAnyDecorator(PermsDecorator):
457 """Checks for access permission for any of given predicates for specific
454 """Checks for access permission for any of given predicates for specific
458 repository. In order to fulfill the request any of predicates must be meet
455 repository. In order to fulfill the request any of predicates must be meet
459 """
456 """
460
457
461 def check_permissions(self):
458 def check_permissions(self):
462 repo_name = get_repo_slug(request)
459 repo_name = get_repo_slug(request)
463
460
464 try:
461 try:
465 user_perms = set([self.user_perms['repositories'][repo_name]])
462 user_perms = set([self.user_perms['repositories'][repo_name]])
466 except KeyError:
463 except KeyError:
467 return False
464 return False
468 if self.required_perms.intersection(user_perms):
465 if self.required_perms.intersection(user_perms):
469 return True
466 return True
470 return False
467 return False
471 #===============================================================================
468 #===============================================================================
472 # CHECK FUNCTIONS
469 # CHECK FUNCTIONS
473 #===============================================================================
470 #===============================================================================
474
471
475 class PermsFunction(object):
472 class PermsFunction(object):
476 """Base function for other check functions"""
473 """Base function for other check functions"""
477
474
478 def __init__(self, *perms):
475 def __init__(self, *perms):
479 available_perms = config['available_permissions']
476 available_perms = config['available_permissions']
480
477
481 for perm in perms:
478 for perm in perms:
482 if perm not in available_perms:
479 if perm not in available_perms:
483 raise Exception("'%s' permission in not defined" % perm)
480 raise Exception("'%s' permission in not defined" % perm)
484 self.required_perms = set(perms)
481 self.required_perms = set(perms)
485 self.user_perms = None
482 self.user_perms = None
486 self.granted_for = ''
483 self.granted_for = ''
487 self.repo_name = None
484 self.repo_name = None
488
485
489 def __call__(self, check_Location=''):
486 def __call__(self, check_Location=''):
490 user = session.get('rhodecode_user', False)
487 user = session.get('rhodecode_user', False)
491 if not user:
488 if not user:
492 return False
489 return False
493 self.user_perms = user.permissions
490 self.user_perms = user.permissions
494 self.granted_for = user
491 self.granted_for = user
495 log.debug('checking %s %s %s', self.__class__.__name__,
492 log.debug('checking %s %s %s', self.__class__.__name__,
496 self.required_perms, user)
493 self.required_perms, user)
497
494
498 if self.check_permissions():
495 if self.check_permissions():
499 log.debug('Permission granted %s @ %s', self.granted_for,
496 log.debug('Permission granted %s @ %s', self.granted_for,
500 check_Location or 'unspecified location')
497 check_Location or 'unspecified location')
501 return True
498 return True
502
499
503 else:
500 else:
504 log.warning('Permission denied for %s @ %s', self.granted_for,
501 log.warning('Permission denied for %s @ %s', self.granted_for,
505 check_Location or 'unspecified location')
502 check_Location or 'unspecified location')
506 return False
503 return False
507
504
508 def check_permissions(self):
505 def check_permissions(self):
509 """Dummy function for overriding"""
506 """Dummy function for overriding"""
510 raise Exception('You have to write this function in child class')
507 raise Exception('You have to write this function in child class')
511
508
512 class HasPermissionAll(PermsFunction):
509 class HasPermissionAll(PermsFunction):
513 def check_permissions(self):
510 def check_permissions(self):
514 if self.required_perms.issubset(self.user_perms.get('global')):
511 if self.required_perms.issubset(self.user_perms.get('global')):
515 return True
512 return True
516 return False
513 return False
517
514
518 class HasPermissionAny(PermsFunction):
515 class HasPermissionAny(PermsFunction):
519 def check_permissions(self):
516 def check_permissions(self):
520 if self.required_perms.intersection(self.user_perms.get('global')):
517 if self.required_perms.intersection(self.user_perms.get('global')):
521 return True
518 return True
522 return False
519 return False
523
520
524 class HasRepoPermissionAll(PermsFunction):
521 class HasRepoPermissionAll(PermsFunction):
525
522
526 def __call__(self, repo_name=None, check_Location=''):
523 def __call__(self, repo_name=None, check_Location=''):
527 self.repo_name = repo_name
524 self.repo_name = repo_name
528 return super(HasRepoPermissionAll, self).__call__(check_Location)
525 return super(HasRepoPermissionAll, self).__call__(check_Location)
529
526
530 def check_permissions(self):
527 def check_permissions(self):
531 if not self.repo_name:
528 if not self.repo_name:
532 self.repo_name = get_repo_slug(request)
529 self.repo_name = get_repo_slug(request)
533
530
534 try:
531 try:
535 self.user_perms = set([self.user_perms['repositories']\
532 self.user_perms = set([self.user_perms['repositories']\
536 [self.repo_name]])
533 [self.repo_name]])
537 except KeyError:
534 except KeyError:
538 return False
535 return False
539 self.granted_for = self.repo_name
536 self.granted_for = self.repo_name
540 if self.required_perms.issubset(self.user_perms):
537 if self.required_perms.issubset(self.user_perms):
541 return True
538 return True
542 return False
539 return False
543
540
544 class HasRepoPermissionAny(PermsFunction):
541 class HasRepoPermissionAny(PermsFunction):
545
542
546 def __call__(self, repo_name=None, check_Location=''):
543 def __call__(self, repo_name=None, check_Location=''):
547 self.repo_name = repo_name
544 self.repo_name = repo_name
548 return super(HasRepoPermissionAny, self).__call__(check_Location)
545 return super(HasRepoPermissionAny, self).__call__(check_Location)
549
546
550 def check_permissions(self):
547 def check_permissions(self):
551 if not self.repo_name:
548 if not self.repo_name:
552 self.repo_name = get_repo_slug(request)
549 self.repo_name = get_repo_slug(request)
553
550
554 try:
551 try:
555 self.user_perms = set([self.user_perms['repositories']\
552 self.user_perms = set([self.user_perms['repositories']\
556 [self.repo_name]])
553 [self.repo_name]])
557 except KeyError:
554 except KeyError:
558 return False
555 return False
559 self.granted_for = self.repo_name
556 self.granted_for = self.repo_name
560 if self.required_perms.intersection(self.user_perms):
557 if self.required_perms.intersection(self.user_perms):
561 return True
558 return True
562 return False
559 return False
563
560
564 #===============================================================================
561 #===============================================================================
565 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
562 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
566 #===============================================================================
563 #===============================================================================
567
564
568 class HasPermissionAnyMiddleware(object):
565 class HasPermissionAnyMiddleware(object):
569 def __init__(self, *perms):
566 def __init__(self, *perms):
570 self.required_perms = set(perms)
567 self.required_perms = set(perms)
571
568
572 def __call__(self, user, repo_name):
569 def __call__(self, user, repo_name):
573 usr = AuthUser(user.user_id)
570 usr = AuthUser(user.user_id)
574 try:
571 try:
575 self.user_perms = set([usr.permissions['repositories'][repo_name]])
572 self.user_perms = set([usr.permissions['repositories'][repo_name]])
576 except:
573 except:
577 self.user_perms = set()
574 self.user_perms = set()
578 self.granted_for = ''
575 self.granted_for = ''
579 self.username = user.username
576 self.username = user.username
580 self.repo_name = repo_name
577 self.repo_name = repo_name
581 return self.check_permissions()
578 return self.check_permissions()
582
579
583 def check_permissions(self):
580 def check_permissions(self):
584 log.debug('checking mercurial protocol '
581 log.debug('checking mercurial protocol '
585 'permissions %s for user:%s repository:%s', self.user_perms,
582 'permissions %s for user:%s repository:%s', self.user_perms,
586 self.username, self.repo_name)
583 self.username, self.repo_name)
587 if self.required_perms.intersection(self.user_perms):
584 if self.required_perms.intersection(self.user_perms):
588 log.debug('permission granted')
585 log.debug('permission granted')
589 return True
586 return True
590 log.debug('permission denied')
587 log.debug('permission denied')
591 return False
588 return False
General Comments 0
You need to be logged in to leave comments. Login now