##// END OF EJS Templates
some extra checks for auth lib
marcink -
r339:5d517bbf default
parent child Browse files
Show More
@@ -1,387 +1,399 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
26 26 from functools import wraps
27 27 from pylons import session, url, request
28 28 from pylons.controllers.util import abort, redirect
29 29 from pylons_app.model import meta
30 30 from pylons_app.model.db import User, Repo2Perm, Repository, Permission
31 31 from pylons_app.lib.utils import get_repo_slug
32 32 from sqlalchemy.exc import OperationalError
33 33 from sqlalchemy.orm.exc import NoResultFound, MultipleResultsFound
34 34 import crypt
35 35 import logging
36 36 from pylons import config
37 37 log = logging.getLogger(__name__)
38 38
39 39 def get_crypt_password(password):
40 40 """
41 41 Cryptographic function used for password hashing
42 42 @param password: password to hash
43 43 """
44 44 return crypt.crypt(password, '6a')
45 45
46 46 def authfunc(environ, username, password):
47 47 sa = meta.Session
48 48 password_crypt = get_crypt_password(password)
49 49 try:
50 50 user = sa.query(User).filter(User.username == username).one()
51 51 except (NoResultFound, MultipleResultsFound, OperationalError) as e:
52 52 log.error(e)
53 53 user = None
54 54
55 55 if user:
56 56 if user.active:
57 57 if user.username == username and user.password == password_crypt:
58 58 log.info('user %s authenticated correctly', username)
59 59 return True
60 60 else:
61 61 log.error('user %s is disabled', username)
62 62
63 63 return False
64 64
65 65 class AuthUser(object):
66 66 """
67 67 A simple object that handles a mercurial username for authentication
68 68 """
69 69 def __init__(self):
70 70 self.username = 'None'
71 71 self.user_id = None
72 72 self.is_authenticated = False
73 73 self.is_admin = False
74 74 self.permissions = {}
75 75
76 76
77 77 def set_available_permissions(config):
78 78 """
79 79 This function will propagate pylons globals with all available defined
80 80 permission given in db. We don't wannt to check each time from db for new
81 81 permissions since adding a new permission also requires application restart
82 82 ie. to decorate new views with the newly created permission
83 83 @param config:
84 84 """
85 85 log.info('getting information about all available permissions')
86 86 sa = meta.Session
87 87 all_perms = sa.query(Permission).all()
88 88 config['available_permissions'] = [x.permission_name for x in all_perms]
89 89
90 90 def set_base_path(config):
91 91 config['base_path'] = config['pylons.app_globals'].base_path
92 92
93 93 def fill_perms(user):
94 94 sa = meta.Session
95 95 user.permissions['repositories'] = {}
96 96
97 97 #first fetch default permissions
98 98 default_perms = sa.query(Repo2Perm, Repository, Permission)\
99 99 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
100 100 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
101 101 .filter(Repo2Perm.user_id == sa.query(User).filter(User.username ==
102 102 'default').one().user_id).all()
103 103
104 104 if user.is_admin:
105 105 user.permissions['global'] = set(['hg.admin'])
106 106 #admin have all rights full
107 107 for perm in default_perms:
108 108 p = 'repository.admin'
109 109 user.permissions['repositories'][perm.Repo2Perm.repository] = p
110 110
111 111 else:
112 112 user.permissions['global'] = set()
113 113 for perm in default_perms:
114 114 if perm.Repository.private:
115 115 #disable defaults for private repos,
116 116 p = 'repository.none'
117 117 elif perm.Repository.user_id == user.user_id:
118 118 #set admin if owner
119 119 p = 'repository.admin'
120 120 else:
121 121 p = perm.Permission.permission_name
122 122
123 123 user.permissions['repositories'][perm.Repo2Perm.repository] = p
124 124
125 125
126 126 user_perms = sa.query(Repo2Perm, Permission, Repository)\
127 127 .join((Repository, Repo2Perm.repository == Repository.repo_name))\
128 128 .join((Permission, Repo2Perm.permission_id == Permission.permission_id))\
129 129 .filter(Repo2Perm.user_id == user.user_id).all()
130 130 #overwrite userpermissions with defaults
131 131 for perm in user_perms:
132 132 #set write if owner
133 133 if perm.Repository.user_id == user.user_id:
134 134 p = 'repository.write'
135 135 else:
136 136 p = perm.Permission.permission_name
137 137 user.permissions['repositories'][perm.Repo2Perm.repository] = p
138 138 return user
139 139
140 140 def get_user(session):
141 141 """
142 142 Gets user from session, and wraps permissions into user
143 143 @param session:
144 144 """
145 145 user = session.get('hg_app_user', AuthUser())
146 146
147 147 if user.is_authenticated:
148 148 user = fill_perms(user)
149 149
150 150 session['hg_app_user'] = user
151 151 session.save()
152 152 return user
153 153
154 154 #===============================================================================
155 155 # CHECK DECORATORS
156 156 #===============================================================================
157 157 class LoginRequired(object):
158 158 """
159 159 Must be logged in to execute this function else redirect to login page
160 160 """
161 161
162 162 def __call__(self, func):
163 163 @wraps(func)
164 164 def _wrapper(*fargs, **fkwargs):
165 165 user = session.get('hg_app_user', AuthUser())
166 166 log.debug('Checking login required for user:%s', user.username)
167 167 if user.is_authenticated:
168 168 log.debug('user %s is authenticated', user.username)
169 169 func(*fargs)
170 170 else:
171 171 log.warn('user %s not authenticated', user.username)
172 172 log.debug('redirecting to login page')
173 173 return redirect(url('login_home'))
174 174
175 175 return _wrapper
176 176
177 177 class PermsDecorator(object):
178 178 """
179 179 Base class for decorators
180 180 """
181 181
182 182 def __init__(self, *required_perms):
183 183 available_perms = config['available_permissions']
184 184 for perm in required_perms:
185 185 if perm not in available_perms:
186 186 raise Exception("'%s' permission is not defined" % perm)
187 187 self.required_perms = set(required_perms)
188 188 self.user_perms = None
189 189
190 190 def __call__(self, func):
191 191 @wraps(func)
192 192 def _wrapper(*fargs, **fkwargs):
193 193 self.user_perms = session.get('hg_app_user', AuthUser()).permissions
194 194 log.debug('checking %s permissions %s for %s',
195 195 self.__class__.__name__, self.required_perms, func.__name__)
196 196
197 197 if self.check_permissions():
198 198 log.debug('Permission granted for %s', func.__name__)
199 199 return func(*fargs)
200 200
201 201 else:
202 202 log.warning('Permission denied for %s', func.__name__)
203 203 #redirect with forbidden ret code
204 204 return abort(403)
205 205 return _wrapper
206 206
207 207
208 208 def check_permissions(self):
209 209 """
210 210 Dummy function for overriding
211 211 """
212 212 raise Exception('You have to write this function in child class')
213 213
214 214 class HasPermissionAllDecorator(PermsDecorator):
215 215 """
216 216 Checks for access permission for all given predicates. All of them have to
217 217 be meet in order to fulfill the request
218 218 """
219 219
220 220 def check_permissions(self):
221 if self.required_perms.issubset(self.user_perms['global']):
221 if self.required_perms.issubset(self.user_perms.get('global')):
222 222 return True
223 223 return False
224 224
225 225
226 226 class HasPermissionAnyDecorator(PermsDecorator):
227 227 """
228 228 Checks for access permission for any of given predicates. In order to
229 229 fulfill the request any of predicates must be meet
230 230 """
231 231
232 232 def check_permissions(self):
233 if self.required_perms.intersection(self.user_perms['global']):
233 if self.required_perms.intersection(self.user_perms.get('global')):
234 234 return True
235 235 return False
236 236
237 237 class HasRepoPermissionAllDecorator(PermsDecorator):
238 238 """
239 239 Checks for access permission for all given predicates for specific
240 240 repository. All of them have to be meet in order to fulfill the request
241 241 """
242 242
243 243 def check_permissions(self):
244 244 repo_name = get_repo_slug(request)
245 try:
245 246 user_perms = set([self.user_perms['repositories'][repo_name]])
247 except KeyError:
248 return False
246 249 if self.required_perms.issubset(user_perms):
247 250 return True
248 251 return False
249 252
250 253
251 254 class HasRepoPermissionAnyDecorator(PermsDecorator):
252 255 """
253 256 Checks for access permission for any of given predicates for specific
254 257 repository. In order to fulfill the request any of predicates must be meet
255 258 """
256 259
257 260 def check_permissions(self):
258 261 repo_name = get_repo_slug(request)
259 262
263 try:
260 264 user_perms = set([self.user_perms['repositories'][repo_name]])
265 except KeyError:
266 return False
261 267 if self.required_perms.intersection(user_perms):
262 268 return True
263 269 return False
264 270 #===============================================================================
265 271 # CHECK FUNCTIONS
266 272 #===============================================================================
267 273
268 274 class PermsFunction(object):
269 275 """
270 276 Base function for other check functions
271 277 """
272 278
273 279 def __init__(self, *perms):
274 280 available_perms = config['available_permissions']
275 281
276 282 for perm in perms:
277 283 if perm not in available_perms:
278 284 raise Exception("'%s' permission in not defined" % perm)
279 285 self.required_perms = set(perms)
280 286 self.user_perms = None
281 287 self.granted_for = ''
282 288 self.repo_name = None
283 289
284 290 def __call__(self, check_Location=''):
285 291 user = session.get('hg_app_user', False)
286 292 if not user:
287 293 return False
288 294 self.user_perms = user.permissions
289 295 self.granted_for = user.username
290 296 log.debug('checking %s %s', self.__class__.__name__, self.required_perms)
291 297
292 298 if self.check_permissions():
293 299 log.debug('Permission granted for %s @%s', self.granted_for,
294 300 check_Location)
295 301 return True
296 302
297 303 else:
298 304 log.warning('Permission denied for %s @%s', self.granted_for,
299 305 check_Location)
300 306 return False
301 307
302 308 def check_permissions(self):
303 309 """
304 310 Dummy function for overriding
305 311 """
306 312 raise Exception('You have to write this function in child class')
307 313
308 314 class HasPermissionAll(PermsFunction):
309 315 def check_permissions(self):
310 if self.required_perms.issubset(self.user_perms['global']):
316 if self.required_perms.issubset(self.user_perms.get('global')):
311 317 return True
312 318 return False
313 319
314 320 class HasPermissionAny(PermsFunction):
315 321 def check_permissions(self):
316 if self.required_perms.intersection(self.user_perms['global']):
322 if self.required_perms.intersection(self.user_perms.get('global')):
317 323 return True
318 324 return False
319 325
320 326 class HasRepoPermissionAll(PermsFunction):
321 327
322 328 def __call__(self, repo_name=None, check_Location=''):
323 329 self.repo_name = repo_name
324 330 return super(HasRepoPermissionAll, self).__call__(check_Location)
325 331
326 332 def check_permissions(self):
327 333 if not self.repo_name:
328 334 self.repo_name = get_repo_slug(request)
329 335
336 try:
330 337 self.user_perms = set([self.user_perms['repositories']\
331 .get(self.repo_name)])
338 [self.repo_name]])
339 except KeyError:
340 return False
332 341 self.granted_for = self.repo_name
333 342 if self.required_perms.issubset(self.user_perms):
334 343 return True
335 344 return False
336 345
337 346 class HasRepoPermissionAny(PermsFunction):
338 347
339 348
340 349 def __call__(self, repo_name=None, check_Location=''):
341 350 self.repo_name = repo_name
342 351 return super(HasRepoPermissionAny, self).__call__(check_Location)
343 352
344 353 def check_permissions(self):
345 354 if not self.repo_name:
346 355 self.repo_name = get_repo_slug(request)
347 356
357 try:
348 358 self.user_perms = set([self.user_perms['repositories']\
349 .get(self.repo_name)])
359 [self.repo_name]])
360 except KeyError:
361 return False
350 362 self.granted_for = self.repo_name
351 363 if self.required_perms.intersection(self.user_perms):
352 364 return True
353 365 return False
354 366
355 367 #===============================================================================
356 368 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
357 369 #===============================================================================
358 370
359 371 class HasPermissionAnyMiddleware(object):
360 372 def __init__(self, *perms):
361 373 self.required_perms = set(perms)
362 374
363 375 def __call__(self, user, repo_name):
364 376 usr = AuthUser()
365 377 usr.user_id = user.user_id
366 378 usr.username = user.username
367 379 usr.is_admin = user.admin
368 380
369 381 try:
370 382 self.user_perms = set([fill_perms(usr)\
371 383 .permissions['repositories'][repo_name]])
372 384 except:
373 385 self.user_perms = set()
374 386 self.granted_for = ''
375 387 self.username = user.username
376 388 self.repo_name = repo_name
377 389 return self.check_permissions()
378 390
379 391 def check_permissions(self):
380 392 log.debug('checking mercurial protocol '
381 393 'permissions for user:%s repository:%s',
382 394 self.username, self.repo_name)
383 395 if self.required_perms.intersection(self.user_perms):
384 396 log.debug('permission granted')
385 397 return True
386 398 log.debug('permission denied')
387 399 return False
General Comments 0
You need to be logged in to leave comments. Login now