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