##// END OF EJS Templates
added more info into __repr__ of auth user for better debugging and logging
marcink -
r3903:ddd05df2 beta
parent child Browse files
Show More
@@ -1,1113 +1,1113 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|%s')>" % (self.user_id, self.username,
448 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
449 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 except Exception:
504 except Exception:
505 pass
505 pass
506 finally:
506 finally:
507 meta.Session.remove()
507 meta.Session.remove()
508
508
509 config['available_permissions'] = [x.permission_name for x in all_perms]
509 config['available_permissions'] = [x.permission_name for x in all_perms]
510
510
511
511
512 #==============================================================================
512 #==============================================================================
513 # CHECK DECORATORS
513 # CHECK DECORATORS
514 #==============================================================================
514 #==============================================================================
515 class LoginRequired(object):
515 class LoginRequired(object):
516 """
516 """
517 Must be logged in to execute this function else
517 Must be logged in to execute this function else
518 redirect to login page
518 redirect to login page
519
519
520 :param api_access: if enabled this checks only for valid auth token
520 :param api_access: if enabled this checks only for valid auth token
521 and grants access based on valid token
521 and grants access based on valid token
522 """
522 """
523
523
524 def __init__(self, api_access=False):
524 def __init__(self, api_access=False):
525 self.api_access = api_access
525 self.api_access = api_access
526
526
527 def __call__(self, func):
527 def __call__(self, func):
528 return decorator(self.__wrapper, func)
528 return decorator(self.__wrapper, func)
529
529
530 def __wrapper(self, func, *fargs, **fkwargs):
530 def __wrapper(self, func, *fargs, **fkwargs):
531 cls = fargs[0]
531 cls = fargs[0]
532 user = cls.rhodecode_user
532 user = cls.rhodecode_user
533 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
533 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
534 # defined whitelist of controllers which API access will be enabled
534 # defined whitelist of controllers which API access will be enabled
535 whitelist = aslist(config.get('api_access_controllers_whitelist'),
535 whitelist = aslist(config.get('api_access_controllers_whitelist'),
536 sep=',')
536 sep=',')
537 api_access_whitelist = loc in whitelist
537 api_access_whitelist = loc in whitelist
538 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
538 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
539 api_access_whitelist))
539 api_access_whitelist))
540 #check IP
540 #check IP
541 ip_access_ok = True
541 ip_access_ok = True
542 if not user.ip_allowed:
542 if not user.ip_allowed:
543 from rhodecode.lib import helpers as h
543 from rhodecode.lib import helpers as h
544 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
544 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
545 category='warning')
545 category='warning')
546 ip_access_ok = False
546 ip_access_ok = False
547
547
548 api_access_ok = False
548 api_access_ok = False
549 if self.api_access or api_access_whitelist:
549 if self.api_access or api_access_whitelist:
550 log.debug('Checking API KEY access for %s' % cls)
550 log.debug('Checking API KEY access for %s' % cls)
551 if user.api_key == request.GET.get('api_key'):
551 if user.api_key == request.GET.get('api_key'):
552 api_access_ok = True
552 api_access_ok = True
553 else:
553 else:
554 log.debug("API KEY token not valid")
554 log.debug("API KEY token not valid")
555
555
556 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
556 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
557 if (user.is_authenticated or api_access_ok) and ip_access_ok:
557 if (user.is_authenticated or api_access_ok) and ip_access_ok:
558 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
558 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
559 log.info('user %s is authenticated and granted access to %s '
559 log.info('user %s is authenticated and granted access to %s '
560 'using %s' % (user.username, loc, reason)
560 'using %s' % (user.username, loc, reason)
561 )
561 )
562 return func(*fargs, **fkwargs)
562 return func(*fargs, **fkwargs)
563 else:
563 else:
564 log.warn('user %s NOT authenticated on func: %s' % (
564 log.warn('user %s NOT authenticated on func: %s' % (
565 user, loc)
565 user, loc)
566 )
566 )
567 p = url.current()
567 p = url.current()
568
568
569 log.debug('redirecting to login page with %s' % p)
569 log.debug('redirecting to login page with %s' % p)
570 return redirect(url('login_home', came_from=p))
570 return redirect(url('login_home', came_from=p))
571
571
572
572
573 class NotAnonymous(object):
573 class NotAnonymous(object):
574 """
574 """
575 Must be logged in to execute this function else
575 Must be logged in to execute this function else
576 redirect to login page"""
576 redirect to login page"""
577
577
578 def __call__(self, func):
578 def __call__(self, func):
579 return decorator(self.__wrapper, func)
579 return decorator(self.__wrapper, func)
580
580
581 def __wrapper(self, func, *fargs, **fkwargs):
581 def __wrapper(self, func, *fargs, **fkwargs):
582 cls = fargs[0]
582 cls = fargs[0]
583 self.user = cls.rhodecode_user
583 self.user = cls.rhodecode_user
584
584
585 log.debug('Checking if user is not anonymous @%s' % cls)
585 log.debug('Checking if user is not anonymous @%s' % cls)
586
586
587 anonymous = self.user.username == 'default'
587 anonymous = self.user.username == 'default'
588
588
589 if anonymous:
589 if anonymous:
590 p = url.current()
590 p = url.current()
591
591
592 import rhodecode.lib.helpers as h
592 import rhodecode.lib.helpers as h
593 h.flash(_('You need to be a registered user to '
593 h.flash(_('You need to be a registered user to '
594 'perform this action'),
594 'perform this action'),
595 category='warning')
595 category='warning')
596 return redirect(url('login_home', came_from=p))
596 return redirect(url('login_home', came_from=p))
597 else:
597 else:
598 return func(*fargs, **fkwargs)
598 return func(*fargs, **fkwargs)
599
599
600
600
601 class PermsDecorator(object):
601 class PermsDecorator(object):
602 """Base class for controller decorators"""
602 """Base class for controller decorators"""
603
603
604 def __init__(self, *required_perms):
604 def __init__(self, *required_perms):
605 available_perms = config['available_permissions']
605 available_perms = config['available_permissions']
606 for perm in required_perms:
606 for perm in required_perms:
607 if perm not in available_perms:
607 if perm not in available_perms:
608 raise Exception("'%s' permission is not defined" % perm)
608 raise Exception("'%s' permission is not defined" % perm)
609 self.required_perms = set(required_perms)
609 self.required_perms = set(required_perms)
610 self.user_perms = None
610 self.user_perms = None
611
611
612 def __call__(self, func):
612 def __call__(self, func):
613 return decorator(self.__wrapper, func)
613 return decorator(self.__wrapper, func)
614
614
615 def __wrapper(self, func, *fargs, **fkwargs):
615 def __wrapper(self, func, *fargs, **fkwargs):
616 cls = fargs[0]
616 cls = fargs[0]
617 self.user = cls.rhodecode_user
617 self.user = cls.rhodecode_user
618 self.user_perms = self.user.permissions
618 self.user_perms = self.user.permissions
619 log.debug('checking %s permissions %s for %s %s',
619 log.debug('checking %s permissions %s for %s %s',
620 self.__class__.__name__, self.required_perms, cls, self.user)
620 self.__class__.__name__, self.required_perms, cls, self.user)
621
621
622 if self.check_permissions():
622 if self.check_permissions():
623 log.debug('Permission granted for %s %s' % (cls, self.user))
623 log.debug('Permission granted for %s %s' % (cls, self.user))
624 return func(*fargs, **fkwargs)
624 return func(*fargs, **fkwargs)
625
625
626 else:
626 else:
627 log.debug('Permission denied for %s %s' % (cls, self.user))
627 log.debug('Permission denied for %s %s' % (cls, self.user))
628 anonymous = self.user.username == 'default'
628 anonymous = self.user.username == 'default'
629
629
630 if anonymous:
630 if anonymous:
631 p = url.current()
631 p = url.current()
632
632
633 import rhodecode.lib.helpers as h
633 import rhodecode.lib.helpers as h
634 h.flash(_('You need to be a signed in to '
634 h.flash(_('You need to be a signed in to '
635 'view this page'),
635 'view this page'),
636 category='warning')
636 category='warning')
637 return redirect(url('login_home', came_from=p))
637 return redirect(url('login_home', came_from=p))
638
638
639 else:
639 else:
640 # redirect with forbidden ret code
640 # redirect with forbidden ret code
641 return abort(403)
641 return abort(403)
642
642
643 def check_permissions(self):
643 def check_permissions(self):
644 """Dummy function for overriding"""
644 """Dummy function for overriding"""
645 raise Exception('You have to write this function in child class')
645 raise Exception('You have to write this function in child class')
646
646
647
647
648 class HasPermissionAllDecorator(PermsDecorator):
648 class HasPermissionAllDecorator(PermsDecorator):
649 """
649 """
650 Checks for access permission for all given predicates. All of them
650 Checks for access permission for all given predicates. All of them
651 have to be meet in order to fulfill the request
651 have to be meet in order to fulfill the request
652 """
652 """
653
653
654 def check_permissions(self):
654 def check_permissions(self):
655 if self.required_perms.issubset(self.user_perms.get('global')):
655 if self.required_perms.issubset(self.user_perms.get('global')):
656 return True
656 return True
657 return False
657 return False
658
658
659
659
660 class HasPermissionAnyDecorator(PermsDecorator):
660 class HasPermissionAnyDecorator(PermsDecorator):
661 """
661 """
662 Checks for access permission for any of given predicates. In order to
662 Checks for access permission for any of given predicates. In order to
663 fulfill the request any of predicates must be meet
663 fulfill the request any of predicates must be meet
664 """
664 """
665
665
666 def check_permissions(self):
666 def check_permissions(self):
667 if self.required_perms.intersection(self.user_perms.get('global')):
667 if self.required_perms.intersection(self.user_perms.get('global')):
668 return True
668 return True
669 return False
669 return False
670
670
671
671
672 class HasRepoPermissionAllDecorator(PermsDecorator):
672 class HasRepoPermissionAllDecorator(PermsDecorator):
673 """
673 """
674 Checks for access permission for all given predicates for specific
674 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
675 repository. All of them have to be meet in order to fulfill the request
676 """
676 """
677
677
678 def check_permissions(self):
678 def check_permissions(self):
679 repo_name = get_repo_slug(request)
679 repo_name = get_repo_slug(request)
680 try:
680 try:
681 user_perms = set([self.user_perms['repositories'][repo_name]])
681 user_perms = set([self.user_perms['repositories'][repo_name]])
682 except KeyError:
682 except KeyError:
683 return False
683 return False
684 if self.required_perms.issubset(user_perms):
684 if self.required_perms.issubset(user_perms):
685 return True
685 return True
686 return False
686 return False
687
687
688
688
689 class HasRepoPermissionAnyDecorator(PermsDecorator):
689 class HasRepoPermissionAnyDecorator(PermsDecorator):
690 """
690 """
691 Checks for access permission for any of given predicates for specific
691 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
692 repository. In order to fulfill the request any of predicates must be meet
693 """
693 """
694
694
695 def check_permissions(self):
695 def check_permissions(self):
696 repo_name = get_repo_slug(request)
696 repo_name = get_repo_slug(request)
697 try:
697 try:
698 user_perms = set([self.user_perms['repositories'][repo_name]])
698 user_perms = set([self.user_perms['repositories'][repo_name]])
699 except KeyError:
699 except KeyError:
700 return False
700 return False
701
701
702 if self.required_perms.intersection(user_perms):
702 if self.required_perms.intersection(user_perms):
703 return True
703 return True
704 return False
704 return False
705
705
706
706
707 class HasReposGroupPermissionAllDecorator(PermsDecorator):
707 class HasReposGroupPermissionAllDecorator(PermsDecorator):
708 """
708 """
709 Checks for access permission for all given predicates for specific
709 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
710 repository group. All of them have to be meet in order to fulfill the request
711 """
711 """
712
712
713 def check_permissions(self):
713 def check_permissions(self):
714 group_name = get_repos_group_slug(request)
714 group_name = get_repos_group_slug(request)
715 try:
715 try:
716 user_perms = set([self.user_perms['repositories_groups'][group_name]])
716 user_perms = set([self.user_perms['repositories_groups'][group_name]])
717 except KeyError:
717 except KeyError:
718 return False
718 return False
719
719
720 if self.required_perms.issubset(user_perms):
720 if self.required_perms.issubset(user_perms):
721 return True
721 return True
722 return False
722 return False
723
723
724
724
725 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
725 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
726 """
726 """
727 Checks for access permission for any of given predicates for specific
727 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
728 repository group. In order to fulfill the request any of predicates must be meet
729 """
729 """
730
730
731 def check_permissions(self):
731 def check_permissions(self):
732 group_name = get_repos_group_slug(request)
732 group_name = get_repos_group_slug(request)
733 try:
733 try:
734 user_perms = set([self.user_perms['repositories_groups'][group_name]])
734 user_perms = set([self.user_perms['repositories_groups'][group_name]])
735 except KeyError:
735 except KeyError:
736 return False
736 return False
737
737
738 if self.required_perms.intersection(user_perms):
738 if self.required_perms.intersection(user_perms):
739 return True
739 return True
740 return False
740 return False
741
741
742
742
743 class HasUserGroupPermissionAllDecorator(PermsDecorator):
743 class HasUserGroupPermissionAllDecorator(PermsDecorator):
744 """
744 """
745 Checks for access permission for all given predicates for specific
745 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
746 user group. All of them have to be meet in order to fulfill the request
747 """
747 """
748
748
749 def check_permissions(self):
749 def check_permissions(self):
750 group_name = get_user_group_slug(request)
750 group_name = get_user_group_slug(request)
751 try:
751 try:
752 user_perms = set([self.user_perms['user_groups'][group_name]])
752 user_perms = set([self.user_perms['user_groups'][group_name]])
753 except KeyError:
753 except KeyError:
754 return False
754 return False
755
755
756 if self.required_perms.issubset(user_perms):
756 if self.required_perms.issubset(user_perms):
757 return True
757 return True
758 return False
758 return False
759
759
760
760
761 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
761 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
762 """
762 """
763 Checks for access permission for any of given predicates for specific
763 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
764 user group. In order to fulfill the request any of predicates must be meet
765 """
765 """
766
766
767 def check_permissions(self):
767 def check_permissions(self):
768 group_name = get_user_group_slug(request)
768 group_name = get_user_group_slug(request)
769 try:
769 try:
770 user_perms = set([self.user_perms['user_groups'][group_name]])
770 user_perms = set([self.user_perms['user_groups'][group_name]])
771 except KeyError:
771 except KeyError:
772 return False
772 return False
773
773
774 if self.required_perms.intersection(user_perms):
774 if self.required_perms.intersection(user_perms):
775 return True
775 return True
776 return False
776 return False
777
777
778
778
779 #==============================================================================
779 #==============================================================================
780 # CHECK FUNCTIONS
780 # CHECK FUNCTIONS
781 #==============================================================================
781 #==============================================================================
782 class PermsFunction(object):
782 class PermsFunction(object):
783 """Base function for other check functions"""
783 """Base function for other check functions"""
784
784
785 def __init__(self, *perms):
785 def __init__(self, *perms):
786 available_perms = config['available_permissions']
786 available_perms = config['available_permissions']
787
787
788 for perm in perms:
788 for perm in perms:
789 if perm not in available_perms:
789 if perm not in available_perms:
790 raise Exception("'%s' permission is not defined" % perm)
790 raise Exception("'%s' permission is not defined" % perm)
791 self.required_perms = set(perms)
791 self.required_perms = set(perms)
792 self.user_perms = None
792 self.user_perms = None
793 self.repo_name = None
793 self.repo_name = None
794 self.group_name = None
794 self.group_name = None
795
795
796 def __call__(self, check_location=''):
796 def __call__(self, check_location=''):
797 #TODO: put user as attribute here
797 #TODO: put user as attribute here
798 user = request.user
798 user = request.user
799 cls_name = self.__class__.__name__
799 cls_name = self.__class__.__name__
800 check_scope = {
800 check_scope = {
801 'HasPermissionAll': '',
801 'HasPermissionAll': '',
802 'HasPermissionAny': '',
802 'HasPermissionAny': '',
803 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
803 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
804 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
804 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
805 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
805 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
806 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
806 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
807 }.get(cls_name, '?')
807 }.get(cls_name, '?')
808 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
808 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
809 self.required_perms, user, check_scope,
809 self.required_perms, user, check_scope,
810 check_location or 'unspecified location')
810 check_location or 'unspecified location')
811 if not user:
811 if not user:
812 log.debug('Empty request user')
812 log.debug('Empty request user')
813 return False
813 return False
814 self.user_perms = user.permissions
814 self.user_perms = user.permissions
815 if self.check_permissions():
815 if self.check_permissions():
816 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
816 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
817 check_location or 'unspecified location')
817 check_location or 'unspecified location')
818 return True
818 return True
819
819
820 else:
820 else:
821 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
821 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
822 check_location or 'unspecified location')
822 check_location or 'unspecified location')
823 return False
823 return False
824
824
825 def check_permissions(self):
825 def check_permissions(self):
826 """Dummy function for overriding"""
826 """Dummy function for overriding"""
827 raise Exception('You have to write this function in child class')
827 raise Exception('You have to write this function in child class')
828
828
829
829
830 class HasPermissionAll(PermsFunction):
830 class HasPermissionAll(PermsFunction):
831 def check_permissions(self):
831 def check_permissions(self):
832 if self.required_perms.issubset(self.user_perms.get('global')):
832 if self.required_perms.issubset(self.user_perms.get('global')):
833 return True
833 return True
834 return False
834 return False
835
835
836
836
837 class HasPermissionAny(PermsFunction):
837 class HasPermissionAny(PermsFunction):
838 def check_permissions(self):
838 def check_permissions(self):
839 if self.required_perms.intersection(self.user_perms.get('global')):
839 if self.required_perms.intersection(self.user_perms.get('global')):
840 return True
840 return True
841 return False
841 return False
842
842
843
843
844 class HasRepoPermissionAll(PermsFunction):
844 class HasRepoPermissionAll(PermsFunction):
845 def __call__(self, repo_name=None, check_location=''):
845 def __call__(self, repo_name=None, check_location=''):
846 self.repo_name = repo_name
846 self.repo_name = repo_name
847 return super(HasRepoPermissionAll, self).__call__(check_location)
847 return super(HasRepoPermissionAll, self).__call__(check_location)
848
848
849 def check_permissions(self):
849 def check_permissions(self):
850 if not self.repo_name:
850 if not self.repo_name:
851 self.repo_name = get_repo_slug(request)
851 self.repo_name = get_repo_slug(request)
852
852
853 try:
853 try:
854 self._user_perms = set(
854 self._user_perms = set(
855 [self.user_perms['repositories'][self.repo_name]]
855 [self.user_perms['repositories'][self.repo_name]]
856 )
856 )
857 except KeyError:
857 except KeyError:
858 return False
858 return False
859 if self.required_perms.issubset(self._user_perms):
859 if self.required_perms.issubset(self._user_perms):
860 return True
860 return True
861 return False
861 return False
862
862
863
863
864 class HasRepoPermissionAny(PermsFunction):
864 class HasRepoPermissionAny(PermsFunction):
865 def __call__(self, repo_name=None, check_location=''):
865 def __call__(self, repo_name=None, check_location=''):
866 self.repo_name = repo_name
866 self.repo_name = repo_name
867 return super(HasRepoPermissionAny, self).__call__(check_location)
867 return super(HasRepoPermissionAny, self).__call__(check_location)
868
868
869 def check_permissions(self):
869 def check_permissions(self):
870 if not self.repo_name:
870 if not self.repo_name:
871 self.repo_name = get_repo_slug(request)
871 self.repo_name = get_repo_slug(request)
872
872
873 try:
873 try:
874 self._user_perms = set(
874 self._user_perms = set(
875 [self.user_perms['repositories'][self.repo_name]]
875 [self.user_perms['repositories'][self.repo_name]]
876 )
876 )
877 except KeyError:
877 except KeyError:
878 return False
878 return False
879 if self.required_perms.intersection(self._user_perms):
879 if self.required_perms.intersection(self._user_perms):
880 return True
880 return True
881 return False
881 return False
882
882
883
883
884 class HasReposGroupPermissionAny(PermsFunction):
884 class HasReposGroupPermissionAny(PermsFunction):
885 def __call__(self, group_name=None, check_location=''):
885 def __call__(self, group_name=None, check_location=''):
886 self.group_name = group_name
886 self.group_name = group_name
887 return super(HasReposGroupPermissionAny, self).__call__(check_location)
887 return super(HasReposGroupPermissionAny, self).__call__(check_location)
888
888
889 def check_permissions(self):
889 def check_permissions(self):
890 try:
890 try:
891 self._user_perms = set(
891 self._user_perms = set(
892 [self.user_perms['repositories_groups'][self.group_name]]
892 [self.user_perms['repositories_groups'][self.group_name]]
893 )
893 )
894 except KeyError:
894 except KeyError:
895 return False
895 return False
896 if self.required_perms.intersection(self._user_perms):
896 if self.required_perms.intersection(self._user_perms):
897 return True
897 return True
898 return False
898 return False
899
899
900
900
901 class HasReposGroupPermissionAll(PermsFunction):
901 class HasReposGroupPermissionAll(PermsFunction):
902 def __call__(self, group_name=None, check_location=''):
902 def __call__(self, group_name=None, check_location=''):
903 self.group_name = group_name
903 self.group_name = group_name
904 return super(HasReposGroupPermissionAll, self).__call__(check_location)
904 return super(HasReposGroupPermissionAll, self).__call__(check_location)
905
905
906 def check_permissions(self):
906 def check_permissions(self):
907 try:
907 try:
908 self._user_perms = set(
908 self._user_perms = set(
909 [self.user_perms['repositories_groups'][self.group_name]]
909 [self.user_perms['repositories_groups'][self.group_name]]
910 )
910 )
911 except KeyError:
911 except KeyError:
912 return False
912 return False
913 if self.required_perms.issubset(self._user_perms):
913 if self.required_perms.issubset(self._user_perms):
914 return True
914 return True
915 return False
915 return False
916
916
917
917
918 class HasUserGroupPermissionAny(PermsFunction):
918 class HasUserGroupPermissionAny(PermsFunction):
919 def __call__(self, user_group_name=None, check_location=''):
919 def __call__(self, user_group_name=None, check_location=''):
920 self.user_group_name = user_group_name
920 self.user_group_name = user_group_name
921 return super(HasUserGroupPermissionAny, self).__call__(check_location)
921 return super(HasUserGroupPermissionAny, self).__call__(check_location)
922
922
923 def check_permissions(self):
923 def check_permissions(self):
924 try:
924 try:
925 self._user_perms = set(
925 self._user_perms = set(
926 [self.user_perms['user_groups'][self.user_group_name]]
926 [self.user_perms['user_groups'][self.user_group_name]]
927 )
927 )
928 except KeyError:
928 except KeyError:
929 return False
929 return False
930 if self.required_perms.intersection(self._user_perms):
930 if self.required_perms.intersection(self._user_perms):
931 return True
931 return True
932 return False
932 return False
933
933
934
934
935 class HasUserGroupPermissionAll(PermsFunction):
935 class HasUserGroupPermissionAll(PermsFunction):
936 def __call__(self, user_group_name=None, check_location=''):
936 def __call__(self, user_group_name=None, check_location=''):
937 self.user_group_name = user_group_name
937 self.user_group_name = user_group_name
938 return super(HasUserGroupPermissionAll, self).__call__(check_location)
938 return super(HasUserGroupPermissionAll, self).__call__(check_location)
939
939
940 def check_permissions(self):
940 def check_permissions(self):
941 try:
941 try:
942 self._user_perms = set(
942 self._user_perms = set(
943 [self.user_perms['user_groups'][self.user_group_name]]
943 [self.user_perms['user_groups'][self.user_group_name]]
944 )
944 )
945 except KeyError:
945 except KeyError:
946 return False
946 return False
947 if self.required_perms.issubset(self._user_perms):
947 if self.required_perms.issubset(self._user_perms):
948 return True
948 return True
949 return False
949 return False
950
950
951 #==============================================================================
951 #==============================================================================
952 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
952 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
953 #==============================================================================
953 #==============================================================================
954 class HasPermissionAnyMiddleware(object):
954 class HasPermissionAnyMiddleware(object):
955 def __init__(self, *perms):
955 def __init__(self, *perms):
956 self.required_perms = set(perms)
956 self.required_perms = set(perms)
957
957
958 def __call__(self, user, repo_name):
958 def __call__(self, user, repo_name):
959 # repo_name MUST be unicode, since we handle keys in permission
959 # repo_name MUST be unicode, since we handle keys in permission
960 # dict by unicode
960 # dict by unicode
961 repo_name = safe_unicode(repo_name)
961 repo_name = safe_unicode(repo_name)
962 usr = AuthUser(user.user_id)
962 usr = AuthUser(user.user_id)
963 try:
963 try:
964 self.user_perms = set([usr.permissions['repositories'][repo_name]])
964 self.user_perms = set([usr.permissions['repositories'][repo_name]])
965 except Exception:
965 except Exception:
966 log.error('Exception while accessing permissions %s' %
966 log.error('Exception while accessing permissions %s' %
967 traceback.format_exc())
967 traceback.format_exc())
968 self.user_perms = set()
968 self.user_perms = set()
969 self.username = user.username
969 self.username = user.username
970 self.repo_name = repo_name
970 self.repo_name = repo_name
971 return self.check_permissions()
971 return self.check_permissions()
972
972
973 def check_permissions(self):
973 def check_permissions(self):
974 log.debug('checking VCS protocol '
974 log.debug('checking VCS protocol '
975 'permissions %s for user:%s repository:%s', self.user_perms,
975 'permissions %s for user:%s repository:%s', self.user_perms,
976 self.username, self.repo_name)
976 self.username, self.repo_name)
977 if self.required_perms.intersection(self.user_perms):
977 if self.required_perms.intersection(self.user_perms):
978 log.debug('permission granted for user:%s on repo:%s' % (
978 log.debug('permission granted for user:%s on repo:%s' % (
979 self.username, self.repo_name
979 self.username, self.repo_name
980 )
980 )
981 )
981 )
982 return True
982 return True
983 log.debug('permission denied for user:%s on repo:%s' % (
983 log.debug('permission denied for user:%s on repo:%s' % (
984 self.username, self.repo_name
984 self.username, self.repo_name
985 )
985 )
986 )
986 )
987 return False
987 return False
988
988
989
989
990 #==============================================================================
990 #==============================================================================
991 # SPECIAL VERSION TO HANDLE API AUTH
991 # SPECIAL VERSION TO HANDLE API AUTH
992 #==============================================================================
992 #==============================================================================
993 class _BaseApiPerm(object):
993 class _BaseApiPerm(object):
994 def __init__(self, *perms):
994 def __init__(self, *perms):
995 self.required_perms = set(perms)
995 self.required_perms = set(perms)
996
996
997 def __call__(self, check_location='unspecified', user=None, repo_name=None):
997 def __call__(self, check_location='unspecified', user=None, repo_name=None):
998 cls_name = self.__class__.__name__
998 cls_name = self.__class__.__name__
999 check_scope = 'user:%s, repo:%s' % (user, repo_name)
999 check_scope = 'user:%s, repo:%s' % (user, repo_name)
1000 log.debug('checking cls:%s %s %s @ %s', cls_name,
1000 log.debug('checking cls:%s %s %s @ %s', cls_name,
1001 self.required_perms, check_scope, check_location)
1001 self.required_perms, check_scope, check_location)
1002 if not user:
1002 if not user:
1003 log.debug('Empty User passed into arguments')
1003 log.debug('Empty User passed into arguments')
1004 return False
1004 return False
1005
1005
1006 ## process user
1006 ## process user
1007 if not isinstance(user, AuthUser):
1007 if not isinstance(user, AuthUser):
1008 user = AuthUser(user.user_id)
1008 user = AuthUser(user.user_id)
1009
1009
1010 if self.check_permissions(user.permissions, repo_name):
1010 if self.check_permissions(user.permissions, repo_name):
1011 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
1011 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
1012 user, check_location)
1012 user, check_location)
1013 return True
1013 return True
1014
1014
1015 else:
1015 else:
1016 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
1016 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
1017 user, check_location)
1017 user, check_location)
1018 return False
1018 return False
1019
1019
1020 def check_permissions(self, perm_defs, repo_name):
1020 def check_permissions(self, perm_defs, repo_name):
1021 """
1021 """
1022 implement in child class should return True if permissions are ok,
1022 implement in child class should return True if permissions are ok,
1023 False otherwise
1023 False otherwise
1024
1024
1025 :param perm_defs: dict with permission definitions
1025 :param perm_defs: dict with permission definitions
1026 :param repo_name: repo name
1026 :param repo_name: repo name
1027 """
1027 """
1028 raise NotImplementedError()
1028 raise NotImplementedError()
1029
1029
1030
1030
1031 class HasPermissionAllApi(_BaseApiPerm):
1031 class HasPermissionAllApi(_BaseApiPerm):
1032 def __call__(self, user, check_location=''):
1032 def __call__(self, user, check_location=''):
1033 return super(HasPermissionAllApi, self)\
1033 return super(HasPermissionAllApi, self)\
1034 .__call__(check_location=check_location, user=user)
1034 .__call__(check_location=check_location, user=user)
1035
1035
1036 def check_permissions(self, perm_defs, repo):
1036 def check_permissions(self, perm_defs, repo):
1037 if self.required_perms.issubset(perm_defs.get('global')):
1037 if self.required_perms.issubset(perm_defs.get('global')):
1038 return True
1038 return True
1039 return False
1039 return False
1040
1040
1041
1041
1042 class HasPermissionAnyApi(_BaseApiPerm):
1042 class HasPermissionAnyApi(_BaseApiPerm):
1043 def __call__(self, user, check_location=''):
1043 def __call__(self, user, check_location=''):
1044 return super(HasPermissionAnyApi, self)\
1044 return super(HasPermissionAnyApi, self)\
1045 .__call__(check_location=check_location, user=user)
1045 .__call__(check_location=check_location, user=user)
1046
1046
1047 def check_permissions(self, perm_defs, repo):
1047 def check_permissions(self, perm_defs, repo):
1048 if self.required_perms.intersection(perm_defs.get('global')):
1048 if self.required_perms.intersection(perm_defs.get('global')):
1049 return True
1049 return True
1050 return False
1050 return False
1051
1051
1052
1052
1053 class HasRepoPermissionAllApi(_BaseApiPerm):
1053 class HasRepoPermissionAllApi(_BaseApiPerm):
1054 def __call__(self, user, repo_name, check_location=''):
1054 def __call__(self, user, repo_name, check_location=''):
1055 return super(HasRepoPermissionAllApi, self)\
1055 return super(HasRepoPermissionAllApi, self)\
1056 .__call__(check_location=check_location, user=user,
1056 .__call__(check_location=check_location, user=user,
1057 repo_name=repo_name)
1057 repo_name=repo_name)
1058
1058
1059 def check_permissions(self, perm_defs, repo_name):
1059 def check_permissions(self, perm_defs, repo_name):
1060
1060
1061 try:
1061 try:
1062 self._user_perms = set(
1062 self._user_perms = set(
1063 [perm_defs['repositories'][repo_name]]
1063 [perm_defs['repositories'][repo_name]]
1064 )
1064 )
1065 except KeyError:
1065 except KeyError:
1066 log.warning(traceback.format_exc())
1066 log.warning(traceback.format_exc())
1067 return False
1067 return False
1068 if self.required_perms.issubset(self._user_perms):
1068 if self.required_perms.issubset(self._user_perms):
1069 return True
1069 return True
1070 return False
1070 return False
1071
1071
1072
1072
1073 class HasRepoPermissionAnyApi(_BaseApiPerm):
1073 class HasRepoPermissionAnyApi(_BaseApiPerm):
1074 def __call__(self, user, repo_name, check_location=''):
1074 def __call__(self, user, repo_name, check_location=''):
1075 return super(HasRepoPermissionAnyApi, self)\
1075 return super(HasRepoPermissionAnyApi, self)\
1076 .__call__(check_location=check_location, user=user,
1076 .__call__(check_location=check_location, user=user,
1077 repo_name=repo_name)
1077 repo_name=repo_name)
1078
1078
1079 def check_permissions(self, perm_defs, repo_name):
1079 def check_permissions(self, perm_defs, repo_name):
1080
1080
1081 try:
1081 try:
1082 _user_perms = set(
1082 _user_perms = set(
1083 [perm_defs['repositories'][repo_name]]
1083 [perm_defs['repositories'][repo_name]]
1084 )
1084 )
1085 except KeyError:
1085 except KeyError:
1086 log.warning(traceback.format_exc())
1086 log.warning(traceback.format_exc())
1087 return False
1087 return False
1088 if self.required_perms.intersection(_user_perms):
1088 if self.required_perms.intersection(_user_perms):
1089 return True
1089 return True
1090 return False
1090 return False
1091
1091
1092
1092
1093 def check_ip_access(source_ip, allowed_ips=None):
1093 def check_ip_access(source_ip, allowed_ips=None):
1094 """
1094 """
1095 Checks if source_ip is a subnet of any of allowed_ips.
1095 Checks if source_ip is a subnet of any of allowed_ips.
1096
1096
1097 :param source_ip:
1097 :param source_ip:
1098 :param allowed_ips: list of allowed ips together with mask
1098 :param allowed_ips: list of allowed ips together with mask
1099 """
1099 """
1100 from rhodecode.lib import ipaddr
1100 from rhodecode.lib import ipaddr
1101 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1101 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1102 if isinstance(allowed_ips, (tuple, list, set)):
1102 if isinstance(allowed_ips, (tuple, list, set)):
1103 for ip in allowed_ips:
1103 for ip in allowed_ips:
1104 try:
1104 try:
1105 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1105 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1106 return True
1106 return True
1107 # for any case we cannot determine the IP, don't crash just
1107 # 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
1108 # skip it and log as error, we want to say forbidden still when
1109 # sending bad IP
1109 # sending bad IP
1110 except Exception:
1110 except Exception:
1111 log.error(traceback.format_exc())
1111 log.error(traceback.format_exc())
1112 continue
1112 continue
1113 return False
1113 return False
General Comments 0
You need to be logged in to leave comments. Login now