##// END OF EJS Templates
Added redirection to page that request came from, after login in
marcink -
r437:930f8182 default
parent child Browse files
Show More
@@ -1,97 +1,102
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 c.came_from = request.GET.get('came_from',None)
46
45 47 if c.hg_app_user.is_authenticated:
46 48 return redirect(url('hg_home'))
47 49
48 50 if request.POST:
49 51 #import Login Form validator class
50 52 login_form = LoginForm()
51 53 try:
52 54 c.form_result = login_form.to_python(dict(request.POST))
55 if c.came_from:
56 return redirect(c.came_from)
57 else:
53 58 return redirect(url('hg_home'))
54 59
55 60 except formencode.Invalid as errors:
56 61 return htmlfill.render(
57 62 render('/login.html'),
58 63 defaults=errors.value,
59 64 errors=errors.error_dict or {},
60 65 prefix_error=False,
61 66 encoding="UTF-8")
62 67
63 68 return render('/login.html')
64 69
65 70 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
66 71 def register(self):
67 72 user_model = UserModel()
68 73 c.auto_active = False
69 74 for perm in user_model.get_default().user_perms:
70 75 if perm.permission.permission_name == 'hg.register.auto_activate':
71 76 c.auto_active = True
72 77 break
73 78
74 79 if request.POST:
75 80
76 81 register_form = RegisterForm()()
77 82 try:
78 83 form_result = register_form.to_python(dict(request.POST))
79 84 form_result['active'] = c.auto_active
80 85 user_model.create_registration(form_result)
81 86 return redirect(url('login_home'))
82 87
83 88 except formencode.Invalid as errors:
84 89 return htmlfill.render(
85 90 render('/register.html'),
86 91 defaults=errors.value,
87 92 errors=errors.error_dict or {},
88 93 prefix_error=False,
89 94 encoding="UTF-8")
90 95
91 96 return render('/register.html')
92 97
93 98 def logout(self):
94 99 session['hg_app_user'] = AuthUser()
95 100 session.save()
96 101 log.info('Logging out and setting user as Empty')
97 102 redirect(url('hg_home'))
@@ -1,450 +1,454
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 127 return user
128 128
129 129 def fill_perms(user):
130 130 """
131 131 Fills user permission attribute with permissions taken from database
132 132 @param user:
133 133 """
134 134
135 135 sa = meta.Session
136 136 user.permissions['repositories'] = {}
137 137 user.permissions['global'] = set()
138 138
139 139 #===========================================================================
140 140 # fetch default permissions
141 141 #===========================================================================
142 142 default_perms = sa.query(RepoToPerm, Repository, Permission)\
143 143 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
144 144 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
145 145 .filter(RepoToPerm.user == sa.query(User).filter(User.username ==
146 146 'default').scalar()).all()
147 147
148 148 if user.is_admin:
149 149 #=======================================================================
150 150 # #admin have all default rights set to admin
151 151 #=======================================================================
152 152 user.permissions['global'].add('hg.admin')
153 153
154 154 for perm in default_perms:
155 155 p = 'repository.admin'
156 156 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
157 157
158 158 else:
159 159 #=======================================================================
160 160 # set default permissions
161 161 #=======================================================================
162 162
163 163 #default global
164 164 default_global_perms = sa.query(UserToPerm)\
165 165 .filter(UserToPerm.user == sa.query(User).filter(User.username ==
166 166 'default').one())
167 167
168 168 for perm in default_global_perms:
169 169 user.permissions['global'].add(perm.permission.permission_name)
170 170
171 171 #default repositories
172 172 for perm in default_perms:
173 173 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
174 174 #disable defaults for private repos,
175 175 p = 'repository.none'
176 176 elif perm.Repository.user_id == user.user_id:
177 177 #set admin if owner
178 178 p = 'repository.admin'
179 179 else:
180 180 p = perm.Permission.permission_name
181 181
182 182 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
183 183
184 184 #=======================================================================
185 185 # #overwrite default with user permissions if any
186 186 #=======================================================================
187 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 log.debug('redirecting to login page')
232 return redirect(url('login_home'))
231
232 p = request.environ.get('PATH_INFO')
233 if request.environ.get('QUERY_STRING'):
234 p+='?'+request.environ.get('QUERY_STRING')
235 log.debug('redirecting to login page with %',p)
236 return redirect(url('login_home',came_from=p))
233 237
234 238 class PermsDecorator(object):
235 239 """Base class for decorators"""
236 240
237 241 def __init__(self, *required_perms):
238 242 available_perms = config['available_permissions']
239 243 for perm in required_perms:
240 244 if perm not in available_perms:
241 245 raise Exception("'%s' permission is not defined" % perm)
242 246 self.required_perms = set(required_perms)
243 247 self.user_perms = None
244 248
245 249 def __call__(self, func):
246 250 return decorator(self.__wrapper, func)
247 251
248 252
249 253 def __wrapper(self, func, *fargs, **fkwargs):
250 254 # _wrapper.__name__ = func.__name__
251 255 # _wrapper.__dict__.update(func.__dict__)
252 256 # _wrapper.__doc__ = func.__doc__
253 257
254 258 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
255 259 log.debug('checking %s permissions %s for %s',
256 260 self.__class__.__name__, self.required_perms, func.__name__)
257 261
258 262 if self.check_permissions():
259 263 log.debug('Permission granted for %s', func.__name__)
260 264
261 265 return func(*fargs, **fkwargs)
262 266
263 267 else:
264 268 log.warning('Permission denied for %s', func.__name__)
265 269 #redirect with forbidden ret code
266 270 return abort(403)
267 271
268 272
269 273
270 274 def check_permissions(self):
271 275 """Dummy function for overriding"""
272 276 raise Exception('You have to write this function in child class')
273 277
274 278 class HasPermissionAllDecorator(PermsDecorator):
275 279 """Checks for access permission for all given predicates. All of them
276 280 have to be meet in order to fulfill the request
277 281 """
278 282
279 283 def check_permissions(self):
280 284 if self.required_perms.issubset(self.user_perms.get('global')):
281 285 return True
282 286 return False
283 287
284 288
285 289 class HasPermissionAnyDecorator(PermsDecorator):
286 290 """Checks for access permission for any of given predicates. In order to
287 291 fulfill the request any of predicates must be meet
288 292 """
289 293
290 294 def check_permissions(self):
291 295 if self.required_perms.intersection(self.user_perms.get('global')):
292 296 return True
293 297 return False
294 298
295 299 class HasRepoPermissionAllDecorator(PermsDecorator):
296 300 """Checks for access permission for all given predicates for specific
297 301 repository. All of them have to be meet in order to fulfill the request
298 302 """
299 303
300 304 def check_permissions(self):
301 305 repo_name = get_repo_slug(request)
302 306 try:
303 307 user_perms = set([self.user_perms['repositories'][repo_name]])
304 308 except KeyError:
305 309 return False
306 310 if self.required_perms.issubset(user_perms):
307 311 return True
308 312 return False
309 313
310 314
311 315 class HasRepoPermissionAnyDecorator(PermsDecorator):
312 316 """Checks for access permission for any of given predicates for specific
313 317 repository. In order to fulfill the request any of predicates must be meet
314 318 """
315 319
316 320 def check_permissions(self):
317 321 repo_name = get_repo_slug(request)
318 322
319 323 try:
320 324 user_perms = set([self.user_perms['repositories'][repo_name]])
321 325 except KeyError:
322 326 return False
323 327 if self.required_perms.intersection(user_perms):
324 328 return True
325 329 return False
326 330 #===============================================================================
327 331 # CHECK FUNCTIONS
328 332 #===============================================================================
329 333
330 334 class PermsFunction(object):
331 335 """Base function for other check functions"""
332 336
333 337 def __init__(self, *perms):
334 338 available_perms = config['available_permissions']
335 339
336 340 for perm in perms:
337 341 if perm not in available_perms:
338 342 raise Exception("'%s' permission in not defined" % perm)
339 343 self.required_perms = set(perms)
340 344 self.user_perms = None
341 345 self.granted_for = ''
342 346 self.repo_name = None
343 347
344 348 def __call__(self, check_Location=''):
345 349 user = session.get('hg_app_user', False)
346 350 if not user:
347 351 return False
348 352 self.user_perms = user.permissions
349 353 self.granted_for = user.username
350 354 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
351 355
352 356 if self.check_permissions():
353 357 log.debug('Permission granted for %s @%s', self.granted_for,
354 358 check_Location)
355 359 return True
356 360
357 361 else:
358 362 log.warning('Permission denied for %s @%s', self.granted_for,
359 363 check_Location)
360 364 return False
361 365
362 366 def check_permissions(self):
363 367 """Dummy function for overriding"""
364 368 raise Exception('You have to write this function in child class')
365 369
366 370 class HasPermissionAll(PermsFunction):
367 371 def check_permissions(self):
368 372 if self.required_perms.issubset(self.user_perms.get('global')):
369 373 return True
370 374 return False
371 375
372 376 class HasPermissionAny(PermsFunction):
373 377 def check_permissions(self):
374 378 if self.required_perms.intersection(self.user_perms.get('global')):
375 379 return True
376 380 return False
377 381
378 382 class HasRepoPermissionAll(PermsFunction):
379 383
380 384 def __call__(self, repo_name=None, check_Location=''):
381 385 self.repo_name = repo_name
382 386 return super(HasRepoPermissionAll, self).__call__(check_Location)
383 387
384 388 def check_permissions(self):
385 389 if not self.repo_name:
386 390 self.repo_name = get_repo_slug(request)
387 391
388 392 try:
389 393 self.user_perms = set([self.user_perms['repositories']\
390 394 [self.repo_name]])
391 395 except KeyError:
392 396 return False
393 397 self.granted_for = self.repo_name
394 398 if self.required_perms.issubset(self.user_perms):
395 399 return True
396 400 return False
397 401
398 402 class HasRepoPermissionAny(PermsFunction):
399 403
400 404 def __call__(self, repo_name=None, check_Location=''):
401 405 self.repo_name = repo_name
402 406 return super(HasRepoPermissionAny, self).__call__(check_Location)
403 407
404 408 def check_permissions(self):
405 409 if not self.repo_name:
406 410 self.repo_name = get_repo_slug(request)
407 411
408 412 try:
409 413 self.user_perms = set([self.user_perms['repositories']\
410 414 [self.repo_name]])
411 415 except KeyError:
412 416 return False
413 417 self.granted_for = self.repo_name
414 418 if self.required_perms.intersection(self.user_perms):
415 419 return True
416 420 return False
417 421
418 422 #===============================================================================
419 423 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
420 424 #===============================================================================
421 425
422 426 class HasPermissionAnyMiddleware(object):
423 427 def __init__(self, *perms):
424 428 self.required_perms = set(perms)
425 429
426 430 def __call__(self, user, repo_name):
427 431 usr = AuthUser()
428 432 usr.user_id = user.user_id
429 433 usr.username = user.username
430 434 usr.is_admin = user.admin
431 435
432 436 try:
433 437 self.user_perms = set([fill_perms(usr)\
434 438 .permissions['repositories'][repo_name]])
435 439 except:
436 440 self.user_perms = set()
437 441 self.granted_for = ''
438 442 self.username = user.username
439 443 self.repo_name = repo_name
440 444 return self.check_permissions()
441 445
442 446 def check_permissions(self):
443 447 log.debug('checking mercurial protocol '
444 448 'permissions for user:%s repository:%s',
445 449 self.username, self.repo_name)
446 450 if self.required_perms.intersection(self.user_perms):
447 451 log.debug('permission granted')
448 452 return True
449 453 log.debug('permission denied')
450 454 return False
@@ -1,78 +1,78
1 1 ## -*- coding: utf-8 -*-
2 2 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
3 3 <html xmlns="http://www.w3.org/1999/xhtml" id="mainhtml">
4 4 <head>
5 5 <title>${_('Sign In to hg-app')}</title>
6 6 <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
7 7 <link rel="icon" href="/images/hgicon.png" type="image/png" />
8 8 <meta name="robots" content="index, nofollow"/>
9 9
10 10 <!-- stylesheets -->
11 11 <link rel="stylesheet" type="text/css" href="/css/reset.css" />
12 12 <link rel="stylesheet" type="text/css" href="/css/style.css" media="screen" />
13 13 <link id="color" rel="stylesheet" type="text/css" href="/css/colors/blue.css" />
14 14
15 15 <!-- scripts -->
16 16
17 17 </head>
18 18 <body>
19 19 <div id="login">
20 20 <!-- login -->
21 21 <div class="title">
22 22 <h5>${_('Sign In to hg-app')}</h5>
23 23 <div class="corner tl"></div>
24 24 <div class="corner tr"></div>
25 25 </div>
26 26 <div class="inner">
27 ${h.form(h.url.current())}
27 ${h.form(h.url.current(came_from=c.came_from))}
28 28 <div class="form">
29 29 <!-- fields -->
30 30
31 31 <div class="fields">
32 32 <div class="field">
33 33 <div class="label">
34 34 <label for="username">${_('Username')}:</label>
35 35 </div>
36 36 <div class="input">
37 37 ${h.text('username',class_='focus',size=40)}
38 38 </div>
39 39
40 40 </div>
41 41 <div class="field">
42 42 <div class="label">
43 43 <label for="password">${_('Password')}:</label>
44 44 </div>
45 45 <div class="input">
46 46 ${h.password('password',class_='focus',size=40)}
47 47 </div>
48 48
49 49 </div>
50 50 ##<div class="field">
51 51 ## <div class="checkbox">
52 52 ## <input type="checkbox" id="remember" name="remember" />
53 53 ## <label for="remember">Remember me</label>
54 54 ## </div>
55 55 ##</div>
56 56 <div class="buttons">
57 57 ${h.submit('sign_in','Sign In',class_="ui-button ui-widget ui-state-default ui-corner-all")}
58 58 </div>
59 59 </div>
60 60 <!-- end fields -->
61 61 <!-- links -->
62 62 <div class="links">
63 63 ${h.link_to(_('Forgot your password ?'),h.url('#'))}
64 64 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
65 65 /
66 66 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
67 67 %endif
68 68 </div>
69 69
70 70 <!-- end links -->
71 71 </div>
72 72 ${h.end_form()}
73 73 </div>
74 74 <!-- end login -->
75 75 </div>
76 76 </body>
77 77 </html>
78 78
General Comments 0
You need to be logged in to leave comments. Login now