##// END OF EJS Templates
added some more logging into get_container_username function
marcink -
r3172:264d9c93 beta
parent child Browse files
Show More
@@ -1,982 +1,984
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 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode import __platform__, is_windows, is_unix
38 from rhodecode import __platform__, is_windows, is_unix
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
44 from rhodecode.lib.auth_ldap import AuthLdap
44 from rhodecode.lib.auth_ldap import AuthLdap
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
49 from rhodecode.lib.caching_query import FromCache
49 from rhodecode.lib.caching_query import FromCache
50
50
51 log = logging.getLogger(__name__)
51 log = logging.getLogger(__name__)
52
52
53
53
54 class PasswordGenerator(object):
54 class PasswordGenerator(object):
55 """
55 """
56 This is a simple class for generating password from different sets of
56 This is a simple class for generating password from different sets of
57 characters
57 characters
58 usage::
58 usage::
59
59
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 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
63 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, length, type_=None):
79 def gen_password(self, length, type_=None):
80 if type_ is None:
80 if type_ is None:
81 type_ = self.ALPHABETS_FULL
81 type_ = self.ALPHABETS_FULL
82 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
82 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
83 return self.passwd
83 return self.passwd
84
84
85
85
86 class RhodeCodeCrypto(object):
86 class RhodeCodeCrypto(object):
87
87
88 @classmethod
88 @classmethod
89 def hash_string(cls, str_):
89 def hash_string(cls, str_):
90 """
90 """
91 Cryptographic function used for password hashing based on pybcrypt
91 Cryptographic function used for password hashing based on pybcrypt
92 or pycrypto in windows
92 or pycrypto in windows
93
93
94 :param password: password to hash
94 :param password: password to hash
95 """
95 """
96 if is_windows:
96 if is_windows:
97 from hashlib import sha256
97 from hashlib import sha256
98 return sha256(str_).hexdigest()
98 return sha256(str_).hexdigest()
99 elif is_unix:
99 elif is_unix:
100 import bcrypt
100 import bcrypt
101 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
101 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
102 else:
102 else:
103 raise Exception('Unknown or unsupported platform %s' \
103 raise Exception('Unknown or unsupported platform %s' \
104 % __platform__)
104 % __platform__)
105
105
106 @classmethod
106 @classmethod
107 def hash_check(cls, password, hashed):
107 def hash_check(cls, password, hashed):
108 """
108 """
109 Checks matching password with it's hashed value, runs different
109 Checks matching password with it's hashed value, runs different
110 implementation based on platform it runs on
110 implementation based on platform it runs on
111
111
112 :param password: password
112 :param password: password
113 :param hashed: password in hashed form
113 :param hashed: password in hashed form
114 """
114 """
115
115
116 if is_windows:
116 if is_windows:
117 from hashlib import sha256
117 from hashlib import sha256
118 return sha256(password).hexdigest() == hashed
118 return sha256(password).hexdigest() == hashed
119 elif is_unix:
119 elif is_unix:
120 import bcrypt
120 import bcrypt
121 return bcrypt.hashpw(password, hashed) == hashed
121 return bcrypt.hashpw(password, hashed) == hashed
122 else:
122 else:
123 raise Exception('Unknown or unsupported platform %s' \
123 raise Exception('Unknown or unsupported platform %s' \
124 % __platform__)
124 % __platform__)
125
125
126
126
127 def get_crypt_password(password):
127 def get_crypt_password(password):
128 return RhodeCodeCrypto.hash_string(password)
128 return RhodeCodeCrypto.hash_string(password)
129
129
130
130
131 def check_password(password, hashed):
131 def check_password(password, hashed):
132 return RhodeCodeCrypto.hash_check(password, hashed)
132 return RhodeCodeCrypto.hash_check(password, hashed)
133
133
134
134
135 def generate_api_key(str_, salt=None):
135 def generate_api_key(str_, salt=None):
136 """
136 """
137 Generates API KEY from given string
137 Generates API KEY from given string
138
138
139 :param str_:
139 :param str_:
140 :param salt:
140 :param salt:
141 """
141 """
142
142
143 if salt is None:
143 if salt is None:
144 salt = _RandomNameSequence().next()
144 salt = _RandomNameSequence().next()
145
145
146 return hashlib.sha1(str_ + salt).hexdigest()
146 return hashlib.sha1(str_ + salt).hexdigest()
147
147
148
148
149 def authfunc(environ, username, password):
149 def authfunc(environ, username, password):
150 """
150 """
151 Dummy authentication wrapper function used in Mercurial and Git for
151 Dummy authentication wrapper function used in Mercurial and Git for
152 access control.
152 access control.
153
153
154 :param environ: needed only for using in Basic auth
154 :param environ: needed only for using in Basic auth
155 """
155 """
156 return authenticate(username, password)
156 return authenticate(username, password)
157
157
158
158
159 def authenticate(username, password):
159 def authenticate(username, password):
160 """
160 """
161 Authentication function used for access control,
161 Authentication function used for access control,
162 firstly checks for db authentication then if ldap is enabled for ldap
162 firstly checks for db authentication then if ldap is enabled for ldap
163 authentication, also creates ldap user if not in database
163 authentication, also creates ldap user if not in database
164
164
165 :param username: username
165 :param username: username
166 :param password: password
166 :param password: password
167 """
167 """
168
168
169 user_model = UserModel()
169 user_model = UserModel()
170 user = User.get_by_username(username)
170 user = User.get_by_username(username)
171
171
172 log.debug('Authenticating user using RhodeCode account')
172 log.debug('Authenticating user using RhodeCode account')
173 if user is not None and not user.ldap_dn:
173 if user is not None and not user.ldap_dn:
174 if user.active:
174 if user.active:
175 if user.username == 'default' and user.active:
175 if user.username == 'default' and user.active:
176 log.info('user %s authenticated correctly as anonymous user' %
176 log.info('user %s authenticated correctly as anonymous user' %
177 username)
177 username)
178 return True
178 return True
179
179
180 elif user.username == username and check_password(password,
180 elif user.username == username and check_password(password,
181 user.password):
181 user.password):
182 log.info('user %s authenticated correctly' % username)
182 log.info('user %s authenticated correctly' % username)
183 return True
183 return True
184 else:
184 else:
185 log.warning('user %s tried auth but is disabled' % username)
185 log.warning('user %s tried auth but is disabled' % username)
186
186
187 else:
187 else:
188 log.debug('Regular authentication failed')
188 log.debug('Regular authentication failed')
189 user_obj = User.get_by_username(username, case_insensitive=True)
189 user_obj = User.get_by_username(username, case_insensitive=True)
190
190
191 if user_obj is not None and not user_obj.ldap_dn:
191 if user_obj is not None and not user_obj.ldap_dn:
192 log.debug('this user already exists as non ldap')
192 log.debug('this user already exists as non ldap')
193 return False
193 return False
194
194
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 #======================================================================
196 #======================================================================
197 # FALLBACK TO LDAP AUTH IF ENABLE
197 # FALLBACK TO LDAP AUTH IF ENABLE
198 #======================================================================
198 #======================================================================
199 if str2bool(ldap_settings.get('ldap_active')):
199 if str2bool(ldap_settings.get('ldap_active')):
200 log.debug("Authenticating user using ldap")
200 log.debug("Authenticating user using ldap")
201 kwargs = {
201 kwargs = {
202 'server': ldap_settings.get('ldap_host', ''),
202 'server': ldap_settings.get('ldap_host', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 'port': ldap_settings.get('ldap_port'),
204 'port': ldap_settings.get('ldap_port'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
212 'ldap_version': 3,
212 'ldap_version': 3,
213 }
213 }
214 log.debug('Checking for ldap authentication')
214 log.debug('Checking for ldap authentication')
215 try:
215 try:
216 aldap = AuthLdap(**kwargs)
216 aldap = AuthLdap(**kwargs)
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 password)
218 password)
219 log.debug('Got ldap DN response %s' % user_dn)
219 log.debug('Got ldap DN response %s' % user_dn)
220
220
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 .get(k), [''])[0]
222 .get(k), [''])[0]
223
223
224 user_attrs = {
224 user_attrs = {
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 'email': get_ldap_attr('ldap_attr_email'),
227 'email': get_ldap_attr('ldap_attr_email'),
228 }
228 }
229
229
230 # don't store LDAP password since we don't need it. Override
230 # don't store LDAP password since we don't need it. Override
231 # with some random generated password
231 # with some random generated password
232 _password = PasswordGenerator().gen_password(length=8)
232 _password = PasswordGenerator().gen_password(length=8)
233 # create this user on the fly if it doesn't exist in rhodecode
233 # create this user on the fly if it doesn't exist in rhodecode
234 # database
234 # database
235 if user_model.create_ldap(username, _password, user_dn,
235 if user_model.create_ldap(username, _password, user_dn,
236 user_attrs):
236 user_attrs):
237 log.info('created new ldap user %s' % username)
237 log.info('created new ldap user %s' % username)
238
238
239 Session().commit()
239 Session().commit()
240 return True
240 return True
241 except (LdapUsernameError, LdapPasswordError,):
241 except (LdapUsernameError, LdapPasswordError,):
242 pass
242 pass
243 except (Exception,):
243 except (Exception,):
244 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
245 pass
245 pass
246 return False
246 return False
247
247
248
248
249 def login_container_auth(username):
249 def login_container_auth(username):
250 user = User.get_by_username(username)
250 user = User.get_by_username(username)
251 if user is None:
251 if user is None:
252 user_attrs = {
252 user_attrs = {
253 'name': username,
253 'name': username,
254 'lastname': None,
254 'lastname': None,
255 'email': None,
255 'email': None,
256 }
256 }
257 user = UserModel().create_for_container_auth(username, user_attrs)
257 user = UserModel().create_for_container_auth(username, user_attrs)
258 if not user:
258 if not user:
259 return None
259 return None
260 log.info('User %s was created by container authentication' % username)
260 log.info('User %s was created by container authentication' % username)
261
261
262 if not user.active:
262 if not user.active:
263 return None
263 return None
264
264
265 user.update_lastlogin()
265 user.update_lastlogin()
266 Session().commit()
266 Session().commit()
267
267
268 log.debug('User %s is now logged in by container authentication',
268 log.debug('User %s is now logged in by container authentication',
269 user.username)
269 user.username)
270 return user
270 return user
271
271
272
272
273 def get_container_username(environ, config):
273 def get_container_username(environ, config):
274 username = None
274 username = None
275
275
276 if str2bool(config.get('container_auth_enabled', False)):
276 if str2bool(config.get('container_auth_enabled', False)):
277 from paste.httpheaders import REMOTE_USER
277 from paste.httpheaders import REMOTE_USER
278 username = REMOTE_USER(environ)
278 username = REMOTE_USER(environ)
279 log.debug('extracted REMOTE_USER:%s' % (username))
279
280
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 username = environ.get('HTTP_X_FORWARDED_USER')
282 username = environ.get('HTTP_X_FORWARDED_USER')
283 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
282
284
283 if username:
285 if username:
284 # Removing realm and domain from username
286 # Removing realm and domain from username
285 username = username.partition('@')[0]
287 username = username.partition('@')[0]
286 username = username.rpartition('\\')[2]
288 username = username.rpartition('\\')[2]
287 log.debug('Received username %s from container' % username)
289 log.debug('Received username %s from container' % username)
288
290
289 return username
291 return username
290
292
291
293
292 class CookieStoreWrapper(object):
294 class CookieStoreWrapper(object):
293
295
294 def __init__(self, cookie_store):
296 def __init__(self, cookie_store):
295 self.cookie_store = cookie_store
297 self.cookie_store = cookie_store
296
298
297 def __repr__(self):
299 def __repr__(self):
298 return 'CookieStore<%s>' % (self.cookie_store)
300 return 'CookieStore<%s>' % (self.cookie_store)
299
301
300 def get(self, key, other=None):
302 def get(self, key, other=None):
301 if isinstance(self.cookie_store, dict):
303 if isinstance(self.cookie_store, dict):
302 return self.cookie_store.get(key, other)
304 return self.cookie_store.get(key, other)
303 elif isinstance(self.cookie_store, AuthUser):
305 elif isinstance(self.cookie_store, AuthUser):
304 return self.cookie_store.__dict__.get(key, other)
306 return self.cookie_store.__dict__.get(key, other)
305
307
306
308
307 class AuthUser(object):
309 class AuthUser(object):
308 """
310 """
309 A simple object that handles all attributes of user in RhodeCode
311 A simple object that handles all attributes of user in RhodeCode
310
312
311 It does lookup based on API key,given user, or user present in session
313 It does lookup based on API key,given user, or user present in session
312 Then it fills all required information for such user. It also checks if
314 Then it fills all required information for such user. It also checks if
313 anonymous access is enabled and if so, it returns default user as logged
315 anonymous access is enabled and if so, it returns default user as logged
314 in
316 in
315 """
317 """
316
318
317 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
319 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
318
320
319 self.user_id = user_id
321 self.user_id = user_id
320 self.api_key = None
322 self.api_key = None
321 self.username = username
323 self.username = username
322 self.ip_addr = ip_addr
324 self.ip_addr = ip_addr
323
325
324 self.name = ''
326 self.name = ''
325 self.lastname = ''
327 self.lastname = ''
326 self.email = ''
328 self.email = ''
327 self.is_authenticated = False
329 self.is_authenticated = False
328 self.admin = False
330 self.admin = False
329 self.inherit_default_permissions = False
331 self.inherit_default_permissions = False
330 self.permissions = {}
332 self.permissions = {}
331 self._api_key = api_key
333 self._api_key = api_key
332 self.propagate_data()
334 self.propagate_data()
333 self._instance = None
335 self._instance = None
334
336
335 def propagate_data(self):
337 def propagate_data(self):
336 user_model = UserModel()
338 user_model = UserModel()
337 self.anonymous_user = User.get_by_username('default', cache=True)
339 self.anonymous_user = User.get_by_username('default', cache=True)
338 is_user_loaded = False
340 is_user_loaded = False
339
341
340 # try go get user by api key
342 # try go get user by api key
341 if self._api_key and self._api_key != self.anonymous_user.api_key:
343 if self._api_key and self._api_key != self.anonymous_user.api_key:
342 log.debug('Auth User lookup by API KEY %s' % self._api_key)
344 log.debug('Auth User lookup by API KEY %s' % self._api_key)
343 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
345 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
344 # lookup by userid
346 # lookup by userid
345 elif (self.user_id is not None and
347 elif (self.user_id is not None and
346 self.user_id != self.anonymous_user.user_id):
348 self.user_id != self.anonymous_user.user_id):
347 log.debug('Auth User lookup by USER ID %s' % self.user_id)
349 log.debug('Auth User lookup by USER ID %s' % self.user_id)
348 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
350 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
349 # lookup by username
351 # lookup by username
350 elif self.username and \
352 elif self.username and \
351 str2bool(config.get('container_auth_enabled', False)):
353 str2bool(config.get('container_auth_enabled', False)):
352
354
353 log.debug('Auth User lookup by USER NAME %s' % self.username)
355 log.debug('Auth User lookup by USER NAME %s' % self.username)
354 dbuser = login_container_auth(self.username)
356 dbuser = login_container_auth(self.username)
355 if dbuser is not None:
357 if dbuser is not None:
356 log.debug('filling all attributes to object')
358 log.debug('filling all attributes to object')
357 for k, v in dbuser.get_dict().items():
359 for k, v in dbuser.get_dict().items():
358 setattr(self, k, v)
360 setattr(self, k, v)
359 self.set_authenticated()
361 self.set_authenticated()
360 is_user_loaded = True
362 is_user_loaded = True
361 else:
363 else:
362 log.debug('No data in %s that could been used to log in' % self)
364 log.debug('No data in %s that could been used to log in' % self)
363
365
364 if not is_user_loaded:
366 if not is_user_loaded:
365 # if we cannot authenticate user try anonymous
367 # if we cannot authenticate user try anonymous
366 if self.anonymous_user.active is True:
368 if self.anonymous_user.active is True:
367 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
369 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
368 # then we set this user is logged in
370 # then we set this user is logged in
369 self.is_authenticated = True
371 self.is_authenticated = True
370 else:
372 else:
371 self.user_id = None
373 self.user_id = None
372 self.username = None
374 self.username = None
373 self.is_authenticated = False
375 self.is_authenticated = False
374
376
375 if not self.username:
377 if not self.username:
376 self.username = 'None'
378 self.username = 'None'
377
379
378 log.debug('Auth User is now %s' % self)
380 log.debug('Auth User is now %s' % self)
379 user_model.fill_perms(self)
381 user_model.fill_perms(self)
380
382
381 @property
383 @property
382 def is_admin(self):
384 def is_admin(self):
383 return self.admin
385 return self.admin
384
386
385 @property
387 @property
386 def ip_allowed(self):
388 def ip_allowed(self):
387 """
389 """
388 Checks if ip_addr used in constructor is allowed from defined list of
390 Checks if ip_addr used in constructor is allowed from defined list of
389 allowed ip_addresses for user
391 allowed ip_addresses for user
390
392
391 :returns: boolean, True if ip is in allowed ip range
393 :returns: boolean, True if ip is in allowed ip range
392 """
394 """
393 #check IP
395 #check IP
394 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
396 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
395 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
397 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
396 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
398 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
397 return True
399 return True
398 else:
400 else:
399 log.info('Access for IP:%s forbidden, '
401 log.info('Access for IP:%s forbidden, '
400 'not in %s' % (self.ip_addr, allowed_ips))
402 'not in %s' % (self.ip_addr, allowed_ips))
401 return False
403 return False
402
404
403 def __repr__(self):
405 def __repr__(self):
404 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
406 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
405 self.is_authenticated)
407 self.is_authenticated)
406
408
407 def set_authenticated(self, authenticated=True):
409 def set_authenticated(self, authenticated=True):
408 if self.user_id != self.anonymous_user.user_id:
410 if self.user_id != self.anonymous_user.user_id:
409 self.is_authenticated = authenticated
411 self.is_authenticated = authenticated
410
412
411 def get_cookie_store(self):
413 def get_cookie_store(self):
412 return {'username': self.username,
414 return {'username': self.username,
413 'user_id': self.user_id,
415 'user_id': self.user_id,
414 'is_authenticated': self.is_authenticated}
416 'is_authenticated': self.is_authenticated}
415
417
416 @classmethod
418 @classmethod
417 def from_cookie_store(cls, cookie_store):
419 def from_cookie_store(cls, cookie_store):
418 """
420 """
419 Creates AuthUser from a cookie store
421 Creates AuthUser from a cookie store
420
422
421 :param cls:
423 :param cls:
422 :param cookie_store:
424 :param cookie_store:
423 """
425 """
424 user_id = cookie_store.get('user_id')
426 user_id = cookie_store.get('user_id')
425 username = cookie_store.get('username')
427 username = cookie_store.get('username')
426 api_key = cookie_store.get('api_key')
428 api_key = cookie_store.get('api_key')
427 return AuthUser(user_id, api_key, username)
429 return AuthUser(user_id, api_key, username)
428
430
429 @classmethod
431 @classmethod
430 def get_allowed_ips(cls, user_id, cache=False):
432 def get_allowed_ips(cls, user_id, cache=False):
431 _set = set()
433 _set = set()
432 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
434 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
433 if cache:
435 if cache:
434 user_ips = user_ips.options(FromCache("sql_cache_short",
436 user_ips = user_ips.options(FromCache("sql_cache_short",
435 "get_user_ips_%s" % user_id))
437 "get_user_ips_%s" % user_id))
436 for ip in user_ips:
438 for ip in user_ips:
437 _set.add(ip.ip_addr)
439 _set.add(ip.ip_addr)
438 return _set or set(['0.0.0.0/0'])
440 return _set or set(['0.0.0.0/0'])
439
441
440
442
441 def set_available_permissions(config):
443 def set_available_permissions(config):
442 """
444 """
443 This function will propagate pylons globals with all available defined
445 This function will propagate pylons globals with all available defined
444 permission given in db. We don't want to check each time from db for new
446 permission given in db. We don't want to check each time from db for new
445 permissions since adding a new permission also requires application restart
447 permissions since adding a new permission also requires application restart
446 ie. to decorate new views with the newly created permission
448 ie. to decorate new views with the newly created permission
447
449
448 :param config: current pylons config instance
450 :param config: current pylons config instance
449
451
450 """
452 """
451 log.info('getting information about all available permissions')
453 log.info('getting information about all available permissions')
452 try:
454 try:
453 sa = meta.Session
455 sa = meta.Session
454 all_perms = sa.query(Permission).all()
456 all_perms = sa.query(Permission).all()
455 except Exception:
457 except Exception:
456 pass
458 pass
457 finally:
459 finally:
458 meta.Session.remove()
460 meta.Session.remove()
459
461
460 config['available_permissions'] = [x.permission_name for x in all_perms]
462 config['available_permissions'] = [x.permission_name for x in all_perms]
461
463
462
464
463 #==============================================================================
465 #==============================================================================
464 # CHECK DECORATORS
466 # CHECK DECORATORS
465 #==============================================================================
467 #==============================================================================
466 class LoginRequired(object):
468 class LoginRequired(object):
467 """
469 """
468 Must be logged in to execute this function else
470 Must be logged in to execute this function else
469 redirect to login page
471 redirect to login page
470
472
471 :param api_access: if enabled this checks only for valid auth token
473 :param api_access: if enabled this checks only for valid auth token
472 and grants access based on valid token
474 and grants access based on valid token
473 """
475 """
474
476
475 def __init__(self, api_access=False):
477 def __init__(self, api_access=False):
476 self.api_access = api_access
478 self.api_access = api_access
477
479
478 def __call__(self, func):
480 def __call__(self, func):
479 return decorator(self.__wrapper, func)
481 return decorator(self.__wrapper, func)
480
482
481 def __wrapper(self, func, *fargs, **fkwargs):
483 def __wrapper(self, func, *fargs, **fkwargs):
482 cls = fargs[0]
484 cls = fargs[0]
483 user = cls.rhodecode_user
485 user = cls.rhodecode_user
484 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
486 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
485
487
486 #check IP
488 #check IP
487 ip_access_ok = True
489 ip_access_ok = True
488 if not user.ip_allowed:
490 if not user.ip_allowed:
489 from rhodecode.lib import helpers as h
491 from rhodecode.lib import helpers as h
490 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
492 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
491 category='warning')
493 category='warning')
492 ip_access_ok = False
494 ip_access_ok = False
493
495
494 api_access_ok = False
496 api_access_ok = False
495 if self.api_access:
497 if self.api_access:
496 log.debug('Checking API KEY access for %s' % cls)
498 log.debug('Checking API KEY access for %s' % cls)
497 if user.api_key == request.GET.get('api_key'):
499 if user.api_key == request.GET.get('api_key'):
498 api_access_ok = True
500 api_access_ok = True
499 else:
501 else:
500 log.debug("API KEY token not valid")
502 log.debug("API KEY token not valid")
501
503
502 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
504 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
503 if (user.is_authenticated or api_access_ok) and ip_access_ok:
505 if (user.is_authenticated or api_access_ok) and ip_access_ok:
504 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
506 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
505 log.info('user %s is authenticated and granted access to %s '
507 log.info('user %s is authenticated and granted access to %s '
506 'using %s' % (user.username, loc, reason)
508 'using %s' % (user.username, loc, reason)
507 )
509 )
508 return func(*fargs, **fkwargs)
510 return func(*fargs, **fkwargs)
509 else:
511 else:
510 log.warn('user %s NOT authenticated on func: %s' % (
512 log.warn('user %s NOT authenticated on func: %s' % (
511 user, loc)
513 user, loc)
512 )
514 )
513 p = url.current()
515 p = url.current()
514
516
515 log.debug('redirecting to login page with %s' % p)
517 log.debug('redirecting to login page with %s' % p)
516 return redirect(url('login_home', came_from=p))
518 return redirect(url('login_home', came_from=p))
517
519
518
520
519 class NotAnonymous(object):
521 class NotAnonymous(object):
520 """
522 """
521 Must be logged in to execute this function else
523 Must be logged in to execute this function else
522 redirect to login page"""
524 redirect to login page"""
523
525
524 def __call__(self, func):
526 def __call__(self, func):
525 return decorator(self.__wrapper, func)
527 return decorator(self.__wrapper, func)
526
528
527 def __wrapper(self, func, *fargs, **fkwargs):
529 def __wrapper(self, func, *fargs, **fkwargs):
528 cls = fargs[0]
530 cls = fargs[0]
529 self.user = cls.rhodecode_user
531 self.user = cls.rhodecode_user
530
532
531 log.debug('Checking if user is not anonymous @%s' % cls)
533 log.debug('Checking if user is not anonymous @%s' % cls)
532
534
533 anonymous = self.user.username == 'default'
535 anonymous = self.user.username == 'default'
534
536
535 if anonymous:
537 if anonymous:
536 p = url.current()
538 p = url.current()
537
539
538 import rhodecode.lib.helpers as h
540 import rhodecode.lib.helpers as h
539 h.flash(_('You need to be a registered user to '
541 h.flash(_('You need to be a registered user to '
540 'perform this action'),
542 'perform this action'),
541 category='warning')
543 category='warning')
542 return redirect(url('login_home', came_from=p))
544 return redirect(url('login_home', came_from=p))
543 else:
545 else:
544 return func(*fargs, **fkwargs)
546 return func(*fargs, **fkwargs)
545
547
546
548
547 class PermsDecorator(object):
549 class PermsDecorator(object):
548 """Base class for controller decorators"""
550 """Base class for controller decorators"""
549
551
550 def __init__(self, *required_perms):
552 def __init__(self, *required_perms):
551 available_perms = config['available_permissions']
553 available_perms = config['available_permissions']
552 for perm in required_perms:
554 for perm in required_perms:
553 if perm not in available_perms:
555 if perm not in available_perms:
554 raise Exception("'%s' permission is not defined" % perm)
556 raise Exception("'%s' permission is not defined" % perm)
555 self.required_perms = set(required_perms)
557 self.required_perms = set(required_perms)
556 self.user_perms = None
558 self.user_perms = None
557
559
558 def __call__(self, func):
560 def __call__(self, func):
559 return decorator(self.__wrapper, func)
561 return decorator(self.__wrapper, func)
560
562
561 def __wrapper(self, func, *fargs, **fkwargs):
563 def __wrapper(self, func, *fargs, **fkwargs):
562 cls = fargs[0]
564 cls = fargs[0]
563 self.user = cls.rhodecode_user
565 self.user = cls.rhodecode_user
564 self.user_perms = self.user.permissions
566 self.user_perms = self.user.permissions
565 log.debug('checking %s permissions %s for %s %s',
567 log.debug('checking %s permissions %s for %s %s',
566 self.__class__.__name__, self.required_perms, cls, self.user)
568 self.__class__.__name__, self.required_perms, cls, self.user)
567
569
568 if self.check_permissions():
570 if self.check_permissions():
569 log.debug('Permission granted for %s %s' % (cls, self.user))
571 log.debug('Permission granted for %s %s' % (cls, self.user))
570 return func(*fargs, **fkwargs)
572 return func(*fargs, **fkwargs)
571
573
572 else:
574 else:
573 log.debug('Permission denied for %s %s' % (cls, self.user))
575 log.debug('Permission denied for %s %s' % (cls, self.user))
574 anonymous = self.user.username == 'default'
576 anonymous = self.user.username == 'default'
575
577
576 if anonymous:
578 if anonymous:
577 p = url.current()
579 p = url.current()
578
580
579 import rhodecode.lib.helpers as h
581 import rhodecode.lib.helpers as h
580 h.flash(_('You need to be a signed in to '
582 h.flash(_('You need to be a signed in to '
581 'view this page'),
583 'view this page'),
582 category='warning')
584 category='warning')
583 return redirect(url('login_home', came_from=p))
585 return redirect(url('login_home', came_from=p))
584
586
585 else:
587 else:
586 # redirect with forbidden ret code
588 # redirect with forbidden ret code
587 return abort(403)
589 return abort(403)
588
590
589 def check_permissions(self):
591 def check_permissions(self):
590 """Dummy function for overriding"""
592 """Dummy function for overriding"""
591 raise Exception('You have to write this function in child class')
593 raise Exception('You have to write this function in child class')
592
594
593
595
594 class HasPermissionAllDecorator(PermsDecorator):
596 class HasPermissionAllDecorator(PermsDecorator):
595 """
597 """
596 Checks for access permission for all given predicates. All of them
598 Checks for access permission for all given predicates. All of them
597 have to be meet in order to fulfill the request
599 have to be meet in order to fulfill the request
598 """
600 """
599
601
600 def check_permissions(self):
602 def check_permissions(self):
601 if self.required_perms.issubset(self.user_perms.get('global')):
603 if self.required_perms.issubset(self.user_perms.get('global')):
602 return True
604 return True
603 return False
605 return False
604
606
605
607
606 class HasPermissionAnyDecorator(PermsDecorator):
608 class HasPermissionAnyDecorator(PermsDecorator):
607 """
609 """
608 Checks for access permission for any of given predicates. In order to
610 Checks for access permission for any of given predicates. In order to
609 fulfill the request any of predicates must be meet
611 fulfill the request any of predicates must be meet
610 """
612 """
611
613
612 def check_permissions(self):
614 def check_permissions(self):
613 if self.required_perms.intersection(self.user_perms.get('global')):
615 if self.required_perms.intersection(self.user_perms.get('global')):
614 return True
616 return True
615 return False
617 return False
616
618
617
619
618 class HasRepoPermissionAllDecorator(PermsDecorator):
620 class HasRepoPermissionAllDecorator(PermsDecorator):
619 """
621 """
620 Checks for access permission for all given predicates for specific
622 Checks for access permission for all given predicates for specific
621 repository. All of them have to be meet in order to fulfill the request
623 repository. All of them have to be meet in order to fulfill the request
622 """
624 """
623
625
624 def check_permissions(self):
626 def check_permissions(self):
625 repo_name = get_repo_slug(request)
627 repo_name = get_repo_slug(request)
626 try:
628 try:
627 user_perms = set([self.user_perms['repositories'][repo_name]])
629 user_perms = set([self.user_perms['repositories'][repo_name]])
628 except KeyError:
630 except KeyError:
629 return False
631 return False
630 if self.required_perms.issubset(user_perms):
632 if self.required_perms.issubset(user_perms):
631 return True
633 return True
632 return False
634 return False
633
635
634
636
635 class HasRepoPermissionAnyDecorator(PermsDecorator):
637 class HasRepoPermissionAnyDecorator(PermsDecorator):
636 """
638 """
637 Checks for access permission for any of given predicates for specific
639 Checks for access permission for any of given predicates for specific
638 repository. In order to fulfill the request any of predicates must be meet
640 repository. In order to fulfill the request any of predicates must be meet
639 """
641 """
640
642
641 def check_permissions(self):
643 def check_permissions(self):
642 repo_name = get_repo_slug(request)
644 repo_name = get_repo_slug(request)
643
645
644 try:
646 try:
645 user_perms = set([self.user_perms['repositories'][repo_name]])
647 user_perms = set([self.user_perms['repositories'][repo_name]])
646 except KeyError:
648 except KeyError:
647 return False
649 return False
648
650
649 if self.required_perms.intersection(user_perms):
651 if self.required_perms.intersection(user_perms):
650 return True
652 return True
651 return False
653 return False
652
654
653
655
654 class HasReposGroupPermissionAllDecorator(PermsDecorator):
656 class HasReposGroupPermissionAllDecorator(PermsDecorator):
655 """
657 """
656 Checks for access permission for all given predicates for specific
658 Checks for access permission for all given predicates for specific
657 repository. All of them have to be meet in order to fulfill the request
659 repository. All of them have to be meet in order to fulfill the request
658 """
660 """
659
661
660 def check_permissions(self):
662 def check_permissions(self):
661 group_name = get_repos_group_slug(request)
663 group_name = get_repos_group_slug(request)
662 try:
664 try:
663 user_perms = set([self.user_perms['repositories_groups'][group_name]])
665 user_perms = set([self.user_perms['repositories_groups'][group_name]])
664 except KeyError:
666 except KeyError:
665 return False
667 return False
666 if self.required_perms.issubset(user_perms):
668 if self.required_perms.issubset(user_perms):
667 return True
669 return True
668 return False
670 return False
669
671
670
672
671 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
673 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
672 """
674 """
673 Checks for access permission for any of given predicates for specific
675 Checks for access permission for any of given predicates for specific
674 repository. In order to fulfill the request any of predicates must be meet
676 repository. In order to fulfill the request any of predicates must be meet
675 """
677 """
676
678
677 def check_permissions(self):
679 def check_permissions(self):
678 group_name = get_repos_group_slug(request)
680 group_name = get_repos_group_slug(request)
679
681
680 try:
682 try:
681 user_perms = set([self.user_perms['repositories_groups'][group_name]])
683 user_perms = set([self.user_perms['repositories_groups'][group_name]])
682 except KeyError:
684 except KeyError:
683 return False
685 return False
684 if self.required_perms.intersection(user_perms):
686 if self.required_perms.intersection(user_perms):
685 return True
687 return True
686 return False
688 return False
687
689
688
690
689 #==============================================================================
691 #==============================================================================
690 # CHECK FUNCTIONS
692 # CHECK FUNCTIONS
691 #==============================================================================
693 #==============================================================================
692 class PermsFunction(object):
694 class PermsFunction(object):
693 """Base function for other check functions"""
695 """Base function for other check functions"""
694
696
695 def __init__(self, *perms):
697 def __init__(self, *perms):
696 available_perms = config['available_permissions']
698 available_perms = config['available_permissions']
697
699
698 for perm in perms:
700 for perm in perms:
699 if perm not in available_perms:
701 if perm not in available_perms:
700 raise Exception("'%s' permission is not defined" % perm)
702 raise Exception("'%s' permission is not defined" % perm)
701 self.required_perms = set(perms)
703 self.required_perms = set(perms)
702 self.user_perms = None
704 self.user_perms = None
703 self.repo_name = None
705 self.repo_name = None
704 self.group_name = None
706 self.group_name = None
705
707
706 def __call__(self, check_Location=''):
708 def __call__(self, check_Location=''):
707 user = request.user
709 user = request.user
708 cls_name = self.__class__.__name__
710 cls_name = self.__class__.__name__
709 check_scope = {
711 check_scope = {
710 'HasPermissionAll': '',
712 'HasPermissionAll': '',
711 'HasPermissionAny': '',
713 'HasPermissionAny': '',
712 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
714 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
713 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
715 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
714 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
716 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
715 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
717 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
716 }.get(cls_name, '?')
718 }.get(cls_name, '?')
717 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
719 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
718 self.required_perms, user, check_scope,
720 self.required_perms, user, check_scope,
719 check_Location or 'unspecified location')
721 check_Location or 'unspecified location')
720 if not user:
722 if not user:
721 log.debug('Empty request user')
723 log.debug('Empty request user')
722 return False
724 return False
723 self.user_perms = user.permissions
725 self.user_perms = user.permissions
724 if self.check_permissions():
726 if self.check_permissions():
725 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
727 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
726 check_Location or 'unspecified location')
728 check_Location or 'unspecified location')
727 return True
729 return True
728
730
729 else:
731 else:
730 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
732 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
731 check_Location or 'unspecified location')
733 check_Location or 'unspecified location')
732 return False
734 return False
733
735
734 def check_permissions(self):
736 def check_permissions(self):
735 """Dummy function for overriding"""
737 """Dummy function for overriding"""
736 raise Exception('You have to write this function in child class')
738 raise Exception('You have to write this function in child class')
737
739
738
740
739 class HasPermissionAll(PermsFunction):
741 class HasPermissionAll(PermsFunction):
740 def check_permissions(self):
742 def check_permissions(self):
741 if self.required_perms.issubset(self.user_perms.get('global')):
743 if self.required_perms.issubset(self.user_perms.get('global')):
742 return True
744 return True
743 return False
745 return False
744
746
745
747
746 class HasPermissionAny(PermsFunction):
748 class HasPermissionAny(PermsFunction):
747 def check_permissions(self):
749 def check_permissions(self):
748 if self.required_perms.intersection(self.user_perms.get('global')):
750 if self.required_perms.intersection(self.user_perms.get('global')):
749 return True
751 return True
750 return False
752 return False
751
753
752
754
753 class HasRepoPermissionAll(PermsFunction):
755 class HasRepoPermissionAll(PermsFunction):
754 def __call__(self, repo_name=None, check_Location=''):
756 def __call__(self, repo_name=None, check_Location=''):
755 self.repo_name = repo_name
757 self.repo_name = repo_name
756 return super(HasRepoPermissionAll, self).__call__(check_Location)
758 return super(HasRepoPermissionAll, self).__call__(check_Location)
757
759
758 def check_permissions(self):
760 def check_permissions(self):
759 if not self.repo_name:
761 if not self.repo_name:
760 self.repo_name = get_repo_slug(request)
762 self.repo_name = get_repo_slug(request)
761
763
762 try:
764 try:
763 self._user_perms = set(
765 self._user_perms = set(
764 [self.user_perms['repositories'][self.repo_name]]
766 [self.user_perms['repositories'][self.repo_name]]
765 )
767 )
766 except KeyError:
768 except KeyError:
767 return False
769 return False
768 if self.required_perms.issubset(self._user_perms):
770 if self.required_perms.issubset(self._user_perms):
769 return True
771 return True
770 return False
772 return False
771
773
772
774
773 class HasRepoPermissionAny(PermsFunction):
775 class HasRepoPermissionAny(PermsFunction):
774 def __call__(self, repo_name=None, check_Location=''):
776 def __call__(self, repo_name=None, check_Location=''):
775 self.repo_name = repo_name
777 self.repo_name = repo_name
776 return super(HasRepoPermissionAny, self).__call__(check_Location)
778 return super(HasRepoPermissionAny, self).__call__(check_Location)
777
779
778 def check_permissions(self):
780 def check_permissions(self):
779 if not self.repo_name:
781 if not self.repo_name:
780 self.repo_name = get_repo_slug(request)
782 self.repo_name = get_repo_slug(request)
781
783
782 try:
784 try:
783 self._user_perms = set(
785 self._user_perms = set(
784 [self.user_perms['repositories'][self.repo_name]]
786 [self.user_perms['repositories'][self.repo_name]]
785 )
787 )
786 except KeyError:
788 except KeyError:
787 return False
789 return False
788 if self.required_perms.intersection(self._user_perms):
790 if self.required_perms.intersection(self._user_perms):
789 return True
791 return True
790 return False
792 return False
791
793
792
794
793 class HasReposGroupPermissionAny(PermsFunction):
795 class HasReposGroupPermissionAny(PermsFunction):
794 def __call__(self, group_name=None, check_Location=''):
796 def __call__(self, group_name=None, check_Location=''):
795 self.group_name = group_name
797 self.group_name = group_name
796 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
798 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
797
799
798 def check_permissions(self):
800 def check_permissions(self):
799 try:
801 try:
800 self._user_perms = set(
802 self._user_perms = set(
801 [self.user_perms['repositories_groups'][self.group_name]]
803 [self.user_perms['repositories_groups'][self.group_name]]
802 )
804 )
803 except KeyError:
805 except KeyError:
804 return False
806 return False
805 if self.required_perms.intersection(self._user_perms):
807 if self.required_perms.intersection(self._user_perms):
806 return True
808 return True
807 return False
809 return False
808
810
809
811
810 class HasReposGroupPermissionAll(PermsFunction):
812 class HasReposGroupPermissionAll(PermsFunction):
811 def __call__(self, group_name=None, check_Location=''):
813 def __call__(self, group_name=None, check_Location=''):
812 self.group_name = group_name
814 self.group_name = group_name
813 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
815 return super(HasReposGroupPermissionAll, self).__call__(check_Location)
814
816
815 def check_permissions(self):
817 def check_permissions(self):
816 try:
818 try:
817 self._user_perms = set(
819 self._user_perms = set(
818 [self.user_perms['repositories_groups'][self.group_name]]
820 [self.user_perms['repositories_groups'][self.group_name]]
819 )
821 )
820 except KeyError:
822 except KeyError:
821 return False
823 return False
822 if self.required_perms.issubset(self._user_perms):
824 if self.required_perms.issubset(self._user_perms):
823 return True
825 return True
824 return False
826 return False
825
827
826
828
827 #==============================================================================
829 #==============================================================================
828 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
830 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
829 #==============================================================================
831 #==============================================================================
830 class HasPermissionAnyMiddleware(object):
832 class HasPermissionAnyMiddleware(object):
831 def __init__(self, *perms):
833 def __init__(self, *perms):
832 self.required_perms = set(perms)
834 self.required_perms = set(perms)
833
835
834 def __call__(self, user, repo_name):
836 def __call__(self, user, repo_name):
835 # repo_name MUST be unicode, since we handle keys in permission
837 # repo_name MUST be unicode, since we handle keys in permission
836 # dict by unicode
838 # dict by unicode
837 repo_name = safe_unicode(repo_name)
839 repo_name = safe_unicode(repo_name)
838 usr = AuthUser(user.user_id)
840 usr = AuthUser(user.user_id)
839 try:
841 try:
840 self.user_perms = set([usr.permissions['repositories'][repo_name]])
842 self.user_perms = set([usr.permissions['repositories'][repo_name]])
841 except Exception:
843 except Exception:
842 log.error('Exception while accessing permissions %s' %
844 log.error('Exception while accessing permissions %s' %
843 traceback.format_exc())
845 traceback.format_exc())
844 self.user_perms = set()
846 self.user_perms = set()
845 self.username = user.username
847 self.username = user.username
846 self.repo_name = repo_name
848 self.repo_name = repo_name
847 return self.check_permissions()
849 return self.check_permissions()
848
850
849 def check_permissions(self):
851 def check_permissions(self):
850 log.debug('checking VCS protocol '
852 log.debug('checking VCS protocol '
851 'permissions %s for user:%s repository:%s', self.user_perms,
853 'permissions %s for user:%s repository:%s', self.user_perms,
852 self.username, self.repo_name)
854 self.username, self.repo_name)
853 if self.required_perms.intersection(self.user_perms):
855 if self.required_perms.intersection(self.user_perms):
854 log.debug('permission granted for user:%s on repo:%s' % (
856 log.debug('permission granted for user:%s on repo:%s' % (
855 self.username, self.repo_name
857 self.username, self.repo_name
856 )
858 )
857 )
859 )
858 return True
860 return True
859 log.debug('permission denied for user:%s on repo:%s' % (
861 log.debug('permission denied for user:%s on repo:%s' % (
860 self.username, self.repo_name
862 self.username, self.repo_name
861 )
863 )
862 )
864 )
863 return False
865 return False
864
866
865
867
866 #==============================================================================
868 #==============================================================================
867 # SPECIAL VERSION TO HANDLE API AUTH
869 # SPECIAL VERSION TO HANDLE API AUTH
868 #==============================================================================
870 #==============================================================================
869 class _BaseApiPerm(object):
871 class _BaseApiPerm(object):
870 def __init__(self, *perms):
872 def __init__(self, *perms):
871 self.required_perms = set(perms)
873 self.required_perms = set(perms)
872
874
873 def __call__(self, check_location='unspecified', user=None, repo_name=None):
875 def __call__(self, check_location='unspecified', user=None, repo_name=None):
874 cls_name = self.__class__.__name__
876 cls_name = self.__class__.__name__
875 check_scope = 'user:%s, repo:%s' % (user, repo_name)
877 check_scope = 'user:%s, repo:%s' % (user, repo_name)
876 log.debug('checking cls:%s %s %s @ %s', cls_name,
878 log.debug('checking cls:%s %s %s @ %s', cls_name,
877 self.required_perms, check_scope, check_location)
879 self.required_perms, check_scope, check_location)
878 if not user:
880 if not user:
879 log.debug('Empty User passed into arguments')
881 log.debug('Empty User passed into arguments')
880 return False
882 return False
881
883
882 ## process user
884 ## process user
883 if not isinstance(user, AuthUser):
885 if not isinstance(user, AuthUser):
884 user = AuthUser(user.user_id)
886 user = AuthUser(user.user_id)
885
887
886 if self.check_permissions(user.permissions, repo_name):
888 if self.check_permissions(user.permissions, repo_name):
887 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
889 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
888 user, check_location)
890 user, check_location)
889 return True
891 return True
890
892
891 else:
893 else:
892 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
894 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
893 user, check_location)
895 user, check_location)
894 return False
896 return False
895
897
896 def check_permissions(self, perm_defs, repo_name):
898 def check_permissions(self, perm_defs, repo_name):
897 """
899 """
898 implement in child class should return True if permissions are ok,
900 implement in child class should return True if permissions are ok,
899 False otherwise
901 False otherwise
900
902
901 :param perm_defs: dict with permission definitions
903 :param perm_defs: dict with permission definitions
902 :param repo_name: repo name
904 :param repo_name: repo name
903 """
905 """
904 raise NotImplementedError()
906 raise NotImplementedError()
905
907
906
908
907 class HasPermissionAllApi(_BaseApiPerm):
909 class HasPermissionAllApi(_BaseApiPerm):
908 def __call__(self, user, check_location=''):
910 def __call__(self, user, check_location=''):
909 return super(HasPermissionAllApi, self)\
911 return super(HasPermissionAllApi, self)\
910 .__call__(check_location=check_location, user=user)
912 .__call__(check_location=check_location, user=user)
911
913
912 def check_permissions(self, perm_defs, repo):
914 def check_permissions(self, perm_defs, repo):
913 if self.required_perms.issubset(perm_defs.get('global')):
915 if self.required_perms.issubset(perm_defs.get('global')):
914 return True
916 return True
915 return False
917 return False
916
918
917
919
918 class HasPermissionAnyApi(_BaseApiPerm):
920 class HasPermissionAnyApi(_BaseApiPerm):
919 def __call__(self, user, check_location=''):
921 def __call__(self, user, check_location=''):
920 return super(HasPermissionAnyApi, self)\
922 return super(HasPermissionAnyApi, self)\
921 .__call__(check_location=check_location, user=user)
923 .__call__(check_location=check_location, user=user)
922
924
923 def check_permissions(self, perm_defs, repo):
925 def check_permissions(self, perm_defs, repo):
924 if self.required_perms.intersection(perm_defs.get('global')):
926 if self.required_perms.intersection(perm_defs.get('global')):
925 return True
927 return True
926 return False
928 return False
927
929
928
930
929 class HasRepoPermissionAllApi(_BaseApiPerm):
931 class HasRepoPermissionAllApi(_BaseApiPerm):
930 def __call__(self, user, repo_name, check_location=''):
932 def __call__(self, user, repo_name, check_location=''):
931 return super(HasRepoPermissionAllApi, self)\
933 return super(HasRepoPermissionAllApi, self)\
932 .__call__(check_location=check_location, user=user,
934 .__call__(check_location=check_location, user=user,
933 repo_name=repo_name)
935 repo_name=repo_name)
934
936
935 def check_permissions(self, perm_defs, repo_name):
937 def check_permissions(self, perm_defs, repo_name):
936
938
937 try:
939 try:
938 self._user_perms = set(
940 self._user_perms = set(
939 [perm_defs['repositories'][repo_name]]
941 [perm_defs['repositories'][repo_name]]
940 )
942 )
941 except KeyError:
943 except KeyError:
942 log.warning(traceback.format_exc())
944 log.warning(traceback.format_exc())
943 return False
945 return False
944 if self.required_perms.issubset(self._user_perms):
946 if self.required_perms.issubset(self._user_perms):
945 return True
947 return True
946 return False
948 return False
947
949
948
950
949 class HasRepoPermissionAnyApi(_BaseApiPerm):
951 class HasRepoPermissionAnyApi(_BaseApiPerm):
950 def __call__(self, user, repo_name, check_location=''):
952 def __call__(self, user, repo_name, check_location=''):
951 return super(HasRepoPermissionAnyApi, self)\
953 return super(HasRepoPermissionAnyApi, self)\
952 .__call__(check_location=check_location, user=user,
954 .__call__(check_location=check_location, user=user,
953 repo_name=repo_name)
955 repo_name=repo_name)
954
956
955 def check_permissions(self, perm_defs, repo_name):
957 def check_permissions(self, perm_defs, repo_name):
956
958
957 try:
959 try:
958 _user_perms = set(
960 _user_perms = set(
959 [perm_defs['repositories'][repo_name]]
961 [perm_defs['repositories'][repo_name]]
960 )
962 )
961 except KeyError:
963 except KeyError:
962 log.warning(traceback.format_exc())
964 log.warning(traceback.format_exc())
963 return False
965 return False
964 if self.required_perms.intersection(_user_perms):
966 if self.required_perms.intersection(_user_perms):
965 return True
967 return True
966 return False
968 return False
967
969
968
970
969 def check_ip_access(source_ip, allowed_ips=None):
971 def check_ip_access(source_ip, allowed_ips=None):
970 """
972 """
971 Checks if source_ip is a subnet of any of allowed_ips.
973 Checks if source_ip is a subnet of any of allowed_ips.
972
974
973 :param source_ip:
975 :param source_ip:
974 :param allowed_ips: list of allowed ips together with mask
976 :param allowed_ips: list of allowed ips together with mask
975 """
977 """
976 from rhodecode.lib import ipaddr
978 from rhodecode.lib import ipaddr
977 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
979 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
978 if isinstance(allowed_ips, (tuple, list, set)):
980 if isinstance(allowed_ips, (tuple, list, set)):
979 for ip in allowed_ips:
981 for ip in allowed_ips:
980 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
982 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
981 return True
983 return True
982 return False
984 return False
General Comments 0
You need to be logged in to leave comments. Login now