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