##// END OF EJS Templates
Added some more details into user edit permissions view
marcink -
r895:62c04c5c beta
parent child Browse files
Show More
@@ -1,172 +1,176 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.admin.users
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Users crud controller for pylons
7 7
8 8 :created_on: Apr 4, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27
28 28 import logging
29 29 import traceback
30 30 import formencode
31 31
32 32 from formencode import htmlfill
33 from pylons import request, session, tmpl_context as c, url
33 from pylons import request, session, tmpl_context as c, url, config
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 from rhodecode.lib.exceptions import *
37 from rhodecode.lib.exceptions import DefaultUserException, UserOwnsReposException
38 38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 fill_perms
40 41 from rhodecode.lib.base import BaseController, render
41 42
42 43 from rhodecode.model.db import User
43 44 from rhodecode.model.forms import UserForm
44 45 from rhodecode.model.user import UserModel
45 46
46 47 log = logging.getLogger(__name__)
47 48
48 49 class UsersController(BaseController):
49 50 """REST Controller styled on the Atom Publishing Protocol"""
50 51 # To properly map this controller, ensure your config/routing.py
51 52 # file has a resource setup:
52 53 # map.resource('user', 'users')
53 54
54 55 @LoginRequired()
55 56 @HasPermissionAllDecorator('hg.admin')
56 57 def __before__(self):
57 58 c.admin_user = session.get('admin_user')
58 59 c.admin_username = session.get('admin_username')
59 60 super(UsersController, self).__before__()
60
61 c.available_permissions = config['available_permissions']
61 62
62 63 def index(self, format='html'):
63 64 """GET /users: All items in the collection"""
64 65 # url('users')
65 66
66 67 c.users_list = self.sa.query(User).all()
67 68 return render('admin/users/users.html')
68 69
69 70 def create(self):
70 71 """POST /users: Create a new item"""
71 72 # url('users')
72 73
73 74 user_model = UserModel()
74 75 login_form = UserForm()()
75 76 try:
76 77 form_result = login_form.to_python(dict(request.POST))
77 78 user_model.create(form_result)
78 79 h.flash(_('created user %s') % form_result['username'],
79 80 category='success')
80 81 #action_logger(self.rhodecode_user, 'new_user', '', '', self.sa)
81 82 except formencode.Invalid, errors:
82 83 return htmlfill.render(
83 84 render('admin/users/user_add.html'),
84 85 defaults=errors.value,
85 86 errors=errors.error_dict or {},
86 87 prefix_error=False,
87 88 encoding="UTF-8")
88 89 except Exception:
89 90 log.error(traceback.format_exc())
90 91 h.flash(_('error occurred during creation of user %s') \
91 92 % request.POST.get('username'), category='error')
92 93 return redirect(url('users'))
93 94
94 95 def new(self, format='html'):
95 96 """GET /users/new: Form to create a new item"""
96 97 # url('new_user')
97 98 return render('admin/users/user_add.html')
98 99
99 100 def update(self, id):
100 101 """PUT /users/id: Update an existing item"""
101 102 # Forms posted to this method should contain a hidden field:
102 103 # <input type="hidden" name="_method" value="PUT" />
103 104 # Or using helpers:
104 105 # h.form(url('user', id=ID),
105 106 # method='put')
106 107 # url('user', id=ID)
107 108 user_model = UserModel()
108 109 c.user = user_model.get(id)
109 110
110 111 _form = UserForm(edit=True, old_data={'user_id':id,
111 112 'email':c.user.email})()
112 113 form_result = {}
113 114 try:
114 115 form_result = _form.to_python(dict(request.POST))
115 116 user_model.update(id, form_result)
116 117 h.flash(_('User updated succesfully'), category='success')
117 118
118 119 except formencode.Invalid, errors:
119 120 return htmlfill.render(
120 121 render('admin/users/user_edit.html'),
121 122 defaults=errors.value,
122 123 errors=errors.error_dict or {},
123 124 prefix_error=False,
124 125 encoding="UTF-8")
125 126 except Exception:
126 127 log.error(traceback.format_exc())
127 128 h.flash(_('error occurred during update of user %s') \
128 129 % form_result.get('username'), category='error')
129 130
130 131 return redirect(url('users'))
131 132
132 133 def delete(self, id):
133 134 """DELETE /users/id: Delete an existing item"""
134 135 # Forms posted to this method should contain a hidden field:
135 136 # <input type="hidden" name="_method" value="DELETE" />
136 137 # Or using helpers:
137 138 # h.form(url('user', id=ID),
138 139 # method='delete')
139 140 # url('user', id=ID)
140 141 user_model = UserModel()
141 142 try:
142 143 user_model.delete(id)
143 h.flash(_('sucessfully deleted user'), category='success')
144 h.flash(_('successfully deleted user'), category='success')
144 145 except (UserOwnsReposException, DefaultUserException), e:
145 146 h.flash(str(e), category='warning')
146 147 except Exception:
147 148 h.flash(_('An error occurred during deletion of user'),
148 149 category='error')
149 150 return redirect(url('users'))
150 151
151 152 def show(self, id, format='html'):
152 153 """GET /users/id: Show a specific item"""
153 154 # url('user', id=ID)
154 155
155 156
156 157 def edit(self, id, format='html'):
157 158 """GET /users/id/edit: Form to edit an existing item"""
158 159 # url('edit_user', id=ID)
159 160 c.user = self.sa.query(User).get(id)
160 161 if not c.user:
161 162 return redirect(url('users'))
162 163 if c.user.username == 'default':
163 164 h.flash(_("You can't edit this user"), category='warning')
164 165 return redirect(url('users'))
166 c.user.permissions = {}
167 c.granted_permissions = fill_perms(c.user).permissions['global']
165 168
166 169 defaults = c.user.get_dict()
170
167 171 return htmlfill.render(
168 172 render('admin/users/user_edit.html'),
169 173 defaults=defaults,
170 174 encoding="UTF-8",
171 175 force_defaults=False
172 176 )
@@ -1,569 +1,577 b''
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # authentication and permission libraries
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
1 # -*- coding: utf-8 -*-
2 """
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
5
6 authentication and permission libraries
7
8 :created_on: Apr 4, 2010
9 :copyright: (c) 2010 by marcink.
10 :license: LICENSE_NAME, see LICENSE_FILE for more details.
11 """
6 12 # This program is free software; you can redistribute it and/or
7 13 # modify it under the terms of the GNU General Public License
8 14 # as published by the Free Software Foundation; version 2
9 15 # of the License or (at your opinion) any later version of the license.
10 16 #
11 17 # This program is distributed in the hope that it will be useful,
12 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 20 # GNU General Public License for more details.
15 21 #
16 22 # You should have received a copy of the GNU General Public License
17 23 # along with this program; if not, write to the Free Software
18 24 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 25 # MA 02110-1301, USA.
20 """
21 Created on April 4, 2010
22 26
23 @author: marcink
24 """
27 import bcrypt
28 import random
29 import logging
30 import traceback
31
32 from decorator import decorator
33
25 34 from pylons import config, session, url, request
26 35 from pylons.controllers.util import abort, redirect
27 from rhodecode.lib.exceptions import *
36
37 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
28 38 from rhodecode.lib.utils import get_repo_slug
29 39 from rhodecode.lib.auth_ldap import AuthLdap
40
30 41 from rhodecode.model import meta
31 42 from rhodecode.model.user import UserModel
32 from rhodecode.model.caching_query import FromCache
33 43 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
34 44 UserToPerm
35 import bcrypt
36 from decorator import decorator
37 import logging
38 import random
39 import traceback
45
40 46
41 47 log = logging.getLogger(__name__)
42 48
43 49 class PasswordGenerator(object):
44 50 """This is a simple class for generating password from
45 51 different sets of characters
46 52 usage:
47 53 passwd_gen = PasswordGenerator()
48 54 #print 8-letter password containing only big and small letters of alphabet
49 55 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
50 56 """
51 57 ALPHABETS_NUM = r'''1234567890'''#[0]
52 58 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''#[1]
53 59 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''#[2]
54 60 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?''' #[3]
55 61 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM + ALPHABETS_SPECIAL#[4]
56 62 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM#[5]
57 63 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
58 64 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM#[6]
59 65 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM#[7]
60 66
61 67 def __init__(self, passwd=''):
62 68 self.passwd = passwd
63 69
64 70 def gen_password(self, len, type):
65 71 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
66 72 return self.passwd
67 73
68 74
69 75 def get_crypt_password(password):
70 76 """Cryptographic function used for password hashing based on sha1
71 77 :param password: password to hash
72 78 """
73 79 return bcrypt.hashpw(password, bcrypt.gensalt(10))
74 80
75 81 def check_password(password, hashed):
76 82 return bcrypt.hashpw(password, hashed) == hashed
77 83
78 84 def authfunc(environ, username, password):
79 85 """
80 86 Dummy authentication function used in Mercurial/Git/ and access control,
81 87
82 88 :param environ: needed only for using in Basic auth
83 89 """
84 90 return authenticate(username, password)
85 91
86 92
87 93 def authenticate(username, password):
88 94 """
89 95 Authentication function used for access control,
90 96 firstly checks for db authentication then if ldap is enabled for ldap
91 97 authentication, also creates ldap user if not in database
92 98
93 99 :param username: username
94 100 :param password: password
95 101 """
96 102 user_model = UserModel()
97 103 user = user_model.get_by_username(username, cache=False)
98 104
99 105 log.debug('Authenticating user using RhodeCode account')
100 106 if user is not None and user.is_ldap is False:
101 107 if user.active:
102 108
103 109 if user.username == 'default' and user.active:
104 110 log.info('user %s authenticated correctly as anonymous user',
105 111 username)
106 112 return True
107 113
108 114 elif user.username == username and check_password(password, user.password):
109 115 log.info('user %s authenticated correctly', username)
110 116 return True
111 117 else:
112 118 log.warning('user %s is disabled', username)
113 119
114 120 else:
115 121 log.debug('Regular authentication failed')
116 122 user_obj = user_model.get_by_username(username, cache=False,
117 123 case_insensitive=True)
118 124
119 125 if user_obj is not None and user_obj.is_ldap is False:
120 126 log.debug('this user already exists as non ldap')
121 127 return False
122 128
123 129 from rhodecode.model.settings import SettingsModel
124 130 ldap_settings = SettingsModel().get_ldap_settings()
125 131
126 132 #======================================================================
127 133 # FALLBACK TO LDAP AUTH IN ENABLE
128 134 #======================================================================
129 135 if ldap_settings.get('ldap_active', False):
130 136 log.debug("Authenticating user using ldap")
131 137 kwargs = {
132 138 'server':ldap_settings.get('ldap_host', ''),
133 139 'base_dn':ldap_settings.get('ldap_base_dn', ''),
134 140 'port':ldap_settings.get('ldap_port'),
135 141 'bind_dn':ldap_settings.get('ldap_dn_user'),
136 142 'bind_pass':ldap_settings.get('ldap_dn_pass'),
137 143 'use_ldaps':ldap_settings.get('ldap_ldaps'),
138 144 'ldap_version':3,
139 145 }
140 146 log.debug('Checking for ldap authentication')
141 147 try:
142 148 aldap = AuthLdap(**kwargs)
143 149 res = aldap.authenticate_ldap(username, password)
144 150 log.debug('Got ldap response %s', res)
145 151
146 152 if user_model.create_ldap(username, password):
147 153 log.info('created new ldap user')
148 154
149 155 return True
150 156 except (LdapUsernameError, LdapPasswordError,):
151 157 pass
152 158 except (Exception,):
153 159 log.error(traceback.format_exc())
154 160 pass
155 161 return False
156 162
157 163 class AuthUser(object):
158 164 """
159 165 A simple object that handles a mercurial username for authentication
160 166 """
161 167 def __init__(self):
162 168 self.username = 'None'
163 169 self.name = ''
164 170 self.lastname = ''
165 171 self.email = ''
166 172 self.user_id = None
167 173 self.is_authenticated = False
168 174 self.is_admin = False
169 175 self.permissions = {}
170 176
171 177 def __repr__(self):
172 178 return "<AuthUser('id:%s:%s')>" % (self.user_id, self.username)
173 179
174 180 def set_available_permissions(config):
175 """
176 This function will propagate pylons globals with all available defined
181 """This function will propagate pylons globals with all available defined
177 182 permission given in db. We don't wannt to check each time from db for new
178 183 permissions since adding a new permission also requires application restart
179 184 ie. to decorate new views with the newly created permission
180 :param config:
185
186 :param config: current pylons config instance
187
181 188 """
182 189 log.info('getting information about all available permissions')
183 190 try:
184 191 sa = meta.Session()
185 192 all_perms = sa.query(Permission).all()
186 193 except:
187 194 pass
188 195 finally:
189 196 meta.Session.remove()
190 197
191 198 config['available_permissions'] = [x.permission_name for x in all_perms]
192 199
193 200 def set_base_path(config):
194 201 config['base_path'] = config['pylons.app_globals'].base_path
195 202
196 203
197 204 def fill_perms(user):
198 """
199 Fills user permission attribute with permissions taken from database
205 """Fills user permission attribute with permissions taken from database
206
200 207 :param user:
208
201 209 """
202 210
203 211 sa = meta.Session()
204 212 user.permissions['repositories'] = {}
205 213 user.permissions['global'] = set()
206 214
207 215 #===========================================================================
208 216 # fetch default permissions
209 217 #===========================================================================
210 218 default_user = UserModel().get_by_username('default', cache=True)
211 219
212 220 default_perms = sa.query(RepoToPerm, Repository, Permission)\
213 221 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
214 222 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
215 223 .filter(RepoToPerm.user == default_user).all()
216 224
217 225 if user.is_admin:
218 226 #=======================================================================
219 227 # #admin have all default rights set to admin
220 228 #=======================================================================
221 229 user.permissions['global'].add('hg.admin')
222 230
223 231 for perm in default_perms:
224 232 p = 'repository.admin'
225 233 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
226 234
227 235 else:
228 236 #=======================================================================
229 237 # set default permissions
230 238 #=======================================================================
231 239
232 240 #default global
233 241 default_global_perms = sa.query(UserToPerm)\
234 242 .filter(UserToPerm.user == sa.query(User)\
235 243 .filter(User.username == 'default').one())
236 244
237 245 for perm in default_global_perms:
238 246 user.permissions['global'].add(perm.permission.permission_name)
239 247
240 248 #default repositories
241 249 for perm in default_perms:
242 250 if perm.Repository.private and not perm.Repository.user_id == user.user_id:
243 251 #disable defaults for private repos,
244 252 p = 'repository.none'
245 253 elif perm.Repository.user_id == user.user_id:
246 254 #set admin if owner
247 255 p = 'repository.admin'
248 256 else:
249 257 p = perm.Permission.permission_name
250 258
251 259 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
252 260
253 261 #=======================================================================
254 262 # #overwrite default with user permissions if any
255 263 #=======================================================================
256 264 user_perms = sa.query(RepoToPerm, Permission, Repository)\
257 265 .join((Repository, RepoToPerm.repository_id == Repository.repo_id))\
258 266 .join((Permission, RepoToPerm.permission_id == Permission.permission_id))\
259 267 .filter(RepoToPerm.user_id == user.user_id).all()
260 268
261 269 for perm in user_perms:
262 270 if perm.Repository.user_id == user.user_id:#set admin if owner
263 271 p = 'repository.admin'
264 272 else:
265 273 p = perm.Permission.permission_name
266 274 user.permissions['repositories'][perm.RepoToPerm.repository.repo_name] = p
267 275 meta.Session.remove()
268 276 return user
269 277
270 278 def get_user(session):
271 279 """
272 280 Gets user from session, and wraps permissions into user
273 281 :param session:
274 282 """
275 283 user = session.get('rhodecode_user', AuthUser())
276 284 #if the user is not logged in we check for anonymous access
277 285 #if user is logged and it's a default user check if we still have anonymous
278 286 #access enabled
279 287 if user.user_id is None or user.username == 'default':
280 288 anonymous_user = UserModel().get_by_username('default', cache=True)
281 289 if anonymous_user.active is True:
282 290 #then we set this user is logged in
283 291 user.is_authenticated = True
284 292 user.user_id = anonymous_user.user_id
285 293 else:
286 294 user.is_authenticated = False
287 295
288 296 if user.is_authenticated:
289 297 user = UserModel().fill_data(user)
290 298
291 299 user = fill_perms(user)
292 300 session['rhodecode_user'] = user
293 301 session.save()
294 302 return user
295 303
296 304 #===============================================================================
297 305 # CHECK DECORATORS
298 306 #===============================================================================
299 307 class LoginRequired(object):
300 308 """Must be logged in to execute this function else
301 309 redirect to login page"""
302 310
303 311 def __call__(self, func):
304 312 return decorator(self.__wrapper, func)
305 313
306 314 def __wrapper(self, func, *fargs, **fkwargs):
307 315 user = session.get('rhodecode_user', AuthUser())
308 316 log.debug('Checking login required for user:%s', user.username)
309 317 if user.is_authenticated:
310 318 log.debug('user %s is authenticated', user.username)
311 319 return func(*fargs, **fkwargs)
312 320 else:
313 321 log.warn('user %s not authenticated', user.username)
314 322
315 323 p = ''
316 324 if request.environ.get('SCRIPT_NAME') != '/':
317 325 p += request.environ.get('SCRIPT_NAME')
318 326
319 327 p += request.environ.get('PATH_INFO')
320 328 if request.environ.get('QUERY_STRING'):
321 329 p += '?' + request.environ.get('QUERY_STRING')
322 330
323 331 log.debug('redirecting to login page with %s', p)
324 332 return redirect(url('login_home', came_from=p))
325 333
326 334 class NotAnonymous(object):
327 335 """Must be logged in to execute this function else
328 336 redirect to login page"""
329 337
330 338 def __call__(self, func):
331 339 return decorator(self.__wrapper, func)
332 340
333 341 def __wrapper(self, func, *fargs, **fkwargs):
334 342 user = session.get('rhodecode_user', AuthUser())
335 343 log.debug('Checking if user is not anonymous')
336 344
337 345 anonymous = user.username == 'default'
338 346
339 347 if anonymous:
340 348 p = ''
341 349 if request.environ.get('SCRIPT_NAME') != '/':
342 350 p += request.environ.get('SCRIPT_NAME')
343 351
344 352 p += request.environ.get('PATH_INFO')
345 353 if request.environ.get('QUERY_STRING'):
346 354 p += '?' + request.environ.get('QUERY_STRING')
347 355 return redirect(url('login_home', came_from=p))
348 356 else:
349 357 return func(*fargs, **fkwargs)
350 358
351 359 class PermsDecorator(object):
352 360 """Base class for decorators"""
353 361
354 362 def __init__(self, *required_perms):
355 363 available_perms = config['available_permissions']
356 364 for perm in required_perms:
357 365 if perm not in available_perms:
358 366 raise Exception("'%s' permission is not defined" % perm)
359 367 self.required_perms = set(required_perms)
360 368 self.user_perms = None
361 369
362 370 def __call__(self, func):
363 371 return decorator(self.__wrapper, func)
364 372
365 373
366 374 def __wrapper(self, func, *fargs, **fkwargs):
367 375 # _wrapper.__name__ = func.__name__
368 376 # _wrapper.__dict__.update(func.__dict__)
369 377 # _wrapper.__doc__ = func.__doc__
370 378 self.user = session.get('rhodecode_user', AuthUser())
371 379 self.user_perms = self.user.permissions
372 380 log.debug('checking %s permissions %s for %s %s',
373 381 self.__class__.__name__, self.required_perms, func.__name__,
374 382 self.user)
375 383
376 384 if self.check_permissions():
377 385 log.debug('Permission granted for %s %s', func.__name__, self.user)
378 386
379 387 return func(*fargs, **fkwargs)
380 388
381 389 else:
382 390 log.warning('Permission denied for %s %s', func.__name__, self.user)
383 391 #redirect with forbidden ret code
384 392 return abort(403)
385 393
386 394
387 395
388 396 def check_permissions(self):
389 397 """Dummy function for overriding"""
390 398 raise Exception('You have to write this function in child class')
391 399
392 400 class HasPermissionAllDecorator(PermsDecorator):
393 401 """Checks for access permission for all given predicates. All of them
394 402 have to be meet in order to fulfill the request
395 403 """
396 404
397 405 def check_permissions(self):
398 406 if self.required_perms.issubset(self.user_perms.get('global')):
399 407 return True
400 408 return False
401 409
402 410
403 411 class HasPermissionAnyDecorator(PermsDecorator):
404 412 """Checks for access permission for any of given predicates. In order to
405 413 fulfill the request any of predicates must be meet
406 414 """
407 415
408 416 def check_permissions(self):
409 417 if self.required_perms.intersection(self.user_perms.get('global')):
410 418 return True
411 419 return False
412 420
413 421 class HasRepoPermissionAllDecorator(PermsDecorator):
414 422 """Checks for access permission for all given predicates for specific
415 423 repository. All of them have to be meet in order to fulfill the request
416 424 """
417 425
418 426 def check_permissions(self):
419 427 repo_name = get_repo_slug(request)
420 428 try:
421 429 user_perms = set([self.user_perms['repositories'][repo_name]])
422 430 except KeyError:
423 431 return False
424 432 if self.required_perms.issubset(user_perms):
425 433 return True
426 434 return False
427 435
428 436
429 437 class HasRepoPermissionAnyDecorator(PermsDecorator):
430 438 """Checks for access permission for any of given predicates for specific
431 439 repository. In order to fulfill the request any of predicates must be meet
432 440 """
433 441
434 442 def check_permissions(self):
435 443 repo_name = get_repo_slug(request)
436 444
437 445 try:
438 446 user_perms = set([self.user_perms['repositories'][repo_name]])
439 447 except KeyError:
440 448 return False
441 449 if self.required_perms.intersection(user_perms):
442 450 return True
443 451 return False
444 452 #===============================================================================
445 453 # CHECK FUNCTIONS
446 454 #===============================================================================
447 455
448 456 class PermsFunction(object):
449 457 """Base function for other check functions"""
450 458
451 459 def __init__(self, *perms):
452 460 available_perms = config['available_permissions']
453 461
454 462 for perm in perms:
455 463 if perm not in available_perms:
456 464 raise Exception("'%s' permission in not defined" % perm)
457 465 self.required_perms = set(perms)
458 466 self.user_perms = None
459 467 self.granted_for = ''
460 468 self.repo_name = None
461 469
462 470 def __call__(self, check_Location=''):
463 471 user = session.get('rhodecode_user', False)
464 472 if not user:
465 473 return False
466 474 self.user_perms = user.permissions
467 475 self.granted_for = user.username
468 476 log.debug('checking %s %s %s', self.__class__.__name__,
469 477 self.required_perms, user)
470 478
471 479 if self.check_permissions():
472 480 log.debug('Permission granted for %s @ %s %s', self.granted_for,
473 481 check_Location, user)
474 482 return True
475 483
476 484 else:
477 485 log.warning('Permission denied for %s @ %s %s', self.granted_for,
478 486 check_Location, user)
479 487 return False
480 488
481 489 def check_permissions(self):
482 490 """Dummy function for overriding"""
483 491 raise Exception('You have to write this function in child class')
484 492
485 493 class HasPermissionAll(PermsFunction):
486 494 def check_permissions(self):
487 495 if self.required_perms.issubset(self.user_perms.get('global')):
488 496 return True
489 497 return False
490 498
491 499 class HasPermissionAny(PermsFunction):
492 500 def check_permissions(self):
493 501 if self.required_perms.intersection(self.user_perms.get('global')):
494 502 return True
495 503 return False
496 504
497 505 class HasRepoPermissionAll(PermsFunction):
498 506
499 507 def __call__(self, repo_name=None, check_Location=''):
500 508 self.repo_name = repo_name
501 509 return super(HasRepoPermissionAll, self).__call__(check_Location)
502 510
503 511 def check_permissions(self):
504 512 if not self.repo_name:
505 513 self.repo_name = get_repo_slug(request)
506 514
507 515 try:
508 516 self.user_perms = set([self.user_perms['repositories']\
509 517 [self.repo_name]])
510 518 except KeyError:
511 519 return False
512 520 self.granted_for = self.repo_name
513 521 if self.required_perms.issubset(self.user_perms):
514 522 return True
515 523 return False
516 524
517 525 class HasRepoPermissionAny(PermsFunction):
518 526
519 527 def __call__(self, repo_name=None, check_Location=''):
520 528 self.repo_name = repo_name
521 529 return super(HasRepoPermissionAny, self).__call__(check_Location)
522 530
523 531 def check_permissions(self):
524 532 if not self.repo_name:
525 533 self.repo_name = get_repo_slug(request)
526 534
527 535 try:
528 536 self.user_perms = set([self.user_perms['repositories']\
529 537 [self.repo_name]])
530 538 except KeyError:
531 539 return False
532 540 self.granted_for = self.repo_name
533 541 if self.required_perms.intersection(self.user_perms):
534 542 return True
535 543 return False
536 544
537 545 #===============================================================================
538 546 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
539 547 #===============================================================================
540 548
541 549 class HasPermissionAnyMiddleware(object):
542 550 def __init__(self, *perms):
543 551 self.required_perms = set(perms)
544 552
545 553 def __call__(self, user, repo_name):
546 554 usr = AuthUser()
547 555 usr.user_id = user.user_id
548 556 usr.username = user.username
549 557 usr.is_admin = user.admin
550 558
551 559 try:
552 560 self.user_perms = set([fill_perms(usr)\
553 561 .permissions['repositories'][repo_name]])
554 562 except:
555 563 self.user_perms = set()
556 564 self.granted_for = ''
557 565 self.username = user.username
558 566 self.repo_name = repo_name
559 567 return self.check_permissions()
560 568
561 569 def check_permissions(self):
562 570 log.debug('checking mercurial protocol '
563 571 'permissions for user:%s repository:%s',
564 572 self.username, self.repo_name)
565 573 if self.required_perms.intersection(self.user_perms):
566 574 log.debug('permission granted')
567 575 return True
568 576 log.debug('permission denied')
569 577 return False
@@ -1,293 +1,298 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.db
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 Database Models for RhodeCode
7 7
8 8 :created_on: Apr 08, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software; you can redistribute it and/or
14 14 # modify it under the terms of the GNU General Public License
15 15 # as published by the Free Software Foundation; version 2
16 16 # of the License or (at your opinion) any later version of the license.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program; if not, write to the Free Software
25 25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 26 # MA 02110-1301, USA.
27 27 import logging
28 28 import datetime
29 29
30 30 from sqlalchemy import *
31 31 from sqlalchemy.exc import DatabaseError
32 32 from sqlalchemy.orm import relation, backref, class_mapper
33 33 from sqlalchemy.orm.session import Session
34 34
35 35 from rhodecode.model.meta import Base
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39 class BaseModel(object):
40 40
41 41 @classmethod
42 42 def _get_keys(cls):
43 43 """return column names for this model """
44 44 return class_mapper(cls).c.keys()
45 45
46 46 def get_dict(self):
47 47 """return dict with keys and values corresponding
48 48 to this model data """
49 49
50 50 d = {}
51 51 for k in self._get_keys():
52 52 d[k] = getattr(self, k)
53 53 return d
54 54
55 55 def get_appstruct(self):
56 56 """return list with keys and values tupples corresponding
57 57 to this model data """
58 58
59 59 l = []
60 60 for k in self._get_keys():
61 61 l.append((k, getattr(self, k),))
62 62 return l
63 63
64 64 def populate_obj(self, populate_dict):
65 65 """populate model with data from given populate_dict"""
66 66
67 67 for k in self._get_keys():
68 68 if k in populate_dict:
69 69 setattr(self, k, populate_dict[k])
70 70
71 71 class RhodeCodeSettings(Base, BaseModel):
72 72 __tablename__ = 'rhodecode_settings'
73 73 __table_args__ = (UniqueConstraint('app_settings_name'), {'useexisting':True})
74 74 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
75 75 app_settings_name = Column("app_settings_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
76 76 app_settings_value = Column("app_settings_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
77 77
78 78 def __init__(self, k, v):
79 79 self.app_settings_name = k
80 80 self.app_settings_value = v
81 81
82 82 def __repr__(self):
83 83 return "<%s('%s:%s')>" % (self.__class__.__name__,
84 84 self.app_settings_name, self.app_settings_value)
85 85
86 86 class RhodeCodeUi(Base, BaseModel):
87 87 __tablename__ = 'rhodecode_ui'
88 88 __table_args__ = {'useexisting':True}
89 89 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
90 90 ui_section = Column("ui_section", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
91 91 ui_key = Column("ui_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
92 92 ui_value = Column("ui_value", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
93 93 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
94 94
95 95
96 96 class User(Base, BaseModel):
97 97 __tablename__ = 'users'
98 98 __table_args__ = (UniqueConstraint('username'), UniqueConstraint('email'), {'useexisting':True})
99 99 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
100 100 username = Column("username", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
101 101 password = Column("password", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
102 102 active = Column("active", Boolean(), nullable=True, unique=None, default=None)
103 103 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
104 104 name = Column("name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
105 105 lastname = Column("lastname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
106 106 email = Column("email", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
107 107 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
108 108 is_ldap = Column("is_ldap", Boolean(), nullable=False, unique=None, default=False)
109 109
110 110 user_log = relation('UserLog', cascade='all')
111 111 user_perms = relation('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
112 112
113 113 repositories = relation('Repository')
114 114 user_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
115 115
116 116 @property
117 117 def full_contact(self):
118 118 return '%s %s <%s>' % (self.name, self.lastname, self.email)
119 119
120
121 @property
122 def is_admin(self):
123 return self.admin
124
120 125 def __repr__(self):
121 126 return "<%s('id:%s:%s')>" % (self.__class__.__name__,
122 127 self.user_id, self.username)
123 128
124 129 def update_lastlogin(self):
125 130 """Update user lastlogin"""
126 131
127 132 try:
128 133 session = Session.object_session(self)
129 134 self.last_login = datetime.datetime.now()
130 135 session.add(self)
131 136 session.commit()
132 137 log.debug('updated user %s lastlogin', self.username)
133 138 except (DatabaseError,):
134 139 session.rollback()
135 140
136 141
137 142 class UserLog(Base, BaseModel):
138 143 __tablename__ = 'user_logs'
139 144 __table_args__ = {'useexisting':True}
140 145 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
141 146 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
142 147 repository_id = Column("repository_id", Integer(length=None, convert_unicode=False, assert_unicode=None), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
143 148 repository_name = Column("repository_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
144 149 user_ip = Column("user_ip", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
145 150 action = Column("action", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
146 151 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
147 152
148 153 user = relation('User')
149 154 repository = relation('Repository')
150 155
151 156 class Repository(Base, BaseModel):
152 157 __tablename__ = 'repositories'
153 158 __table_args__ = (UniqueConstraint('repo_name'), {'useexisting':True},)
154 159 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
155 160 repo_name = Column("repo_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
156 161 repo_type = Column("repo_type", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default='hg')
157 162 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
158 163 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
159 164 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
160 165 description = Column("description", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 166 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
162 167 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
163 168
164 169 user = relation('User')
165 170 fork = relation('Repository', remote_side=repo_id)
166 171 group = relation('Group')
167 172 repo_to_perm = relation('RepoToPerm', cascade='all')
168 173 stats = relation('Statistics', cascade='all', uselist=False)
169 174
170 175 repo_followers = relation('UserFollowing', primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id', cascade='all')
171 176
172 177 def __repr__(self):
173 178 return "<%s('%s:%s')>" % (self.__class__.__name__,
174 179 self.repo_id, self.repo_name)
175 180
176 181 class Group(Base, BaseModel):
177 182 __tablename__ = 'groups'
178 183 __table_args__ = (UniqueConstraint('group_name'), {'useexisting':True},)
179 184
180 185 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
181 186 group_name = Column("group_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
182 187 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
183 188
184 189 parent_group = relation('Group', remote_side=group_id)
185 190
186 191
187 192 def __init__(self, group_name='', parent_group=None):
188 193 self.group_name = group_name
189 194 self.parent_group = parent_group
190 195
191 196 def __repr__(self):
192 197 return "<%s('%s:%s')>" % (self.__class__.__name__, self.group_id,
193 198 self.group_name)
194 199
195 200 class Permission(Base, BaseModel):
196 201 __tablename__ = 'permissions'
197 202 __table_args__ = {'useexisting':True}
198 203 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
199 204 permission_name = Column("permission_name", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
200 205 permission_longname = Column("permission_longname", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
201 206
202 207 def __repr__(self):
203 208 return "<%s('%s:%s')>" % (self.__class__.__name__,
204 209 self.permission_id, self.permission_name)
205 210
206 211 class RepoToPerm(Base, BaseModel):
207 212 __tablename__ = 'repo_to_perm'
208 213 __table_args__ = (UniqueConstraint('user_id', 'repository_id'), {'useexisting':True})
209 214 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
210 215 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
211 216 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
212 217 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
213 218
214 219 user = relation('User')
215 220 permission = relation('Permission')
216 221 repository = relation('Repository')
217 222
218 223 class UserToPerm(Base, BaseModel):
219 224 __tablename__ = 'user_to_perm'
220 225 __table_args__ = (UniqueConstraint('user_id', 'permission_id'), {'useexisting':True})
221 226 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
222 227 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
223 228 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
224 229
225 230 user = relation('User')
226 231 permission = relation('Permission')
227 232
228 233 class GroupToPerm(Base, BaseModel):
229 234 __tablename__ = 'group_to_perm'
230 235 __table_args__ = (UniqueConstraint('group_id', 'permission_id'), {'useexisting':True})
231 236
232 237 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
233 238 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
234 239 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
235 240 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
236 241
237 242 user = relation('User')
238 243 permission = relation('Permission')
239 244 group = relation('Group')
240 245
241 246 class Statistics(Base, BaseModel):
242 247 __tablename__ = 'statistics'
243 248 __table_args__ = (UniqueConstraint('repository_id'), {'useexisting':True})
244 249 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
245 250 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
246 251 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
247 252 commit_activity = Column("commit_activity", LargeBinary(), nullable=False)#JSON data
248 253 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
249 254 languages = Column("languages", LargeBinary(), nullable=False)#JSON data
250 255
251 256 repository = relation('Repository', single_parent=True)
252 257
253 258 class UserFollowing(Base, BaseModel):
254 259 __tablename__ = 'user_followings'
255 260 __table_args__ = (UniqueConstraint('user_id', 'follows_repository_id'),
256 261 UniqueConstraint('user_id', 'follows_user_id')
257 262 , {'useexisting':True})
258 263
259 264 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
260 265 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
261 266 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
262 267 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
263 268
264 269 user = relation('User', primaryjoin='User.user_id==UserFollowing.user_id')
265 270
266 271 follows_user = relation('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
267 272 follows_repository = relation('Repository')
268 273
269 274 class CacheInvalidation(Base, BaseModel):
270 275 __tablename__ = 'cache_invalidation'
271 276 __table_args__ = (UniqueConstraint('cache_key'), {'useexisting':True})
272 277 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
273 278 cache_key = Column("cache_key", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
274 279 cache_args = Column("cache_args", String(length=None, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
275 280 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
276 281
277 282
278 283 def __init__(self, cache_key, cache_args=''):
279 284 self.cache_key = cache_key
280 285 self.cache_args = cache_args
281 286 self.cache_active = False
282 287
283 288 def __repr__(self):
284 289 return "<%s('%s:%s')>" % (self.__class__.__name__,
285 290 self.cache_id, self.cache_key)
286 291
287 292 class DbMigrateVersion(Base, BaseModel):
288 293 __tablename__ = 'db_migrate_version'
289 294 __table_args__ = {'useexisting':True}
290 295 repository_id = Column('repository_id', String(250), primary_key=True)
291 296 repository_path = Column('repository_path', Text)
292 297 version = Column('version', Integer)
293 298
@@ -1,120 +1,234 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Edit user')} ${c.user.username} - ${c.rhodecode_name}
6 6 </%def>
7 7
8 8 <%def name="breadcrumbs_links()">
9 9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 10 &raquo;
11 11 ${h.link_to(_('Users'),h.url('users'))}
12 12 &raquo;
13 13 ${_('edit')} "${c.user.username}"
14 14 </%def>
15 15
16 16 <%def name="page_nav()">
17 17 ${self.menu('admin')}
18 18 </%def>
19 19
20 20 <%def name="main()">
21 21 <div class="box box-left">
22 22 <!-- box / title -->
23 23 <div class="title">
24 24 ${self.breadcrumbs()}
25 25 </div>
26 26 <!-- end box / title -->
27 27 ${h.form(url('user', id=c.user.user_id),method='put')}
28 28 <div class="form">
29 29 <!-- fields -->
30 30 <div class="fields">
31 31 <div class="field">
32 32 <div class="gravatar_box">
33 33 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(c.user.email)}"/></div>
34 34 <p>
35 35 <strong>Change your avatar at <a href="http://gravatar.com">gravatar.com</a></strong><br/>
36 36 ${_('Using')} ${c.user.email}
37 37 </p>
38 38 </div>
39 39 </div>
40 40
41 41 <div class="field">
42 42 <div class="label">
43 43 <label for="username">${_('Username')}:</label>
44 44 </div>
45 45 <div class="input">
46 46 ${h.text('username',class_='medium')}
47 47 </div>
48 48 </div>
49 49
50 50 <div class="field">
51 51 <div class="label">
52 52 <label for="new_password">${_('New password')}:</label>
53 53 </div>
54 54 <div class="input">
55 55 ${h.password('new_password',class_='medium')}
56 56 </div>
57 57 </div>
58 58
59 59 <div class="field">
60 60 <div class="label">
61 61 <label for="name">${_('First Name')}:</label>
62 62 </div>
63 63 <div class="input">
64 64 ${h.text('name',class_='medium')}
65 65 </div>
66 66 </div>
67 67
68 68 <div class="field">
69 69 <div class="label">
70 70 <label for="lastname">${_('Last Name')}:</label>
71 71 </div>
72 72 <div class="input">
73 73 ${h.text('lastname',class_='medium')}
74 74 </div>
75 75 </div>
76 76
77 77 <div class="field">
78 78 <div class="label">
79 79 <label for="email">${_('Email')}:</label>
80 80 </div>
81 81 <div class="input">
82 82 ${h.text('email',class_='medium')}
83 83 </div>
84 84 </div>
85 85
86 86 <div class="field">
87 87 <div class="label label-checkbox">
88 88 <label for="active">${_('Active')}:</label>
89 89 </div>
90 90 <div class="checkboxes">
91 91 ${h.checkbox('active',value=True)}
92 92 </div>
93 93 </div>
94 94
95 95 <div class="field">
96 96 <div class="label label-checkbox">
97 97 <label for="admin">${_('Admin')}:</label>
98 98 </div>
99 99 <div class="checkboxes">
100 100 ${h.checkbox('admin',value=True)}
101 101 </div>
102 102 </div>
103 103 <div class="buttons">
104 104 ${h.submit('save','Save',class_="ui-button")}
105 105 ${h.reset('reset','Reset',class_="ui-button")}
106 106 </div>
107 107 </div>
108 108 </div>
109 109 ${h.end_form()}
110 110 </div>
111 111 <div class="box box-right">
112 112 <!-- box / title -->
113 113 <div class="title">
114 114 <h5>${_('Permissions')}</h5>
115 115 </div>
116 <div class="table">
117 Permissions settings goes here !
118 </div>
116 <form id="map_form" method="post" action="{%url update_permissions %}">
117 <div class="form">
118 <div class="fields">
119
120
121
122 <table>
123 <tr>
124 <td class="label">${_('Permissions')}:</td>
125 <td>
126 <div>
127 <div style="float:left">
128 <div class="text">${_('Granted permissions')}</div>
129 ${h.select('granted_permissions',[],c.granted_permissions,multiple=True,size=8,style="min-width:210px")}
130 </div>
131 <div style="float:left;width:20px;padding-top:50px">
132 <img alt="add" id="add_element"
133 style="padding:2px;cursor:pointer"
134 src="/images/icons/arrow_left.png">
135 <br />
136 <img alt="remove" id="remove_element"
137 style="padding:2px;cursor:pointer"
138 src="/images/icons/arrow_right.png">
139 </div>
140 <div style="float:left">
141 <div class="text">${_('Available permissions')}</div>
142 ${h.select('available_permissions',[],c.available_permissions,multiple=True,size=8,style="min-width:210px")}
143 </div>
144 </div>
145 </td>
146 </tr>
147
148 </table>
149 <div class="buttons">
150 ${h.submit('Save','Save',class_="ui-button")}
151 </div>
152 </div>
153 </div>
154 </form>
155
156
157 <script type="text/javascript">
158 YAHOO.util.Event.onDOMReady(function(){
159
160 var D = YAHOO.util.Dom;
161 var E = YAHOO.util.Event;
162
163 //temp container for storage.
164 var cache = new Array();
165 var c = D.get('id_granted_permissions');
166
167 //get only selected options for further fullfilment
168 for(var i = 0;node =c.options[i];i++){
169 if(node.selected){
170 //push selected to my temp storage left overs :)
171 cache.push(node);
172 }
173 }
174
175 //clear select
176 c.options.length = 0;
177
178 //fill it with remembered options
179 for(var i = 0;node = cache[i];i++){
180 c.options[i]=new Option(node.text, node.value, false, false);
181 }
182
183 function target_callback(e){
184 window.location='/admin/t4?g='+e.target.value;
185 }
186
187 function prompts_action_callback(e){
188
189 var choosen = D.get('id_granted_permissions');
190 var availible = D.get('id_available_permissions');
191
192 if (this.id=='add_element'){
193 for(var i=0; node = availible.options[i];i++){
194 if(node.selected){
195 choosen.appendChild(new Option(node.text, node.value, false, false));
196 }
197 }
198 }
199 else if (this.id=='remove_element'){
200
201 //temp container for storage.
202 cache = new Array();
203
204 for(var i = 0;node = choosen.options[i];i++){
205 if(!node.selected){
206 //push left overs :)
207 cache.push(node);
208 }
209 }
210 //clear select
211 choosen.options.length = 0;
212 for(var i = 0;node = cache[i];i++){
213 choosen.options[i]=new Option(node.text, node.value, false, false);
214 }
215 }
216 else{
217
218 }
219 }
220
221 E.addListener('id_groups','change',target_callback);
222
223 E.addListener(['add_element','remove_element'],'click',prompts_action_callback)
224
225 E.addListener('map_form','submit',function(){
226 var choosen = D.get('id_granted_permissions');
227 for (var i = 0; i < choosen.options.length; i++) {
228 choosen.options[i].selected = 'selected';
229 }
230 })
231 });
232 </script>
119 233 </div>
120 234 </%def> No newline at end of file
@@ -1,119 +1,119 b''
1 1 from rhodecode.tests import *
2 2 from rhodecode.model.db import User
3 3 from rhodecode.lib.auth import check_password
4 4 from sqlalchemy.orm.exc import NoResultFound
5 5
6 6 class TestAdminUsersController(TestController):
7 7
8 8 def test_index(self):
9 9 response = self.app.get(url('users'))
10 10 # Test response...
11 11
12 12 def test_index_as_xml(self):
13 13 response = self.app.get(url('formatted_users', format='xml'))
14 14
15 15 def test_create(self):
16 16 self.log_user()
17 17 username = 'newtestuser'
18 18 password = 'test12'
19 19 name = 'name'
20 20 lastname = 'lastname'
21 21 email = 'mail@mail.com'
22 22
23 23 response = self.app.post(url('users'), {'username':username,
24 24 'password':password,
25 25 'name':name,
26 26 'active':True,
27 27 'lastname':lastname,
28 28 'email':email})
29 29
30 30
31 31 assert '''created user %s''' % (username) in response.session['flash'][0], 'No flash message about new user'
32 32
33 33 new_user = self.sa.query(User).filter(User.username == username).one()
34 34
35 35
36 36 assert new_user.username == username, 'wrong info about username'
37 37 assert check_password(password, new_user.password) == True , 'wrong info about password'
38 38 assert new_user.name == name, 'wrong info about name'
39 39 assert new_user.lastname == lastname, 'wrong info about lastname'
40 40 assert new_user.email == email, 'wrong info about email'
41 41
42 42
43 43 response.follow()
44 44 response = response.follow()
45 45 assert """edit">newtestuser</a>""" in response.body
46 46
47 47 def test_create_err(self):
48 48 self.log_user()
49 49 username = 'new_user'
50 50 password = ''
51 51 name = 'name'
52 52 lastname = 'lastname'
53 53 email = 'errmail.com'
54 54
55 55 response = self.app.post(url('users'), {'username':username,
56 56 'password':password,
57 57 'name':name,
58 58 'active':False,
59 59 'lastname':lastname,
60 60 'email':email})
61 61
62 62 assert """<span class="error-message">Invalid username</span>""" in response.body
63 63 assert """<span class="error-message">Please enter a value</span>""" in response.body
64 64 assert """<span class="error-message">An email address must contain a single @</span>""" in response.body
65 65
66 66 def get_user():
67 67 self.sa.query(User).filter(User.username == username).one()
68 68
69 69 self.assertRaises(NoResultFound, get_user), 'found user in database'
70 70
71 71 def test_new(self):
72 72 response = self.app.get(url('new_user'))
73 73
74 74 def test_new_as_xml(self):
75 75 response = self.app.get(url('formatted_new_user', format='xml'))
76 76
77 77 def test_update(self):
78 78 response = self.app.put(url('user', id=1))
79 79
80 80 def test_update_browser_fakeout(self):
81 81 response = self.app.post(url('user', id=1), params=dict(_method='put'))
82 82
83 83 def test_delete(self):
84 84 self.log_user()
85 85 username = 'newtestuserdeleteme'
86 86 password = 'test12'
87 87 name = 'name'
88 88 lastname = 'lastname'
89 89 email = 'todeletemail@mail.com'
90 90
91 91 response = self.app.post(url('users'), {'username':username,
92 92 'password':password,
93 93 'name':name,
94 94 'active':True,
95 95 'lastname':lastname,
96 96 'email':email})
97 97
98 98 response = response.follow()
99 99
100 100 new_user = self.sa.query(User).filter(User.username == username).one()
101 101 response = self.app.delete(url('user', id=new_user.user_id))
102 102
103 assert """sucessfully deleted user""" in response.session['flash'][0], 'No info about user deletion'
103 assert """successfully deleted user""" in response.session['flash'][0], 'No info about user deletion'
104 104
105 105
106 106 def test_delete_browser_fakeout(self):
107 107 response = self.app.post(url('user', id=1), params=dict(_method='delete'))
108 108
109 109 def test_show(self):
110 110 response = self.app.get(url('user', id=1))
111 111
112 112 def test_show_as_xml(self):
113 113 response = self.app.get(url('formatted_user', id=1, format='xml'))
114 114
115 115 def test_edit(self):
116 116 response = self.app.get(url('edit_user', id=1))
117 117
118 118 def test_edit_as_xml(self):
119 119 response = self.app.get(url('formatted_edit_user', id=1, format='xml'))
General Comments 0
You need to be logged in to leave comments. Login now