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