##// END OF EJS Templates
merged with bugfixes
marcink -
r425:570b9e82 merge default
parent child Browse files
Show More
@@ -1,98 +1,97 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # login controller for pylons
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20
21 21 """
22 22 Created on April 22, 2010
23 23 login controller for pylons
24 24 @author: marcink
25 25 """
26 26 from formencode import htmlfill
27 27 from pylons import request, response, session, tmpl_context as c, url
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons_app.lib.auth import AuthUser, HasPermissionAnyDecorator
30 30 from pylons_app.lib.base import BaseController, render
31 31 from pylons_app.model.forms import LoginForm, RegisterForm
32 32 from pylons_app.model.user_model import UserModel
33 33 import formencode
34 34 import logging
35 35
36 36 log = logging.getLogger(__name__)
37 37
38 38 class LoginController(BaseController):
39 39
40 40 def __before__(self):
41 41 super(LoginController, self).__before__()
42 42
43 43 def index(self):
44 44 #redirect if already logged in
45 45 if c.hg_app_user.is_authenticated:
46 46 return redirect(url('hg_home'))
47 47
48 48 if request.POST:
49 49 #import Login Form validator class
50 50 login_form = LoginForm()
51 51 try:
52 52 c.form_result = login_form.to_python(dict(request.POST))
53 53 return redirect(url('hg_home'))
54 54
55 55 except formencode.Invalid as errors:
56 56 return htmlfill.render(
57 57 render('/login.html'),
58 58 defaults=errors.value,
59 59 errors=errors.error_dict or {},
60 60 prefix_error=False,
61 61 encoding="UTF-8")
62 62
63 63 return render('/login.html')
64 64
65 65 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
66 66 def register(self):
67 67 user_model = UserModel()
68 68 c.auto_active = False
69 69 for perm in user_model.get_default().user_perms:
70 print perm.permission.permission_name
71 70 if perm.permission.permission_name == 'hg.register.auto_activate':
72 71 c.auto_active = True
73 72 break
74 73
75 74 if request.POST:
76 75
77 76 register_form = RegisterForm()()
78 77 try:
79 78 form_result = register_form.to_python(dict(request.POST))
80 79 form_result['active'] = c.auto_active
81 80 user_model.create_registration(form_result)
82 81 return redirect(url('login_home'))
83 82
84 83 except formencode.Invalid as errors:
85 84 return htmlfill.render(
86 85 render('/register.html'),
87 86 defaults=errors.value,
88 87 errors=errors.error_dict or {},
89 88 prefix_error=False,
90 89 encoding="UTF-8")
91 90
92 91 return render('/register.html')
93 92
94 93 def logout(self):
95 94 session['hg_app_user'] = AuthUser()
96 95 session.save()
97 96 log.info('Logging out and setting user as Empty')
98 97 redirect(url('hg_home'))
@@ -1,450 +1,450 b''
1 1 #!/usr/bin/env python
2 2 # encoding: utf-8
3 3 # authentication and permission libraries
4 4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 5 #
6 6 # This program is free software; you can redistribute it and/or
7 7 # modify it under the terms of the GNU General Public License
8 8 # as published by the Free Software Foundation; version 2
9 9 # of the License or (at your opinion) any later version of the license.
10 10 #
11 11 # This program is distributed in the hope that it will be useful,
12 12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 14 # GNU General Public License for more details.
15 15 #
16 16 # You should have received a copy of the GNU General Public License
17 17 # along with this program; if not, write to the Free Software
18 18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 19 # MA 02110-1301, USA.
20 20 """
21 21 Created on April 4, 2010
22 22
23 23 @author: marcink
24 24 """
25 25 from beaker.cache import cache_region
26 26 from pylons import config, session, url, request
27 27 from pylons.controllers.util import abort, redirect
28 28 from pylons_app.lib.utils import get_repo_slug
29 29 from pylons_app.model import meta
30 30 from pylons_app.model.db import User, RepoToPerm, Repository, Permission, \
31 31 UserToPerm
32 32 from sqlalchemy.exc import OperationalError
33 33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 34 import bcrypt
35 35 from decorator import decorator
36 36 import logging
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40 def get_crypt_password(password):
41 41 """Cryptographic function used for password hashing based on sha1
42 42 @param password: password to hash
43 43 """
44 44 return bcrypt.hashpw(password, bcrypt.gensalt(10))
45 45
46 46 def check_password(password, hashed):
47 47 return bcrypt.hashpw(password, hashed) == hashed
48 48
49 49 @cache_region('super_short_term', 'cached_user')
50 50 def get_user_cached(username):
51 51 sa = meta.Session
52 52 try:
53 53 user = sa.query(User).filter(User.username == username).one()
54 54 finally:
55 55 meta.Session.remove()
56 56 return user
57 57
58 58 def authfunc(environ, username, password):
59 59 try:
60 60 user = get_user_cached(username)
61 61 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
62 62 log.error(e)
63 63 user = None
64 64
65 65 if user:
66 66 if user.active:
67 67 if user.username == username and check_password(password, user.password):
68 68 log.info('user %s authenticated correctly', username)
69 69 return True
70 70 else:
71 71 log.error('user %s is disabled', username)
72 72
73 73 return False
74 74
75 75 class AuthUser(object):
76 76 """
77 77 A simple object that handles a mercurial username for authentication
78 78 """
79 79 def __init__(self):
80 80 self.username = 'None'
81 81 self.name = ''
82 82 self.lastname = ''
83 83 self.email = ''
84 84 self.user_id = None
85 85 self.is_authenticated = False
86 86 self.is_admin = False
87 87 self.permissions = {}
88 88
89 89
90 90 def set_available_permissions(config):
91 91 """
92 92 This function will propagate pylons globals with all available defined
93 93 permission given in db. We don't wannt to check each time from db for new
94 94 permissions since adding a new permission also requires application restart
95 95 ie. to decorate new views with the newly created permission
96 96 @param config:
97 97 """
98 98 log.info('getting information about all available permissions')
99 99 try:
100 100 sa = meta.Session
101 101 all_perms = sa.query(Permission).all()
102 102 finally:
103 103 meta.Session.remove()
104 104
105 105 config['available_permissions'] = [x.permission_name for x in all_perms]
106 106
107 107 def set_base_path(config):
108 108 config['base_path'] = config['pylons.app_globals'].base_path
109 109
110 110 def fill_data(user):
111 111 """
112 112 Fills user data with those from database and log out user if not present
113 113 in database
114 114 @param user:
115 115 """
116 116 sa = meta.Session
117 117 dbuser = sa.query(User).get(user.user_id)
118 118 if dbuser:
119 119 user.username = dbuser.username
120 120 user.is_admin = dbuser.admin
121 121 user.name = dbuser.name
122 122 user.lastname = dbuser.lastname
123 123 user.email = dbuser.email
124 124 else:
125 125 user.is_authenticated = False
126 126 meta.Session.remove()
127 from pprint import pprint
128 pprint(user.permissions)
129 127 return user
130 128
131 129 def fill_perms(user):
132 130 """
133 131 Fills user permission attribute with permissions taken from database
134 132 @param user:
135 133 """
136 134
137 135 sa = meta.Session
138 136 user.permissions['repositories'] = {}
139 137 user.permissions['global'] = set()
140 138
141 139 #===========================================================================
142 140 # fetch default permissions
143 141 #===========================================================================
144 default_perms = sa.query(RepoToPerm, UserToPerm, Repository, Permission)\
145 .outerjoin((UserToPerm, RepoToPerm.user_id == UserToPerm.user_id))\
142 default_perms = sa.query(RepoToPerm, Repository, Permission)\
146 143 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
147 144 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
148 .filter(RepoToPerm.user_id == sa.query(User).filter(User.username ==
149 'default').one().user_id).all()
145 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
146 'default').scalar()).all()
150 147
151 148 if user.is_admin:
152 149 #=======================================================================
153 # #admin have all rights set to admin
150 # #admin have all default rights set to admin
154 151 #=======================================================================
155 152 user.permissions['global'].add('hg.admin')
156 153
157 154 for perm in default_perms:
158 155 p = 'repository.admin'
159 156 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
160 157
161 158 else:
162 159 #=======================================================================
163 160 # set default permissions
164 161 #=======================================================================
165 162
166 163 #default global
167 for perm in default_perms:
168 user.permissions['global'].add(perm.UserToPerm.permission.permission_name)
164 default_global_perms = sa.query(UserToPerm)\
165 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
166 'default').one())
167
168 for perm in default_global_perms:
169 user.permissions['global'].add(perm.permission.permission_name)
169 170
170 171 #default repositories
171 172 for perm in default_perms:
172 173 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
173 174 #disable defaults for private repos,
174 175 p = 'repository.none'
175 176 elif perm.Repository.user_id == user.user_id:
176 177 #set admin if owner
177 178 p = 'repository.admin'
178 179 else:
179 180 p = perm.Permission.permission_name
180 181
181 182 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
182 183
183 184 #=======================================================================
184 185 # #overwrite default with user permissions if any
185 186 #=======================================================================
186 user_perms = sa.query(RepoToPerm, UserToPerm, Permission, Repository)\
187 .outerjoin((UserToPerm, RepoToPerm.user_id == UserToPerm.user_id))\
187 user_perms = sa.query(RepoToPerm, Permission, Repository)\
188 188 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
189 189 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
190 190 .filter(RepoToPerm.user_id == user.user_id).all()
191 191
192 192 for perm in user_perms:
193 193 if perm.Repository.user_id == user.user_id:#set admin if owner
194 194 p = 'repository.admin'
195 195 else:
196 196 p = perm.Permission.permission_name
197 197 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
198 198 meta.Session.remove()
199 199 return user
200 200
201 201 def get_user(session):
202 202 """
203 203 Gets user from session, and wraps permissions into user
204 204 @param session:
205 205 """
206 206 user = session.get('hg_app_user', AuthUser())
207 207 if user.is_authenticated:
208 208 user = fill_data(user)
209 209 user = fill_perms(user)
210 210 session['hg_app_user'] = user
211 211 session.save()
212 212 return user
213 213
214 214 #===============================================================================
215 215 # CHECK DECORATORS
216 216 #===============================================================================
217 217 class LoginRequired(object):
218 218 """Must be logged in to execute this function else redirect to login page"""
219 219
220 220 def __call__(self, func):
221 221 return decorator(self.__wrapper, func)
222 222
223 223 def __wrapper(self, func, *fargs, **fkwargs):
224 224 user = session.get('hg_app_user', AuthUser())
225 225 log.debug('Checking login required for user:%s', user.username)
226 226 if user.is_authenticated:
227 227 log.debug('user %s is authenticated', user.username)
228 228 return func(*fargs, **fkwargs)
229 229 else:
230 230 log.warn('user %s not authenticated', user.username)
231 231 log.debug('redirecting to login page')
232 232 return redirect(url('login_home'))
233 233
234 234 class PermsDecorator(object):
235 235 """Base class for decorators"""
236 236
237 237 def __init__(self, *required_perms):
238 238 available_perms = config['available_permissions']
239 239 for perm in required_perms:
240 240 if perm not in available_perms:
241 241 raise Exception("'%s' permission is not defined" % perm)
242 242 self.required_perms = set(required_perms)
243 243 self.user_perms = None
244 244
245 245 def __call__(self, func):
246 246 return decorator(self.__wrapper, func)
247 247
248 248
249 249 def __wrapper(self, func, *fargs, **fkwargs):
250 250 # _wrapper.__name__ = func.__name__
251 251 # _wrapper.__dict__.update(func.__dict__)
252 252 # _wrapper.__doc__ = func.__doc__
253 253
254 254 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
255 255 log.debug('checking %s permissions %s for %s',
256 256 self.__class__.__name__, self.required_perms, func.__name__)
257 257
258 258 if self.check_permissions():
259 259 log.debug('Permission granted for %s', func.__name__)
260 260
261 261 return func(*fargs, **fkwargs)
262 262
263 263 else:
264 264 log.warning('Permission denied for %s', func.__name__)
265 265 #redirect with forbidden ret code
266 266 return abort(403)
267 267
268 268
269 269
270 270 def check_permissions(self):
271 271 """Dummy function for overriding"""
272 272 raise Exception('You have to write this function in child class')
273 273
274 274 class HasPermissionAllDecorator(PermsDecorator):
275 275 """Checks for access permission for all given predicates. All of them
276 276 have to be meet in order to fulfill the request
277 277 """
278 278
279 279 def check_permissions(self):
280 280 if self.required_perms.issubset(self.user_perms.get('global')):
281 281 return True
282 282 return False
283 283
284 284
285 285 class HasPermissionAnyDecorator(PermsDecorator):
286 286 """Checks for access permission for any of given predicates. In order to
287 287 fulfill the request any of predicates must be meet
288 288 """
289 289
290 290 def check_permissions(self):
291 291 if self.required_perms.intersection(self.user_perms.get('global')):
292 292 return True
293 293 return False
294 294
295 295 class HasRepoPermissionAllDecorator(PermsDecorator):
296 296 """Checks for access permission for all given predicates for specific
297 297 repository. All of them have to be meet in order to fulfill the request
298 298 """
299 299
300 300 def check_permissions(self):
301 301 repo_name = get_repo_slug(request)
302 302 try:
303 303 user_perms = set([self.user_perms['repositories'][repo_name]])
304 304 except KeyError:
305 305 return False
306 306 if self.required_perms.issubset(user_perms):
307 307 return True
308 308 return False
309 309
310 310
311 311 class HasRepoPermissionAnyDecorator(PermsDecorator):
312 312 """Checks for access permission for any of given predicates for specific
313 313 repository. In order to fulfill the request any of predicates must be meet
314 314 """
315 315
316 316 def check_permissions(self):
317 317 repo_name = get_repo_slug(request)
318 318
319 319 try:
320 320 user_perms = set([self.user_perms['repositories'][repo_name]])
321 321 except KeyError:
322 322 return False
323 323 if self.required_perms.intersection(user_perms):
324 324 return True
325 325 return False
326 326 #===============================================================================
327 327 # CHECK FUNCTIONS
328 328 #===============================================================================
329 329
330 330 class PermsFunction(object):
331 331 """Base function for other check functions"""
332 332
333 333 def __init__(self, *perms):
334 334 available_perms = config['available_permissions']
335 335
336 336 for perm in perms:
337 337 if perm not in available_perms:
338 338 raise Exception("'%s' permission in not defined" % perm)
339 339 self.required_perms = set(perms)
340 340 self.user_perms = None
341 341 self.granted_for = ''
342 342 self.repo_name = None
343 343
344 344 def __call__(self, check_Location=''):
345 345 user = session.get('hg_app_user', False)
346 346 if not user:
347 347 return False
348 348 self.user_perms = user.permissions
349 349 self.granted_for = user.username
350 350 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
351 351
352 352 if self.check_permissions():
353 353 log.debug('Permission granted for %s @%s', self.granted_for,
354 354 check_Location)
355 355 return True
356 356
357 357 else:
358 358 log.warning('Permission denied for %s @%s', self.granted_for,
359 359 check_Location)
360 360 return False
361 361
362 362 def check_permissions(self):
363 363 """Dummy function for overriding"""
364 364 raise Exception('You have to write this function in child class')
365 365
366 366 class HasPermissionAll(PermsFunction):
367 367 def check_permissions(self):
368 368 if self.required_perms.issubset(self.user_perms.get('global')):
369 369 return True
370 370 return False
371 371
372 372 class HasPermissionAny(PermsFunction):
373 373 def check_permissions(self):
374 374 if self.required_perms.intersection(self.user_perms.get('global')):
375 375 return True
376 376 return False
377 377
378 378 class HasRepoPermissionAll(PermsFunction):
379 379
380 380 def __call__(self, repo_name=None, check_Location=''):
381 381 self.repo_name = repo_name
382 382 return super(HasRepoPermissionAll, self).__call__(check_Location)
383 383
384 384 def check_permissions(self):
385 385 if not self.repo_name:
386 386 self.repo_name = get_repo_slug(request)
387 387
388 388 try:
389 389 self.user_perms = set([self.user_perms['repositories']\
390 390 [self.repo_name]])
391 391 except KeyError:
392 392 return False
393 393 self.granted_for = self.repo_name
394 394 if self.required_perms.issubset(self.user_perms):
395 395 return True
396 396 return False
397 397
398 398 class HasRepoPermissionAny(PermsFunction):
399 399
400 400 def __call__(self, repo_name=None, check_Location=''):
401 401 self.repo_name = repo_name
402 402 return super(HasRepoPermissionAny, self).__call__(check_Location)
403 403
404 404 def check_permissions(self):
405 405 if not self.repo_name:
406 406 self.repo_name = get_repo_slug(request)
407 407
408 408 try:
409 409 self.user_perms = set([self.user_perms['repositories']\
410 410 [self.repo_name]])
411 411 except KeyError:
412 412 return False
413 413 self.granted_for = self.repo_name
414 414 if self.required_perms.intersection(self.user_perms):
415 415 return True
416 416 return False
417 417
418 418 #===============================================================================
419 419 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
420 420 #===============================================================================
421 421
422 422 class HasPermissionAnyMiddleware(object):
423 423 def __init__(self, *perms):
424 424 self.required_perms = set(perms)
425 425
426 426 def __call__(self, user, repo_name):
427 427 usr = AuthUser()
428 428 usr.user_id = user.user_id
429 429 usr.username = user.username
430 430 usr.is_admin = user.admin
431 431
432 432 try:
433 433 self.user_perms = set([fill_perms(usr)\
434 434 .permissions['repositories'][repo_name]])
435 435 except:
436 436 self.user_perms = set()
437 437 self.granted_for = ''
438 438 self.username = user.username
439 439 self.repo_name = repo_name
440 440 return self.check_permissions()
441 441
442 442 def check_permissions(self):
443 443 log.debug('checking mercurial protocol '
444 444 'permissions for user:%s repository:%s',
445 445 self.username, self.repo_name)
446 446 if self.required_perms.intersection(self.user_perms):
447 447 log.debug('permission granted')
448 448 return True
449 449 log.debug('permission denied')
450 450 return False
General Comments 0
You need to be logged in to leave comments. Login now