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