##// END OF EJS Templates
Added session wrapper, for rc 1.2.X compatibility. Adds backwards compatability...
marcink -
r2030:61f9aeb2 beta
parent child Browse files
Show More
@@ -1,785 +1,806 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
37
38 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41 if __platform__ in PLATFORM_WIN:
41 if __platform__ in PLATFORM_WIN:
42 from hashlib import sha256
42 from hashlib import sha256
43 if __platform__ in PLATFORM_OTHERS:
43 if __platform__ in PLATFORM_OTHERS:
44 import bcrypt
44 import bcrypt
45
45
46 from rhodecode.lib import str2bool, safe_unicode
46 from rhodecode.lib import str2bool, safe_unicode
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
48 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
48 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
49 from rhodecode.lib.auth_ldap import AuthLdap
49 from rhodecode.lib.auth_ldap import AuthLdap
50
50
51 from rhodecode.model import meta
51 from rhodecode.model import meta
52 from rhodecode.model.user import UserModel
52 from rhodecode.model.user import UserModel
53 from rhodecode.model.db import Permission, RhodeCodeSetting, User
53 from rhodecode.model.db import Permission, RhodeCodeSetting, User
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class PasswordGenerator(object):
58 class PasswordGenerator(object):
59 """
59 """
60 This is a simple class for generating password from different sets of
60 This is a simple class for generating password from different sets of
61 characters
61 characters
62 usage::
62 usage::
63
63
64 passwd_gen = PasswordGenerator()
64 passwd_gen = PasswordGenerator()
65 #print 8-letter password containing only big and small letters
65 #print 8-letter password containing only big and small letters
66 of alphabet
66 of alphabet
67 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
67 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
68 """
68 """
69 ALPHABETS_NUM = r'''1234567890'''
69 ALPHABETS_NUM = r'''1234567890'''
70 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
70 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
71 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
71 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
72 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
72 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
73 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
73 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
74 + ALPHABETS_NUM + ALPHABETS_SPECIAL
74 + ALPHABETS_NUM + ALPHABETS_SPECIAL
75 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
75 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
76 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
76 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
77 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
77 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
78 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
78 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
79
79
80 def __init__(self, passwd=''):
80 def __init__(self, passwd=''):
81 self.passwd = passwd
81 self.passwd = passwd
82
82
83 def gen_password(self, length, type_=None):
83 def gen_password(self, length, type_=None):
84 if type_ is None:
84 if type_ is None:
85 type_ = self.ALPHABETS_FULL
85 type_ = self.ALPHABETS_FULL
86 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
86 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
87 return self.passwd
87 return self.passwd
88
88
89
89
90 class RhodeCodeCrypto(object):
90 class RhodeCodeCrypto(object):
91
91
92 @classmethod
92 @classmethod
93 def hash_string(cls, str_):
93 def hash_string(cls, str_):
94 """
94 """
95 Cryptographic function used for password hashing based on pybcrypt
95 Cryptographic function used for password hashing based on pybcrypt
96 or pycrypto in windows
96 or pycrypto in windows
97
97
98 :param password: password to hash
98 :param password: password to hash
99 """
99 """
100 if __platform__ in PLATFORM_WIN:
100 if __platform__ in PLATFORM_WIN:
101 return sha256(str_).hexdigest()
101 return sha256(str_).hexdigest()
102 elif __platform__ in PLATFORM_OTHERS:
102 elif __platform__ in PLATFORM_OTHERS:
103 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
103 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
104 else:
104 else:
105 raise Exception('Unknown or unsupported platform %s' \
105 raise Exception('Unknown or unsupported platform %s' \
106 % __platform__)
106 % __platform__)
107
107
108 @classmethod
108 @classmethod
109 def hash_check(cls, password, hashed):
109 def hash_check(cls, password, hashed):
110 """
110 """
111 Checks matching password with it's hashed value, runs different
111 Checks matching password with it's hashed value, runs different
112 implementation based on platform it runs on
112 implementation based on platform it runs on
113
113
114 :param password: password
114 :param password: password
115 :param hashed: password in hashed form
115 :param hashed: password in hashed form
116 """
116 """
117
117
118 if __platform__ in PLATFORM_WIN:
118 if __platform__ in PLATFORM_WIN:
119 return sha256(password).hexdigest() == hashed
119 return sha256(password).hexdigest() == hashed
120 elif __platform__ in PLATFORM_OTHERS:
120 elif __platform__ in PLATFORM_OTHERS:
121 return bcrypt.hashpw(password, hashed) == hashed
121 return bcrypt.hashpw(password, hashed) == hashed
122 else:
122 else:
123 raise Exception('Unknown or unsupported platform %s' \
123 raise Exception('Unknown or unsupported platform %s' \
124 % __platform__)
124 % __platform__)
125
125
126
126
127 def get_crypt_password(password):
127 def get_crypt_password(password):
128 return RhodeCodeCrypto.hash_string(password)
128 return RhodeCodeCrypto.hash_string(password)
129
129
130
130
131 def check_password(password, hashed):
131 def check_password(password, hashed):
132 return RhodeCodeCrypto.hash_check(password, hashed)
132 return RhodeCodeCrypto.hash_check(password, hashed)
133
133
134
134
135 def generate_api_key(str_, salt=None):
135 def generate_api_key(str_, salt=None):
136 """
136 """
137 Generates API KEY from given string
137 Generates API KEY from given string
138
138
139 :param str_:
139 :param str_:
140 :param salt:
140 :param salt:
141 """
141 """
142
142
143 if salt is None:
143 if salt is None:
144 salt = _RandomNameSequence().next()
144 salt = _RandomNameSequence().next()
145
145
146 return hashlib.sha1(str_ + salt).hexdigest()
146 return hashlib.sha1(str_ + salt).hexdigest()
147
147
148
148
149 def authfunc(environ, username, password):
149 def authfunc(environ, username, password):
150 """
150 """
151 Dummy authentication wrapper function used in Mercurial and Git for
151 Dummy authentication wrapper function used in Mercurial and Git for
152 access control.
152 access control.
153
153
154 :param environ: needed only for using in Basic auth
154 :param environ: needed only for using in Basic auth
155 """
155 """
156 return authenticate(username, password)
156 return authenticate(username, password)
157
157
158
158
159 def authenticate(username, password):
159 def authenticate(username, password):
160 """
160 """
161 Authentication function used for access control,
161 Authentication function used for access control,
162 firstly checks for db authentication then if ldap is enabled for ldap
162 firstly checks for db authentication then if ldap is enabled for ldap
163 authentication, also creates ldap user if not in database
163 authentication, also creates ldap user if not in database
164
164
165 :param username: username
165 :param username: username
166 :param password: password
166 :param password: password
167 """
167 """
168
168
169 user_model = UserModel()
169 user_model = UserModel()
170 user = User.get_by_username(username)
170 user = User.get_by_username(username)
171
171
172 log.debug('Authenticating user using RhodeCode account')
172 log.debug('Authenticating user using RhodeCode account')
173 if user is not None and not user.ldap_dn:
173 if user is not None and not user.ldap_dn:
174 if user.active:
174 if user.active:
175 if user.username == 'default' and user.active:
175 if user.username == 'default' and user.active:
176 log.info('user %s authenticated correctly as anonymous user' %
176 log.info('user %s authenticated correctly as anonymous user' %
177 username)
177 username)
178 return True
178 return True
179
179
180 elif user.username == username and check_password(password,
180 elif user.username == username and check_password(password,
181 user.password):
181 user.password):
182 log.info('user %s authenticated correctly' % username)
182 log.info('user %s authenticated correctly' % username)
183 return True
183 return True
184 else:
184 else:
185 log.warning('user %s tried auth but is disabled' % username)
185 log.warning('user %s tried auth but is disabled' % username)
186
186
187 else:
187 else:
188 log.debug('Regular authentication failed')
188 log.debug('Regular authentication failed')
189 user_obj = User.get_by_username(username, case_insensitive=True)
189 user_obj = User.get_by_username(username, case_insensitive=True)
190
190
191 if user_obj is not None and not user_obj.ldap_dn:
191 if user_obj is not None and not user_obj.ldap_dn:
192 log.debug('this user already exists as non ldap')
192 log.debug('this user already exists as non ldap')
193 return False
193 return False
194
194
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 #======================================================================
196 #======================================================================
197 # FALLBACK TO LDAP AUTH IF ENABLE
197 # FALLBACK TO LDAP AUTH IF ENABLE
198 #======================================================================
198 #======================================================================
199 if str2bool(ldap_settings.get('ldap_active')):
199 if str2bool(ldap_settings.get('ldap_active')):
200 log.debug("Authenticating user using ldap")
200 log.debug("Authenticating user using ldap")
201 kwargs = {
201 kwargs = {
202 'server': ldap_settings.get('ldap_host', ''),
202 'server': ldap_settings.get('ldap_host', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 'port': ldap_settings.get('ldap_port'),
204 'port': ldap_settings.get('ldap_port'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
212 'ldap_version': 3,
212 'ldap_version': 3,
213 }
213 }
214 log.debug('Checking for ldap authentication')
214 log.debug('Checking for ldap authentication')
215 try:
215 try:
216 aldap = AuthLdap(**kwargs)
216 aldap = AuthLdap(**kwargs)
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 password)
218 password)
219 log.debug('Got ldap DN response %s' % user_dn)
219 log.debug('Got ldap DN response %s' % user_dn)
220
220
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 .get(k), [''])[0]
222 .get(k), [''])[0]
223
223
224 user_attrs = {
224 user_attrs = {
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 'email': get_ldap_attr('ldap_attr_email'),
227 'email': get_ldap_attr('ldap_attr_email'),
228 }
228 }
229
229
230 # don't store LDAP password since we don't need it. Override
230 # don't store LDAP password since we don't need it. Override
231 # with some random generated password
231 # with some random generated password
232 _password = PasswordGenerator().gen_password(length=8)
232 _password = PasswordGenerator().gen_password(length=8)
233 # create this user on the fly if it doesn't exist in rhodecode
233 # create this user on the fly if it doesn't exist in rhodecode
234 # database
234 # database
235 if user_model.create_ldap(username, _password, user_dn,
235 if user_model.create_ldap(username, _password, user_dn,
236 user_attrs):
236 user_attrs):
237 log.info('created new ldap user %s' % username)
237 log.info('created new ldap user %s' % username)
238
238
239 Session.commit()
239 Session.commit()
240 return True
240 return True
241 except (LdapUsernameError, LdapPasswordError,):
241 except (LdapUsernameError, LdapPasswordError,):
242 pass
242 pass
243 except (Exception,):
243 except (Exception,):
244 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
245 pass
245 pass
246 return False
246 return False
247
247
248
248
249 def login_container_auth(username):
249 def login_container_auth(username):
250 user = User.get_by_username(username)
250 user = User.get_by_username(username)
251 if user is None:
251 if user is None:
252 user_attrs = {
252 user_attrs = {
253 'name': username,
253 'name': username,
254 'lastname': None,
254 'lastname': None,
255 'email': None,
255 'email': None,
256 }
256 }
257 user = UserModel().create_for_container_auth(username, user_attrs)
257 user = UserModel().create_for_container_auth(username, user_attrs)
258 if not user:
258 if not user:
259 return None
259 return None
260 log.info('User %s was created by container authentication' % username)
260 log.info('User %s was created by container authentication' % username)
261
261
262 if not user.active:
262 if not user.active:
263 return None
263 return None
264
264
265 user.update_lastlogin()
265 user.update_lastlogin()
266 Session.commit()
266 Session.commit()
267
267
268 log.debug('User %s is now logged in by container authentication',
268 log.debug('User %s is now logged in by container authentication',
269 user.username)
269 user.username)
270 return user
270 return user
271
271
272
272
273 def get_container_username(environ, config):
273 def get_container_username(environ, config):
274 username = None
274 username = None
275
275
276 if str2bool(config.get('container_auth_enabled', False)):
276 if str2bool(config.get('container_auth_enabled', False)):
277 from paste.httpheaders import REMOTE_USER
277 from paste.httpheaders import REMOTE_USER
278 username = REMOTE_USER(environ)
278 username = REMOTE_USER(environ)
279
279
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 username = environ.get('HTTP_X_FORWARDED_USER')
281 username = environ.get('HTTP_X_FORWARDED_USER')
282
282
283 if username:
283 if username:
284 # Removing realm and domain from username
284 # Removing realm and domain from username
285 username = username.partition('@')[0]
285 username = username.partition('@')[0]
286 username = username.rpartition('\\')[2]
286 username = username.rpartition('\\')[2]
287 log.debug('Received username %s from container' % username)
287 log.debug('Received username %s from container' % username)
288
288
289 return username
289 return username
290
290
291
291
292 class CookieStoreWrapper(object):
293
294 def __init__(self, cookie_store):
295 self.cookie_store = cookie_store
296
297 def __repr__(self):
298 return 'CookieStore<%s>' % (self.cookie_store)
299
300 def get(self, key, other=None):
301 if isinstance(self.cookie_store, dict):
302 return self.cookie_store.get(key, other)
303 elif isinstance(self.cookie_store, AuthUser):
304 return self.cookie_store.__dict__.get(key, other)
305
306
292 class AuthUser(object):
307 class AuthUser(object):
293 """
308 """
294 A simple object that handles all attributes of user in RhodeCode
309 A simple object that handles all attributes of user in RhodeCode
295
310
296 It does lookup based on API key,given user, or user present in session
311 It does lookup based on API key,given user, or user present in session
297 Then it fills all required information for such user. It also checks if
312 Then it fills all required information for such user. It also checks if
298 anonymous access is enabled and if so, it returns default user as logged
313 anonymous access is enabled and if so, it returns default user as logged
299 in
314 in
300 """
315 """
301
316
302 def __init__(self, user_id=None, api_key=None, username=None):
317 def __init__(self, user_id=None, api_key=None, username=None):
303
318
304 self.user_id = user_id
319 self.user_id = user_id
305 self.api_key = None
320 self.api_key = None
306 self.username = username
321 self.username = username
307
322
308 self.name = ''
323 self.name = ''
309 self.lastname = ''
324 self.lastname = ''
310 self.email = ''
325 self.email = ''
311 self.is_authenticated = False
326 self.is_authenticated = False
312 self.admin = False
327 self.admin = False
313 self.permissions = {}
328 self.permissions = {}
314 self._api_key = api_key
329 self._api_key = api_key
315 self.propagate_data()
330 self.propagate_data()
316 self._instance = None
331 self._instance = None
317
332
318 def propagate_data(self):
333 def propagate_data(self):
319 user_model = UserModel()
334 user_model = UserModel()
320 self.anonymous_user = User.get_by_username('default', cache=True)
335 self.anonymous_user = User.get_by_username('default', cache=True)
321 is_user_loaded = False
336 is_user_loaded = False
322
337
323 # try go get user by api key
338 # try go get user by api key
324 if self._api_key and self._api_key != self.anonymous_user.api_key:
339 if self._api_key and self._api_key != self.anonymous_user.api_key:
325 log.debug('Auth User lookup by API KEY %s' % self._api_key)
340 log.debug('Auth User lookup by API KEY %s' % self._api_key)
326 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
341 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
327 # lookup by userid
342 # lookup by userid
328 elif (self.user_id is not None and
343 elif (self.user_id is not None and
329 self.user_id != self.anonymous_user.user_id):
344 self.user_id != self.anonymous_user.user_id):
330 log.debug('Auth User lookup by USER ID %s' % self.user_id)
345 log.debug('Auth User lookup by USER ID %s' % self.user_id)
331 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
346 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
332 # lookup by username
347 # lookup by username
333 elif self.username and \
348 elif self.username and \
334 str2bool(config.get('container_auth_enabled', False)):
349 str2bool(config.get('container_auth_enabled', False)):
335
350
336 log.debug('Auth User lookup by USER NAME %s' % self.username)
351 log.debug('Auth User lookup by USER NAME %s' % self.username)
337 dbuser = login_container_auth(self.username)
352 dbuser = login_container_auth(self.username)
338 if dbuser is not None:
353 if dbuser is not None:
339 for k, v in dbuser.get_dict().items():
354 for k, v in dbuser.get_dict().items():
340 setattr(self, k, v)
355 setattr(self, k, v)
341 self.set_authenticated()
356 self.set_authenticated()
342 is_user_loaded = True
357 is_user_loaded = True
343
358
344 if not is_user_loaded:
359 if not is_user_loaded:
345 # if we cannot authenticate user try anonymous
360 # if we cannot authenticate user try anonymous
346 if self.anonymous_user.active is True:
361 if self.anonymous_user.active is True:
347 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
362 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
348 # then we set this user is logged in
363 # then we set this user is logged in
349 self.is_authenticated = True
364 self.is_authenticated = True
350 else:
365 else:
351 self.user_id = None
366 self.user_id = None
352 self.username = None
367 self.username = None
353 self.is_authenticated = False
368 self.is_authenticated = False
354
369
355 if not self.username:
370 if not self.username:
356 self.username = 'None'
371 self.username = 'None'
357
372
358 log.debug('Auth User is now %s' % self)
373 log.debug('Auth User is now %s' % self)
359 user_model.fill_perms(self)
374 user_model.fill_perms(self)
360
375
361 @property
376 @property
362 def is_admin(self):
377 def is_admin(self):
363 return self.admin
378 return self.admin
364
379
365 def __repr__(self):
380 def __repr__(self):
366 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
381 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
367 self.is_authenticated)
382 self.is_authenticated)
368
383
369 def set_authenticated(self, authenticated=True):
384 def set_authenticated(self, authenticated=True):
370 if self.user_id != self.anonymous_user.user_id:
385 if self.user_id != self.anonymous_user.user_id:
371 self.is_authenticated = authenticated
386 self.is_authenticated = authenticated
372
387
373 def get_cookie_store(self):
388 def get_cookie_store(self):
374 return {'username': self.username,
389 return {'username': self.username,
375 'user_id': self.user_id,
390 'user_id': self.user_id,
376 'is_authenticated': self.is_authenticated}
391 'is_authenticated': self.is_authenticated}
377
392
378 @classmethod
393 @classmethod
379 def from_cookie_store(cls, cookie_store):
394 def from_cookie_store(cls, cookie_store):
395 """
396 Creates AuthUser from a cookie store
397
398 :param cls:
399 :param cookie_store:
400 """
380 user_id = cookie_store.get('user_id')
401 user_id = cookie_store.get('user_id')
381 username = cookie_store.get('username')
402 username = cookie_store.get('username')
382 api_key = cookie_store.get('api_key')
403 api_key = cookie_store.get('api_key')
383 return AuthUser(user_id, api_key, username)
404 return AuthUser(user_id, api_key, username)
384
405
385
406
386 def set_available_permissions(config):
407 def set_available_permissions(config):
387 """
408 """
388 This function will propagate pylons globals with all available defined
409 This function will propagate pylons globals with all available defined
389 permission given in db. We don't want to check each time from db for new
410 permission given in db. We don't want to check each time from db for new
390 permissions since adding a new permission also requires application restart
411 permissions since adding a new permission also requires application restart
391 ie. to decorate new views with the newly created permission
412 ie. to decorate new views with the newly created permission
392
413
393 :param config: current pylons config instance
414 :param config: current pylons config instance
394
415
395 """
416 """
396 log.info('getting information about all available permissions')
417 log.info('getting information about all available permissions')
397 try:
418 try:
398 sa = meta.Session
419 sa = meta.Session
399 all_perms = sa.query(Permission).all()
420 all_perms = sa.query(Permission).all()
400 except Exception:
421 except Exception:
401 pass
422 pass
402 finally:
423 finally:
403 meta.Session.remove()
424 meta.Session.remove()
404
425
405 config['available_permissions'] = [x.permission_name for x in all_perms]
426 config['available_permissions'] = [x.permission_name for x in all_perms]
406
427
407
428
408 #==============================================================================
429 #==============================================================================
409 # CHECK DECORATORS
430 # CHECK DECORATORS
410 #==============================================================================
431 #==============================================================================
411 class LoginRequired(object):
432 class LoginRequired(object):
412 """
433 """
413 Must be logged in to execute this function else
434 Must be logged in to execute this function else
414 redirect to login page
435 redirect to login page
415
436
416 :param api_access: if enabled this checks only for valid auth token
437 :param api_access: if enabled this checks only for valid auth token
417 and grants access based on valid token
438 and grants access based on valid token
418 """
439 """
419
440
420 def __init__(self, api_access=False):
441 def __init__(self, api_access=False):
421 self.api_access = api_access
442 self.api_access = api_access
422
443
423 def __call__(self, func):
444 def __call__(self, func):
424 return decorator(self.__wrapper, func)
445 return decorator(self.__wrapper, func)
425
446
426 def __wrapper(self, func, *fargs, **fkwargs):
447 def __wrapper(self, func, *fargs, **fkwargs):
427 cls = fargs[0]
448 cls = fargs[0]
428 user = cls.rhodecode_user
449 user = cls.rhodecode_user
429
450
430 api_access_ok = False
451 api_access_ok = False
431 if self.api_access:
452 if self.api_access:
432 log.debug('Checking API KEY access for %s' % cls)
453 log.debug('Checking API KEY access for %s' % cls)
433 if user.api_key == request.GET.get('api_key'):
454 if user.api_key == request.GET.get('api_key'):
434 api_access_ok = True
455 api_access_ok = True
435 else:
456 else:
436 log.debug("API KEY token not valid")
457 log.debug("API KEY token not valid")
437 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
458 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
438 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
459 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
439 if user.is_authenticated or api_access_ok:
460 if user.is_authenticated or api_access_ok:
440 log.info('user %s is authenticated and granted access to %s' % (
461 log.info('user %s is authenticated and granted access to %s' % (
441 user.username, loc)
462 user.username, loc)
442 )
463 )
443 return func(*fargs, **fkwargs)
464 return func(*fargs, **fkwargs)
444 else:
465 else:
445 log.warn('user %s NOT authenticated on func: %s' % (
466 log.warn('user %s NOT authenticated on func: %s' % (
446 user, loc)
467 user, loc)
447 )
468 )
448 p = url.current()
469 p = url.current()
449
470
450 log.debug('redirecting to login page with %s' % p)
471 log.debug('redirecting to login page with %s' % p)
451 return redirect(url('login_home', came_from=p))
472 return redirect(url('login_home', came_from=p))
452
473
453
474
454 class NotAnonymous(object):
475 class NotAnonymous(object):
455 """
476 """
456 Must be logged in to execute this function else
477 Must be logged in to execute this function else
457 redirect to login page"""
478 redirect to login page"""
458
479
459 def __call__(self, func):
480 def __call__(self, func):
460 return decorator(self.__wrapper, func)
481 return decorator(self.__wrapper, func)
461
482
462 def __wrapper(self, func, *fargs, **fkwargs):
483 def __wrapper(self, func, *fargs, **fkwargs):
463 cls = fargs[0]
484 cls = fargs[0]
464 self.user = cls.rhodecode_user
485 self.user = cls.rhodecode_user
465
486
466 log.debug('Checking if user is not anonymous @%s' % cls)
487 log.debug('Checking if user is not anonymous @%s' % cls)
467
488
468 anonymous = self.user.username == 'default'
489 anonymous = self.user.username == 'default'
469
490
470 if anonymous:
491 if anonymous:
471 p = url.current()
492 p = url.current()
472
493
473 import rhodecode.lib.helpers as h
494 import rhodecode.lib.helpers as h
474 h.flash(_('You need to be a registered user to '
495 h.flash(_('You need to be a registered user to '
475 'perform this action'),
496 'perform this action'),
476 category='warning')
497 category='warning')
477 return redirect(url('login_home', came_from=p))
498 return redirect(url('login_home', came_from=p))
478 else:
499 else:
479 return func(*fargs, **fkwargs)
500 return func(*fargs, **fkwargs)
480
501
481
502
482 class PermsDecorator(object):
503 class PermsDecorator(object):
483 """Base class for controller decorators"""
504 """Base class for controller decorators"""
484
505
485 def __init__(self, *required_perms):
506 def __init__(self, *required_perms):
486 available_perms = config['available_permissions']
507 available_perms = config['available_permissions']
487 for perm in required_perms:
508 for perm in required_perms:
488 if perm not in available_perms:
509 if perm not in available_perms:
489 raise Exception("'%s' permission is not defined" % perm)
510 raise Exception("'%s' permission is not defined" % perm)
490 self.required_perms = set(required_perms)
511 self.required_perms = set(required_perms)
491 self.user_perms = None
512 self.user_perms = None
492
513
493 def __call__(self, func):
514 def __call__(self, func):
494 return decorator(self.__wrapper, func)
515 return decorator(self.__wrapper, func)
495
516
496 def __wrapper(self, func, *fargs, **fkwargs):
517 def __wrapper(self, func, *fargs, **fkwargs):
497 cls = fargs[0]
518 cls = fargs[0]
498 self.user = cls.rhodecode_user
519 self.user = cls.rhodecode_user
499 self.user_perms = self.user.permissions
520 self.user_perms = self.user.permissions
500 log.debug('checking %s permissions %s for %s %s',
521 log.debug('checking %s permissions %s for %s %s',
501 self.__class__.__name__, self.required_perms, cls,
522 self.__class__.__name__, self.required_perms, cls,
502 self.user)
523 self.user)
503
524
504 if self.check_permissions():
525 if self.check_permissions():
505 log.debug('Permission granted for %s %s' % (cls, self.user))
526 log.debug('Permission granted for %s %s' % (cls, self.user))
506 return func(*fargs, **fkwargs)
527 return func(*fargs, **fkwargs)
507
528
508 else:
529 else:
509 log.debug('Permission denied for %s %s' % (cls, self.user))
530 log.debug('Permission denied for %s %s' % (cls, self.user))
510 anonymous = self.user.username == 'default'
531 anonymous = self.user.username == 'default'
511
532
512 if anonymous:
533 if anonymous:
513 p = url.current()
534 p = url.current()
514
535
515 import rhodecode.lib.helpers as h
536 import rhodecode.lib.helpers as h
516 h.flash(_('You need to be a signed in to '
537 h.flash(_('You need to be a signed in to '
517 'view this page'),
538 'view this page'),
518 category='warning')
539 category='warning')
519 return redirect(url('login_home', came_from=p))
540 return redirect(url('login_home', came_from=p))
520
541
521 else:
542 else:
522 # redirect with forbidden ret code
543 # redirect with forbidden ret code
523 return abort(403)
544 return abort(403)
524
545
525 def check_permissions(self):
546 def check_permissions(self):
526 """Dummy function for overriding"""
547 """Dummy function for overriding"""
527 raise Exception('You have to write this function in child class')
548 raise Exception('You have to write this function in child class')
528
549
529
550
530 class HasPermissionAllDecorator(PermsDecorator):
551 class HasPermissionAllDecorator(PermsDecorator):
531 """
552 """
532 Checks for access permission for all given predicates. All of them
553 Checks for access permission for all given predicates. All of them
533 have to be meet in order to fulfill the request
554 have to be meet in order to fulfill the request
534 """
555 """
535
556
536 def check_permissions(self):
557 def check_permissions(self):
537 if self.required_perms.issubset(self.user_perms.get('global')):
558 if self.required_perms.issubset(self.user_perms.get('global')):
538 return True
559 return True
539 return False
560 return False
540
561
541
562
542 class HasPermissionAnyDecorator(PermsDecorator):
563 class HasPermissionAnyDecorator(PermsDecorator):
543 """
564 """
544 Checks for access permission for any of given predicates. In order to
565 Checks for access permission for any of given predicates. In order to
545 fulfill the request any of predicates must be meet
566 fulfill the request any of predicates must be meet
546 """
567 """
547
568
548 def check_permissions(self):
569 def check_permissions(self):
549 if self.required_perms.intersection(self.user_perms.get('global')):
570 if self.required_perms.intersection(self.user_perms.get('global')):
550 return True
571 return True
551 return False
572 return False
552
573
553
574
554 class HasRepoPermissionAllDecorator(PermsDecorator):
575 class HasRepoPermissionAllDecorator(PermsDecorator):
555 """
576 """
556 Checks for access permission for all given predicates for specific
577 Checks for access permission for all given predicates for specific
557 repository. All of them have to be meet in order to fulfill the request
578 repository. All of them have to be meet in order to fulfill the request
558 """
579 """
559
580
560 def check_permissions(self):
581 def check_permissions(self):
561 repo_name = get_repo_slug(request)
582 repo_name = get_repo_slug(request)
562 try:
583 try:
563 user_perms = set([self.user_perms['repositories'][repo_name]])
584 user_perms = set([self.user_perms['repositories'][repo_name]])
564 except KeyError:
585 except KeyError:
565 return False
586 return False
566 if self.required_perms.issubset(user_perms):
587 if self.required_perms.issubset(user_perms):
567 return True
588 return True
568 return False
589 return False
569
590
570
591
571 class HasRepoPermissionAnyDecorator(PermsDecorator):
592 class HasRepoPermissionAnyDecorator(PermsDecorator):
572 """
593 """
573 Checks for access permission for any of given predicates for specific
594 Checks for access permission for any of given predicates for specific
574 repository. In order to fulfill the request any of predicates must be meet
595 repository. In order to fulfill the request any of predicates must be meet
575 """
596 """
576
597
577 def check_permissions(self):
598 def check_permissions(self):
578 repo_name = get_repo_slug(request)
599 repo_name = get_repo_slug(request)
579
600
580 try:
601 try:
581 user_perms = set([self.user_perms['repositories'][repo_name]])
602 user_perms = set([self.user_perms['repositories'][repo_name]])
582 except KeyError:
603 except KeyError:
583 return False
604 return False
584 if self.required_perms.intersection(user_perms):
605 if self.required_perms.intersection(user_perms):
585 return True
606 return True
586 return False
607 return False
587
608
588
609
589 class HasReposGroupPermissionAllDecorator(PermsDecorator):
610 class HasReposGroupPermissionAllDecorator(PermsDecorator):
590 """
611 """
591 Checks for access permission for all given predicates for specific
612 Checks for access permission for all given predicates for specific
592 repository. All of them have to be meet in order to fulfill the request
613 repository. All of them have to be meet in order to fulfill the request
593 """
614 """
594
615
595 def check_permissions(self):
616 def check_permissions(self):
596 group_name = get_repos_group_slug(request)
617 group_name = get_repos_group_slug(request)
597 try:
618 try:
598 user_perms = set([self.user_perms['repositories_groups'][group_name]])
619 user_perms = set([self.user_perms['repositories_groups'][group_name]])
599 except KeyError:
620 except KeyError:
600 return False
621 return False
601 if self.required_perms.issubset(user_perms):
622 if self.required_perms.issubset(user_perms):
602 return True
623 return True
603 return False
624 return False
604
625
605
626
606 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
627 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
607 """
628 """
608 Checks for access permission for any of given predicates for specific
629 Checks for access permission for any of given predicates for specific
609 repository. In order to fulfill the request any of predicates must be meet
630 repository. In order to fulfill the request any of predicates must be meet
610 """
631 """
611
632
612 def check_permissions(self):
633 def check_permissions(self):
613 group_name = get_repos_group_slug(request)
634 group_name = get_repos_group_slug(request)
614
635
615 try:
636 try:
616 user_perms = set([self.user_perms['repositories_groups'][group_name]])
637 user_perms = set([self.user_perms['repositories_groups'][group_name]])
617 except KeyError:
638 except KeyError:
618 return False
639 return False
619 if self.required_perms.intersection(user_perms):
640 if self.required_perms.intersection(user_perms):
620 return True
641 return True
621 return False
642 return False
622
643
623
644
624 #==============================================================================
645 #==============================================================================
625 # CHECK FUNCTIONS
646 # CHECK FUNCTIONS
626 #==============================================================================
647 #==============================================================================
627 class PermsFunction(object):
648 class PermsFunction(object):
628 """Base function for other check functions"""
649 """Base function for other check functions"""
629
650
630 def __init__(self, *perms):
651 def __init__(self, *perms):
631 available_perms = config['available_permissions']
652 available_perms = config['available_permissions']
632
653
633 for perm in perms:
654 for perm in perms:
634 if perm not in available_perms:
655 if perm not in available_perms:
635 raise Exception("'%s' permission in not defined" % perm)
656 raise Exception("'%s' permission in not defined" % perm)
636 self.required_perms = set(perms)
657 self.required_perms = set(perms)
637 self.user_perms = None
658 self.user_perms = None
638 self.granted_for = ''
659 self.granted_for = ''
639 self.repo_name = None
660 self.repo_name = None
640
661
641 def __call__(self, check_Location=''):
662 def __call__(self, check_Location=''):
642 user = request.user
663 user = request.user
643 if not user:
664 if not user:
644 return False
665 return False
645 self.user_perms = user.permissions
666 self.user_perms = user.permissions
646 self.granted_for = user
667 self.granted_for = user
647 log.debug('checking %s %s %s', self.__class__.__name__,
668 log.debug('checking %s %s %s', self.__class__.__name__,
648 self.required_perms, user)
669 self.required_perms, user)
649
670
650 if self.check_permissions():
671 if self.check_permissions():
651 log.debug('Permission granted %s @ %s', self.granted_for,
672 log.debug('Permission granted %s @ %s', self.granted_for,
652 check_Location or 'unspecified location')
673 check_Location or 'unspecified location')
653 return True
674 return True
654
675
655 else:
676 else:
656 log.debug('Permission denied for %s @ %s', self.granted_for,
677 log.debug('Permission denied for %s @ %s', self.granted_for,
657 check_Location or 'unspecified location')
678 check_Location or 'unspecified location')
658 return False
679 return False
659
680
660 def check_permissions(self):
681 def check_permissions(self):
661 """Dummy function for overriding"""
682 """Dummy function for overriding"""
662 raise Exception('You have to write this function in child class')
683 raise Exception('You have to write this function in child class')
663
684
664
685
665 class HasPermissionAll(PermsFunction):
686 class HasPermissionAll(PermsFunction):
666 def check_permissions(self):
687 def check_permissions(self):
667 if self.required_perms.issubset(self.user_perms.get('global')):
688 if self.required_perms.issubset(self.user_perms.get('global')):
668 return True
689 return True
669 return False
690 return False
670
691
671
692
672 class HasPermissionAny(PermsFunction):
693 class HasPermissionAny(PermsFunction):
673 def check_permissions(self):
694 def check_permissions(self):
674 if self.required_perms.intersection(self.user_perms.get('global')):
695 if self.required_perms.intersection(self.user_perms.get('global')):
675 return True
696 return True
676 return False
697 return False
677
698
678
699
679 class HasRepoPermissionAll(PermsFunction):
700 class HasRepoPermissionAll(PermsFunction):
680
701
681 def __call__(self, repo_name=None, check_Location=''):
702 def __call__(self, repo_name=None, check_Location=''):
682 self.repo_name = repo_name
703 self.repo_name = repo_name
683 return super(HasRepoPermissionAll, self).__call__(check_Location)
704 return super(HasRepoPermissionAll, self).__call__(check_Location)
684
705
685 def check_permissions(self):
706 def check_permissions(self):
686 if not self.repo_name:
707 if not self.repo_name:
687 self.repo_name = get_repo_slug(request)
708 self.repo_name = get_repo_slug(request)
688
709
689 try:
710 try:
690 self.user_perms = set(
711 self.user_perms = set(
691 [self.user_perms['repositories'][self.repo_name]]
712 [self.user_perms['repositories'][self.repo_name]]
692 )
713 )
693 except KeyError:
714 except KeyError:
694 return False
715 return False
695 self.granted_for = self.repo_name
716 self.granted_for = self.repo_name
696 if self.required_perms.issubset(self.user_perms):
717 if self.required_perms.issubset(self.user_perms):
697 return True
718 return True
698 return False
719 return False
699
720
700
721
701 class HasRepoPermissionAny(PermsFunction):
722 class HasRepoPermissionAny(PermsFunction):
702
723
703 def __call__(self, repo_name=None, check_Location=''):
724 def __call__(self, repo_name=None, check_Location=''):
704 self.repo_name = repo_name
725 self.repo_name = repo_name
705 return super(HasRepoPermissionAny, self).__call__(check_Location)
726 return super(HasRepoPermissionAny, self).__call__(check_Location)
706
727
707 def check_permissions(self):
728 def check_permissions(self):
708 if not self.repo_name:
729 if not self.repo_name:
709 self.repo_name = get_repo_slug(request)
730 self.repo_name = get_repo_slug(request)
710
731
711 try:
732 try:
712 self.user_perms = set(
733 self.user_perms = set(
713 [self.user_perms['repositories'][self.repo_name]]
734 [self.user_perms['repositories'][self.repo_name]]
714 )
735 )
715 except KeyError:
736 except KeyError:
716 return False
737 return False
717 self.granted_for = self.repo_name
738 self.granted_for = self.repo_name
718 if self.required_perms.intersection(self.user_perms):
739 if self.required_perms.intersection(self.user_perms):
719 return True
740 return True
720 return False
741 return False
721
742
722
743
723 class HasReposGroupPermissionAny(PermsFunction):
744 class HasReposGroupPermissionAny(PermsFunction):
724 def __call__(self, group_name=None, check_Location=''):
745 def __call__(self, group_name=None, check_Location=''):
725 self.group_name = group_name
746 self.group_name = group_name
726 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
747 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
727
748
728 def check_permissions(self):
749 def check_permissions(self):
729 try:
750 try:
730 self.user_perms = set(
751 self.user_perms = set(
731 [self.user_perms['repositories_groups'][self.group_name]]
752 [self.user_perms['repositories_groups'][self.group_name]]
732 )
753 )
733 except KeyError:
754 except KeyError:
734 return False
755 return False
735 self.granted_for = self.repo_name
756 self.granted_for = self.repo_name
736 if self.required_perms.intersection(self.user_perms):
757 if self.required_perms.intersection(self.user_perms):
737 return True
758 return True
738 return False
759 return False
739
760
740
761
741 class HasReposGroupPermissionAll(PermsFunction):
762 class HasReposGroupPermissionAll(PermsFunction):
742 def __call__(self, group_name=None, check_Location=''):
763 def __call__(self, group_name=None, check_Location=''):
743 self.group_name = group_name
764 self.group_name = group_name
744 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
765 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
745
766
746 def check_permissions(self):
767 def check_permissions(self):
747 try:
768 try:
748 self.user_perms = set(
769 self.user_perms = set(
749 [self.user_perms['repositories_groups'][self.group_name]]
770 [self.user_perms['repositories_groups'][self.group_name]]
750 )
771 )
751 except KeyError:
772 except KeyError:
752 return False
773 return False
753 self.granted_for = self.repo_name
774 self.granted_for = self.repo_name
754 if self.required_perms.issubset(self.user_perms):
775 if self.required_perms.issubset(self.user_perms):
755 return True
776 return True
756 return False
777 return False
757
778
758
779
759 #==============================================================================
780 #==============================================================================
760 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
781 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
761 #==============================================================================
782 #==============================================================================
762 class HasPermissionAnyMiddleware(object):
783 class HasPermissionAnyMiddleware(object):
763 def __init__(self, *perms):
784 def __init__(self, *perms):
764 self.required_perms = set(perms)
785 self.required_perms = set(perms)
765
786
766 def __call__(self, user, repo_name):
787 def __call__(self, user, repo_name):
767 usr = AuthUser(user.user_id)
788 usr = AuthUser(user.user_id)
768 try:
789 try:
769 self.user_perms = set([usr.permissions['repositories'][repo_name]])
790 self.user_perms = set([usr.permissions['repositories'][repo_name]])
770 except:
791 except:
771 self.user_perms = set()
792 self.user_perms = set()
772 self.granted_for = ''
793 self.granted_for = ''
773 self.username = user.username
794 self.username = user.username
774 self.repo_name = repo_name
795 self.repo_name = repo_name
775 return self.check_permissions()
796 return self.check_permissions()
776
797
777 def check_permissions(self):
798 def check_permissions(self):
778 log.debug('checking mercurial protocol '
799 log.debug('checking mercurial protocol '
779 'permissions %s for user:%s repository:%s', self.user_perms,
800 'permissions %s for user:%s repository:%s', self.user_perms,
780 self.username, self.repo_name)
801 self.username, self.repo_name)
781 if self.required_perms.intersection(self.user_perms):
802 if self.required_perms.intersection(self.user_perms):
782 log.debug('permission granted')
803 log.debug('permission granted')
783 return True
804 return True
784 log.debug('permission denied')
805 log.debug('permission denied')
785 return False
806 return False
@@ -1,186 +1,184 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10
10
11 from pylons import config, tmpl_context as c, request, session, url
11 from pylons import config, tmpl_context as c, request, session, url
12 from pylons.controllers import WSGIController
12 from pylons.controllers import WSGIController
13 from pylons.controllers.util import redirect
13 from pylons.controllers.util import redirect
14 from pylons.templating import render_mako as render
14 from pylons.templating import render_mako as render
15
15
16 from rhodecode import __version__, BACKENDS
16 from rhodecode import __version__, BACKENDS
17
17
18 from rhodecode.lib import str2bool, safe_unicode
18 from rhodecode.lib import str2bool, safe_unicode
19 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
19 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
20 HasPermissionAnyMiddleware
20 HasPermissionAnyMiddleware, CookieStoreWrapper
21 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
21 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
22 from rhodecode.model import meta
22 from rhodecode.model import meta
23
23
24 from rhodecode.model.db import Repository
24 from rhodecode.model.db import Repository
25 from rhodecode.model.notification import NotificationModel
25 from rhodecode.model.notification import NotificationModel
26 from rhodecode.model.scm import ScmModel
26 from rhodecode.model.scm import ScmModel
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 class BaseVCSController(object):
31 class BaseVCSController(object):
32
32
33 def __init__(self, application, config):
33 def __init__(self, application, config):
34 self.application = application
34 self.application = application
35 self.config = config
35 self.config = config
36 # base path of repo locations
36 # base path of repo locations
37 self.basepath = self.config['base_path']
37 self.basepath = self.config['base_path']
38 #authenticate this mercurial request using authfunc
38 #authenticate this mercurial request using authfunc
39 self.authenticate = AuthBasicAuthenticator('', authfunc)
39 self.authenticate = AuthBasicAuthenticator('', authfunc)
40 self.ipaddr = '0.0.0.0'
40 self.ipaddr = '0.0.0.0'
41
41
42 def _handle_request(self, environ, start_response):
42 def _handle_request(self, environ, start_response):
43 raise NotImplementedError()
43 raise NotImplementedError()
44
44
45 def _get_by_id(self, repo_name):
45 def _get_by_id(self, repo_name):
46 """
46 """
47 Get's a special pattern _<ID> from clone url and tries to replace it
47 Get's a special pattern _<ID> from clone url and tries to replace it
48 with a repository_name for support of _<ID> non changable urls
48 with a repository_name for support of _<ID> non changable urls
49
49
50 :param repo_name:
50 :param repo_name:
51 """
51 """
52 try:
52 try:
53 data = repo_name.split('/')
53 data = repo_name.split('/')
54 if len(data) >= 2:
54 if len(data) >= 2:
55 by_id = data[1].split('_')
55 by_id = data[1].split('_')
56 if len(by_id) == 2 and by_id[1].isdigit():
56 if len(by_id) == 2 and by_id[1].isdigit():
57 _repo_name = Repository.get(by_id[1]).repo_name
57 _repo_name = Repository.get(by_id[1]).repo_name
58 data[1] = _repo_name
58 data[1] = _repo_name
59 except:
59 except:
60 log.debug('Failed to extract repo_name from id %s' % (
60 log.debug('Failed to extract repo_name from id %s' % (
61 traceback.format_exc()
61 traceback.format_exc()
62 )
62 )
63 )
63 )
64
64
65 return '/'.join(data)
65 return '/'.join(data)
66
66
67 def _invalidate_cache(self, repo_name):
67 def _invalidate_cache(self, repo_name):
68 """
68 """
69 Set's cache for this repository for invalidation on next access
69 Set's cache for this repository for invalidation on next access
70
70
71 :param repo_name: full repo name, also a cache key
71 :param repo_name: full repo name, also a cache key
72 """
72 """
73 invalidate_cache('get_repo_cached_%s' % repo_name)
73 invalidate_cache('get_repo_cached_%s' % repo_name)
74
74
75 def _check_permission(self, action, user, repo_name):
75 def _check_permission(self, action, user, repo_name):
76 """
76 """
77 Checks permissions using action (push/pull) user and repository
77 Checks permissions using action (push/pull) user and repository
78 name
78 name
79
79
80 :param action: push or pull action
80 :param action: push or pull action
81 :param user: user instance
81 :param user: user instance
82 :param repo_name: repository name
82 :param repo_name: repository name
83 """
83 """
84 if action == 'push':
84 if action == 'push':
85 if not HasPermissionAnyMiddleware('repository.write',
85 if not HasPermissionAnyMiddleware('repository.write',
86 'repository.admin')(user,
86 'repository.admin')(user,
87 repo_name):
87 repo_name):
88 return False
88 return False
89
89
90 else:
90 else:
91 #any other action need at least read permission
91 #any other action need at least read permission
92 if not HasPermissionAnyMiddleware('repository.read',
92 if not HasPermissionAnyMiddleware('repository.read',
93 'repository.write',
93 'repository.write',
94 'repository.admin')(user,
94 'repository.admin')(user,
95 repo_name):
95 repo_name):
96 return False
96 return False
97
97
98 return True
98 return True
99
99
100 def __call__(self, environ, start_response):
100 def __call__(self, environ, start_response):
101 start = time.time()
101 start = time.time()
102 try:
102 try:
103 return self._handle_request(environ, start_response)
103 return self._handle_request(environ, start_response)
104 finally:
104 finally:
105 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
105 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
106 log.debug('Request time: %.3fs' % (time.time() - start))
106 log.debug('Request time: %.3fs' % (time.time() - start))
107 meta.Session.remove()
107 meta.Session.remove()
108
108
109
109
110 class BaseController(WSGIController):
110 class BaseController(WSGIController):
111
111
112 def __before__(self):
112 def __before__(self):
113 c.rhodecode_version = __version__
113 c.rhodecode_version = __version__
114 c.rhodecode_instanceid = config.get('instance_id')
114 c.rhodecode_instanceid = config.get('instance_id')
115 c.rhodecode_name = config.get('rhodecode_title')
115 c.rhodecode_name = config.get('rhodecode_title')
116 c.use_gravatar = str2bool(config.get('use_gravatar'))
116 c.use_gravatar = str2bool(config.get('use_gravatar'))
117 c.ga_code = config.get('rhodecode_ga_code')
117 c.ga_code = config.get('rhodecode_ga_code')
118 c.repo_name = get_repo_slug(request)
118 c.repo_name = get_repo_slug(request)
119 c.backends = BACKENDS.keys()
119 c.backends = BACKENDS.keys()
120 c.unread_notifications = NotificationModel()\
120 c.unread_notifications = NotificationModel()\
121 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
121 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
122 self.cut_off_limit = int(config.get('cut_off_limit'))
122 self.cut_off_limit = int(config.get('cut_off_limit'))
123
123
124 self.sa = meta.Session
124 self.sa = meta.Session
125 self.scm_model = ScmModel(self.sa)
125 self.scm_model = ScmModel(self.sa)
126
126
127 def __call__(self, environ, start_response):
127 def __call__(self, environ, start_response):
128 """Invoke the Controller"""
128 """Invoke the Controller"""
129 # WSGIController.__call__ dispatches to the Controller method
129 # WSGIController.__call__ dispatches to the Controller method
130 # the request is routed to. This routing information is
130 # the request is routed to. This routing information is
131 # available in environ['pylons.routes_dict']
131 # available in environ['pylons.routes_dict']
132 start = time.time()
132 start = time.time()
133 try:
133 try:
134 # make sure that we update permissions each time we call controller
134 # make sure that we update permissions each time we call controller
135 api_key = request.GET.get('api_key')
135 api_key = request.GET.get('api_key')
136 cookie_store = session.get('rhodecode_user') or {}
136 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
137 user_id = cookie_store.get('user_id', None)
137 user_id = cookie_store.get('user_id', None)
138 username = get_container_username(environ, config)
138 username = get_container_username(environ, config)
139
139
140 auth_user = AuthUser(user_id, api_key, username)
140 auth_user = AuthUser(user_id, api_key, username)
141 request.user = auth_user
141 request.user = auth_user
142 self.rhodecode_user = c.rhodecode_user = auth_user
142 self.rhodecode_user = c.rhodecode_user = auth_user
143 if not self.rhodecode_user.is_authenticated and \
143 if not self.rhodecode_user.is_authenticated and \
144 self.rhodecode_user.user_id is not None:
144 self.rhodecode_user.user_id is not None:
145 self.rhodecode_user\
145 self.rhodecode_user.set_authenticated(
146 .set_authenticated(cookie_store.get('is_authenticated'))
146 cookie_store.get('is_authenticated')
147
147 )
148 session['rhodecode_user'] = self.rhodecode_user.get_cookie_store()
149 session.save()
150 log.info('User: %s accessed %s' % (
148 log.info('User: %s accessed %s' % (
151 auth_user, safe_unicode(environ.get('PATH_INFO')))
149 auth_user, safe_unicode(environ.get('PATH_INFO')))
152 )
150 )
153 return WSGIController.__call__(self, environ, start_response)
151 return WSGIController.__call__(self, environ, start_response)
154 finally:
152 finally:
155 log.info('Request to %s time: %.3fs' % (
153 log.info('Request to %s time: %.3fs' % (
156 safe_unicode(environ.get('PATH_INFO')), time.time() - start)
154 safe_unicode(environ.get('PATH_INFO')), time.time() - start)
157 )
155 )
158 meta.Session.remove()
156 meta.Session.remove()
159
157
160
158
161 class BaseRepoController(BaseController):
159 class BaseRepoController(BaseController):
162 """
160 """
163 Base class for controllers responsible for loading all needed data for
161 Base class for controllers responsible for loading all needed data for
164 repository loaded items are
162 repository loaded items are
165
163
166 c.rhodecode_repo: instance of scm repository
164 c.rhodecode_repo: instance of scm repository
167 c.rhodecode_db_repo: instance of db
165 c.rhodecode_db_repo: instance of db
168 c.repository_followers: number of followers
166 c.repository_followers: number of followers
169 c.repository_forks: number of forks
167 c.repository_forks: number of forks
170 """
168 """
171
169
172 def __before__(self):
170 def __before__(self):
173 super(BaseRepoController, self).__before__()
171 super(BaseRepoController, self).__before__()
174 if c.repo_name:
172 if c.repo_name:
175
173
176 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
174 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
177 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
175 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
178
176
179 if c.rhodecode_repo is None:
177 if c.rhodecode_repo is None:
180 log.error('%s this repository is present in database but it '
178 log.error('%s this repository is present in database but it '
181 'cannot be created as an scm instance', c.repo_name)
179 'cannot be created as an scm instance', c.repo_name)
182
180
183 redirect(url('home'))
181 redirect(url('home'))
184
182
185 c.repository_followers = self.scm_model.get_followers(c.repo_name)
183 c.repository_followers = self.scm_model.get_followers(c.repo_name)
186 c.repository_forks = self.scm_model.get_forks(c.repo_name)
184 c.repository_forks = self.scm_model.get_forks(c.repo_name)
General Comments 0
You need to be logged in to leave comments. Login now