##// END OF EJS Templates
Added validation into user email map
marcink -
r2479:92255976 beta
parent child Browse files
Show More
@@ -1,247 +1,253
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) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29
29
30 from formencode import htmlfill
30 from formencode import htmlfill
31 from pylons import request, session, tmpl_context as c, url, config
31 from pylons import request, session, tmpl_context as c, url, config
32 from pylons.controllers.util import redirect
32 from pylons.controllers.util import redirect
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34
34
35 from rhodecode.lib.exceptions import DefaultUserException, \
35 from rhodecode.lib.exceptions import DefaultUserException, \
36 UserOwnsReposException
36 UserOwnsReposException
37 from rhodecode.lib import helpers as h
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
38 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator,\
39 AuthUser
39 AuthUser
40 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.base import BaseController, render
41
41
42 from rhodecode.model.db import User, Permission, UserEmailMap
42 from rhodecode.model.db import User, Permission, UserEmailMap
43 from rhodecode.model.forms import UserForm
43 from rhodecode.model.forms import UserForm
44 from rhodecode.model.user import UserModel
44 from rhodecode.model.user import UserModel
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.lib.utils import action_logger
46 from rhodecode.lib.utils import action_logger
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class UsersController(BaseController):
51 class UsersController(BaseController):
52 """REST Controller styled on the Atom Publishing Protocol"""
52 """REST Controller styled on the Atom Publishing Protocol"""
53 # To properly map this controller, ensure your config/routing.py
53 # To properly map this controller, ensure your config/routing.py
54 # file has a resource setup:
54 # file has a resource setup:
55 # map.resource('user', 'users')
55 # map.resource('user', 'users')
56
56
57 @LoginRequired()
57 @LoginRequired()
58 @HasPermissionAllDecorator('hg.admin')
58 @HasPermissionAllDecorator('hg.admin')
59 def __before__(self):
59 def __before__(self):
60 c.admin_user = session.get('admin_user')
60 c.admin_user = session.get('admin_user')
61 c.admin_username = session.get('admin_username')
61 c.admin_username = session.get('admin_username')
62 super(UsersController, self).__before__()
62 super(UsersController, self).__before__()
63 c.available_permissions = config['available_permissions']
63 c.available_permissions = config['available_permissions']
64
64
65 def index(self, format='html'):
65 def index(self, format='html'):
66 """GET /users: All items in the collection"""
66 """GET /users: All items in the collection"""
67 # url('users')
67 # url('users')
68
68
69 c.users_list = self.sa.query(User).all()
69 c.users_list = self.sa.query(User).all()
70 return render('admin/users/users.html')
70 return render('admin/users/users.html')
71
71
72 def create(self):
72 def create(self):
73 """POST /users: Create a new item"""
73 """POST /users: Create a new item"""
74 # url('users')
74 # url('users')
75
75
76 user_model = UserModel()
76 user_model = UserModel()
77 user_form = UserForm()()
77 user_form = UserForm()()
78 try:
78 try:
79 form_result = user_form.to_python(dict(request.POST))
79 form_result = user_form.to_python(dict(request.POST))
80 user_model.create(form_result)
80 user_model.create(form_result)
81 usr = form_result['username']
81 usr = form_result['username']
82 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
82 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
83 None, self.ip_addr, self.sa)
83 None, self.ip_addr, self.sa)
84 h.flash(_('created user %s') % usr,
84 h.flash(_('created user %s') % usr,
85 category='success')
85 category='success')
86 Session.commit()
86 Session.commit()
87 except formencode.Invalid, errors:
87 except formencode.Invalid, errors:
88 return htmlfill.render(
88 return htmlfill.render(
89 render('admin/users/user_add.html'),
89 render('admin/users/user_add.html'),
90 defaults=errors.value,
90 defaults=errors.value,
91 errors=errors.error_dict or {},
91 errors=errors.error_dict or {},
92 prefix_error=False,
92 prefix_error=False,
93 encoding="UTF-8")
93 encoding="UTF-8")
94 except Exception:
94 except Exception:
95 log.error(traceback.format_exc())
95 log.error(traceback.format_exc())
96 h.flash(_('error occurred during creation of user %s') \
96 h.flash(_('error occurred during creation of user %s') \
97 % request.POST.get('username'), category='error')
97 % request.POST.get('username'), category='error')
98 return redirect(url('users'))
98 return redirect(url('users'))
99
99
100 def new(self, format='html'):
100 def new(self, format='html'):
101 """GET /users/new: Form to create a new item"""
101 """GET /users/new: Form to create a new item"""
102 # url('new_user')
102 # url('new_user')
103 return render('admin/users/user_add.html')
103 return render('admin/users/user_add.html')
104
104
105 def update(self, id):
105 def update(self, id):
106 """PUT /users/id: Update an existing item"""
106 """PUT /users/id: Update an existing item"""
107 # Forms posted to this method should contain a hidden field:
107 # Forms posted to this method should contain a hidden field:
108 # <input type="hidden" name="_method" value="PUT" />
108 # <input type="hidden" name="_method" value="PUT" />
109 # Or using helpers:
109 # Or using helpers:
110 # h.form(url('update_user', id=ID),
110 # h.form(url('update_user', id=ID),
111 # method='put')
111 # method='put')
112 # url('user', id=ID)
112 # url('user', id=ID)
113 user_model = UserModel()
113 user_model = UserModel()
114 c.user = user_model.get(id)
114 c.user = user_model.get(id)
115 c.perm_user = AuthUser(user_id=id)
115 c.perm_user = AuthUser(user_id=id)
116 _form = UserForm(edit=True, old_data={'user_id': id,
116 _form = UserForm(edit=True, old_data={'user_id': id,
117 'email': c.user.email})()
117 'email': c.user.email})()
118 form_result = {}
118 form_result = {}
119 try:
119 try:
120 form_result = _form.to_python(dict(request.POST))
120 form_result = _form.to_python(dict(request.POST))
121 user_model.update(id, form_result)
121 user_model.update(id, form_result)
122 usr = form_result['username']
122 usr = form_result['username']
123 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
123 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
124 None, self.ip_addr, self.sa)
124 None, self.ip_addr, self.sa)
125 h.flash(_('User updated successfully'), category='success')
125 h.flash(_('User updated successfully'), category='success')
126 Session.commit()
126 Session.commit()
127 except formencode.Invalid, errors:
127 except formencode.Invalid, errors:
128 c.user_email_map = UserEmailMap.query()\
129 .filter(UserEmailMap.user == c.user).all()
130 defaults = errors.value
128 e = errors.error_dict or {}
131 e = errors.error_dict or {}
129 perm = Permission.get_by_key('hg.create.repository')
132 perm = Permission.get_by_key('hg.create.repository')
130 e.update({'create_repo_perm': user_model.has_perm(id, perm)})
133 defaults.update({'create_repo_perm': user_model.has_perm(id, perm)})
131 return htmlfill.render(
134 return htmlfill.render(
132 render('admin/users/user_edit.html'),
135 render('admin/users/user_edit.html'),
133 defaults=errors.value,
136 defaults=defaults,
134 errors=e,
137 errors=e,
135 prefix_error=False,
138 prefix_error=False,
136 encoding="UTF-8")
139 encoding="UTF-8")
137 except Exception:
140 except Exception:
138 log.error(traceback.format_exc())
141 log.error(traceback.format_exc())
139 h.flash(_('error occurred during update of user %s') \
142 h.flash(_('error occurred during update of user %s') \
140 % form_result.get('username'), category='error')
143 % form_result.get('username'), category='error')
141
144
142 return redirect(url('users'))
145 return redirect(url('users'))
143
146
144 def delete(self, id):
147 def delete(self, id):
145 """DELETE /users/id: Delete an existing item"""
148 """DELETE /users/id: Delete an existing item"""
146 # Forms posted to this method should contain a hidden field:
149 # Forms posted to this method should contain a hidden field:
147 # <input type="hidden" name="_method" value="DELETE" />
150 # <input type="hidden" name="_method" value="DELETE" />
148 # Or using helpers:
151 # Or using helpers:
149 # h.form(url('delete_user', id=ID),
152 # h.form(url('delete_user', id=ID),
150 # method='delete')
153 # method='delete')
151 # url('user', id=ID)
154 # url('user', id=ID)
152 user_model = UserModel()
155 user_model = UserModel()
153 try:
156 try:
154 user_model.delete(id)
157 user_model.delete(id)
155 Session.commit()
158 Session.commit()
156 h.flash(_('successfully deleted user'), category='success')
159 h.flash(_('successfully deleted user'), category='success')
157 except (UserOwnsReposException, DefaultUserException), e:
160 except (UserOwnsReposException, DefaultUserException), e:
158 h.flash(e, category='warning')
161 h.flash(e, category='warning')
159 except Exception:
162 except Exception:
160 log.error(traceback.format_exc())
163 log.error(traceback.format_exc())
161 h.flash(_('An error occurred during deletion of user'),
164 h.flash(_('An error occurred during deletion of user'),
162 category='error')
165 category='error')
163 return redirect(url('users'))
166 return redirect(url('users'))
164
167
165 def show(self, id, format='html'):
168 def show(self, id, format='html'):
166 """GET /users/id: Show a specific item"""
169 """GET /users/id: Show a specific item"""
167 # url('user', id=ID)
170 # url('user', id=ID)
168
171
169 def edit(self, id, format='html'):
172 def edit(self, id, format='html'):
170 """GET /users/id/edit: Form to edit an existing item"""
173 """GET /users/id/edit: Form to edit an existing item"""
171 # url('edit_user', id=ID)
174 # url('edit_user', id=ID)
172 c.user = User.get(id)
175 c.user = User.get(id)
173 if not c.user:
176 if not c.user:
174 return redirect(url('users'))
177 return redirect(url('users'))
175 if c.user.username == 'default':
178 if c.user.username == 'default':
176 h.flash(_("You can't edit this user"), category='warning')
179 h.flash(_("You can't edit this user"), category='warning')
177 return redirect(url('users'))
180 return redirect(url('users'))
178 c.perm_user = AuthUser(user_id=id)
181 c.perm_user = AuthUser(user_id=id)
179 c.user.permissions = {}
182 c.user.permissions = {}
180 c.granted_permissions = UserModel().fill_perms(c.user)\
183 c.granted_permissions = UserModel().fill_perms(c.user)\
181 .permissions['global']
184 .permissions['global']
182 c.user_email_map = UserEmailMap.query()\
185 c.user_email_map = UserEmailMap.query()\
183 .filter(UserEmailMap.user == c.user).all()
186 .filter(UserEmailMap.user == c.user).all()
184 defaults = c.user.get_dict()
187 defaults = c.user.get_dict()
185 perm = Permission.get_by_key('hg.create.repository')
188 perm = Permission.get_by_key('hg.create.repository')
186 defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
189 defaults.update({'create_repo_perm': UserModel().has_perm(id, perm)})
187
190
188 return htmlfill.render(
191 return htmlfill.render(
189 render('admin/users/user_edit.html'),
192 render('admin/users/user_edit.html'),
190 defaults=defaults,
193 defaults=defaults,
191 encoding="UTF-8",
194 encoding="UTF-8",
192 force_defaults=False
195 force_defaults=False
193 )
196 )
194
197
195 def update_perm(self, id):
198 def update_perm(self, id):
196 """PUT /users_perm/id: Update an existing item"""
199 """PUT /users_perm/id: Update an existing item"""
197 # url('user_perm', id=ID, method='put')
200 # url('user_perm', id=ID, method='put')
198
201
199 grant_perm = request.POST.get('create_repo_perm', False)
202 grant_perm = request.POST.get('create_repo_perm', False)
200 user_model = UserModel()
203 user_model = UserModel()
201
204
202 if grant_perm:
205 if grant_perm:
203 perm = Permission.get_by_key('hg.create.none')
206 perm = Permission.get_by_key('hg.create.none')
204 user_model.revoke_perm(id, perm)
207 user_model.revoke_perm(id, perm)
205
208
206 perm = Permission.get_by_key('hg.create.repository')
209 perm = Permission.get_by_key('hg.create.repository')
207 user_model.grant_perm(id, perm)
210 user_model.grant_perm(id, perm)
208 h.flash(_("Granted 'repository create' permission to user"),
211 h.flash(_("Granted 'repository create' permission to user"),
209 category='success')
212 category='success')
210 Session.commit()
213 Session.commit()
211 else:
214 else:
212 perm = Permission.get_by_key('hg.create.repository')
215 perm = Permission.get_by_key('hg.create.repository')
213 user_model.revoke_perm(id, perm)
216 user_model.revoke_perm(id, perm)
214
217
215 perm = Permission.get_by_key('hg.create.none')
218 perm = Permission.get_by_key('hg.create.none')
216 user_model.grant_perm(id, perm)
219 user_model.grant_perm(id, perm)
217 h.flash(_("Revoked 'repository create' permission to user"),
220 h.flash(_("Revoked 'repository create' permission to user"),
218 category='success')
221 category='success')
219 Session.commit()
222 Session.commit()
220 return redirect(url('edit_user', id=id))
223 return redirect(url('edit_user', id=id))
221
224
222 def add_email(self, id):
225 def add_email(self, id):
223 """POST /user_emails:Add an existing item"""
226 """POST /user_emails:Add an existing item"""
224 # url('user_emails', id=ID, method='put')
227 # url('user_emails', id=ID, method='put')
225
228
226 #TODO: validation and form !!!
229 #TODO: validation and form !!!
227 email = request.POST.get('new_email')
230 email = request.POST.get('new_email')
228 user_model = UserModel()
231 user_model = UserModel()
229
232
230 try:
233 try:
231 user_model.add_extra_email(id, email)
234 user_model.add_extra_email(id, email)
232 Session.commit()
235 Session.commit()
233 h.flash(_("Added email %s to user" % email), category='success')
236 h.flash(_("Added email %s to user" % email), category='success')
237 except formencode.Invalid, error:
238 msg = error.error_dict['email']
239 h.flash(msg, category='error')
234 except Exception:
240 except Exception:
235 log.error(traceback.format_exc())
241 log.error(traceback.format_exc())
236 h.flash(_('An error occurred during email saving'),
242 h.flash(_('An error occurred during email saving'),
237 category='error')
243 category='error')
238 return redirect(url('edit_user', id=id))
244 return redirect(url('edit_user', id=id))
239
245
240 def delete_email(self, id):
246 def delete_email(self, id):
241 """DELETE /user_emails_delete/id: Delete an existing item"""
247 """DELETE /user_emails_delete/id: Delete an existing item"""
242 # url('user_emails_delete', id=ID, method='delete')
248 # url('user_emails_delete', id=ID, method='delete')
243 user_model = UserModel()
249 user_model = UserModel()
244 user_model.delete_extra_email(id, request.POST.get('del_email'))
250 user_model.delete_extra_email(id, request.POST.get('del_email'))
245 Session.commit()
251 Session.commit()
246 h.flash(_("Removed email from user"), category='success')
252 h.flash(_("Removed email from user"), category='success')
247 return redirect(url('edit_user', id=id))
253 return redirect(url('edit_user', id=id))
@@ -1,822 +1,821
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37
37
38 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41 if __platform__ in PLATFORM_WIN:
42 from hashlib import sha256
43 if __platform__ in PLATFORM_OTHERS:
44 import bcrypt
45
46 from rhodecode.lib.utils2 import str2bool, safe_unicode
41 from rhodecode.lib.utils2 import str2bool, safe_unicode
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
42 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
48 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
43 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
49 from rhodecode.lib.auth_ldap import AuthLdap
44 from rhodecode.lib.auth_ldap import AuthLdap
50
45
51 from rhodecode.model import meta
46 from rhodecode.model import meta
52 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
53 from rhodecode.model.db import Permission, RhodeCodeSetting, User
48 from rhodecode.model.db import Permission, RhodeCodeSetting, User
54
49
55 log = logging.getLogger(__name__)
50 log = logging.getLogger(__name__)
56
51
57
52
58 class PasswordGenerator(object):
53 class PasswordGenerator(object):
59 """
54 """
60 This is a simple class for generating password from different sets of
55 This is a simple class for generating password from different sets of
61 characters
56 characters
62 usage::
57 usage::
63
58
64 passwd_gen = PasswordGenerator()
59 passwd_gen = PasswordGenerator()
65 #print 8-letter password containing only big and small letters
60 #print 8-letter password containing only big and small letters
66 of alphabet
61 of alphabet
67 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
62 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
68 """
63 """
69 ALPHABETS_NUM = r'''1234567890'''
64 ALPHABETS_NUM = r'''1234567890'''
70 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
65 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
71 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
66 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
72 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
67 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
73 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
68 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
74 + ALPHABETS_NUM + ALPHABETS_SPECIAL
69 + ALPHABETS_NUM + ALPHABETS_SPECIAL
75 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
70 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
76 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
71 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
77 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
72 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
78 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
73 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
79
74
80 def __init__(self, passwd=''):
75 def __init__(self, passwd=''):
81 self.passwd = passwd
76 self.passwd = passwd
82
77
83 def gen_password(self, length, type_=None):
78 def gen_password(self, length, type_=None):
84 if type_ is None:
79 if type_ is None:
85 type_ = self.ALPHABETS_FULL
80 type_ = self.ALPHABETS_FULL
86 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
81 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
87 return self.passwd
82 return self.passwd
88
83
89
84
90 class RhodeCodeCrypto(object):
85 class RhodeCodeCrypto(object):
91
86
92 @classmethod
87 @classmethod
93 def hash_string(cls, str_):
88 def hash_string(cls, str_):
94 """
89 """
95 Cryptographic function used for password hashing based on pybcrypt
90 Cryptographic function used for password hashing based on pybcrypt
96 or pycrypto in windows
91 or pycrypto in windows
97
92
98 :param password: password to hash
93 :param password: password to hash
99 """
94 """
100 if __platform__ in PLATFORM_WIN:
95 if __platform__ in PLATFORM_WIN:
96 from hashlib import sha256
101 return sha256(str_).hexdigest()
97 return sha256(str_).hexdigest()
102 elif __platform__ in PLATFORM_OTHERS:
98 elif __platform__ in PLATFORM_OTHERS:
99 import bcrypt
103 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
100 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
104 else:
101 else:
105 raise Exception('Unknown or unsupported platform %s' \
102 raise Exception('Unknown or unsupported platform %s' \
106 % __platform__)
103 % __platform__)
107
104
108 @classmethod
105 @classmethod
109 def hash_check(cls, password, hashed):
106 def hash_check(cls, password, hashed):
110 """
107 """
111 Checks matching password with it's hashed value, runs different
108 Checks matching password with it's hashed value, runs different
112 implementation based on platform it runs on
109 implementation based on platform it runs on
113
110
114 :param password: password
111 :param password: password
115 :param hashed: password in hashed form
112 :param hashed: password in hashed form
116 """
113 """
117
114
118 if __platform__ in PLATFORM_WIN:
115 if __platform__ in PLATFORM_WIN:
116 from hashlib import sha256
119 return sha256(password).hexdigest() == hashed
117 return sha256(password).hexdigest() == hashed
120 elif __platform__ in PLATFORM_OTHERS:
118 elif __platform__ in PLATFORM_OTHERS:
119 import bcrypt
121 return bcrypt.hashpw(password, hashed) == hashed
120 return bcrypt.hashpw(password, hashed) == hashed
122 else:
121 else:
123 raise Exception('Unknown or unsupported platform %s' \
122 raise Exception('Unknown or unsupported platform %s' \
124 % __platform__)
123 % __platform__)
125
124
126
125
127 def get_crypt_password(password):
126 def get_crypt_password(password):
128 return RhodeCodeCrypto.hash_string(password)
127 return RhodeCodeCrypto.hash_string(password)
129
128
130
129
131 def check_password(password, hashed):
130 def check_password(password, hashed):
132 return RhodeCodeCrypto.hash_check(password, hashed)
131 return RhodeCodeCrypto.hash_check(password, hashed)
133
132
134
133
135 def generate_api_key(str_, salt=None):
134 def generate_api_key(str_, salt=None):
136 """
135 """
137 Generates API KEY from given string
136 Generates API KEY from given string
138
137
139 :param str_:
138 :param str_:
140 :param salt:
139 :param salt:
141 """
140 """
142
141
143 if salt is None:
142 if salt is None:
144 salt = _RandomNameSequence().next()
143 salt = _RandomNameSequence().next()
145
144
146 return hashlib.sha1(str_ + salt).hexdigest()
145 return hashlib.sha1(str_ + salt).hexdigest()
147
146
148
147
149 def authfunc(environ, username, password):
148 def authfunc(environ, username, password):
150 """
149 """
151 Dummy authentication wrapper function used in Mercurial and Git for
150 Dummy authentication wrapper function used in Mercurial and Git for
152 access control.
151 access control.
153
152
154 :param environ: needed only for using in Basic auth
153 :param environ: needed only for using in Basic auth
155 """
154 """
156 return authenticate(username, password)
155 return authenticate(username, password)
157
156
158
157
159 def authenticate(username, password):
158 def authenticate(username, password):
160 """
159 """
161 Authentication function used for access control,
160 Authentication function used for access control,
162 firstly checks for db authentication then if ldap is enabled for ldap
161 firstly checks for db authentication then if ldap is enabled for ldap
163 authentication, also creates ldap user if not in database
162 authentication, also creates ldap user if not in database
164
163
165 :param username: username
164 :param username: username
166 :param password: password
165 :param password: password
167 """
166 """
168
167
169 user_model = UserModel()
168 user_model = UserModel()
170 user = User.get_by_username(username)
169 user = User.get_by_username(username)
171
170
172 log.debug('Authenticating user using RhodeCode account')
171 log.debug('Authenticating user using RhodeCode account')
173 if user is not None and not user.ldap_dn:
172 if user is not None and not user.ldap_dn:
174 if user.active:
173 if user.active:
175 if user.username == 'default' and user.active:
174 if user.username == 'default' and user.active:
176 log.info('user %s authenticated correctly as anonymous user' %
175 log.info('user %s authenticated correctly as anonymous user' %
177 username)
176 username)
178 return True
177 return True
179
178
180 elif user.username == username and check_password(password,
179 elif user.username == username and check_password(password,
181 user.password):
180 user.password):
182 log.info('user %s authenticated correctly' % username)
181 log.info('user %s authenticated correctly' % username)
183 return True
182 return True
184 else:
183 else:
185 log.warning('user %s tried auth but is disabled' % username)
184 log.warning('user %s tried auth but is disabled' % username)
186
185
187 else:
186 else:
188 log.debug('Regular authentication failed')
187 log.debug('Regular authentication failed')
189 user_obj = User.get_by_username(username, case_insensitive=True)
188 user_obj = User.get_by_username(username, case_insensitive=True)
190
189
191 if user_obj is not None and not user_obj.ldap_dn:
190 if user_obj is not None and not user_obj.ldap_dn:
192 log.debug('this user already exists as non ldap')
191 log.debug('this user already exists as non ldap')
193 return False
192 return False
194
193
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
194 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 #======================================================================
195 #======================================================================
197 # FALLBACK TO LDAP AUTH IF ENABLE
196 # FALLBACK TO LDAP AUTH IF ENABLE
198 #======================================================================
197 #======================================================================
199 if str2bool(ldap_settings.get('ldap_active')):
198 if str2bool(ldap_settings.get('ldap_active')):
200 log.debug("Authenticating user using ldap")
199 log.debug("Authenticating user using ldap")
201 kwargs = {
200 kwargs = {
202 'server': ldap_settings.get('ldap_host', ''),
201 'server': ldap_settings.get('ldap_host', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
202 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 'port': ldap_settings.get('ldap_port'),
203 'port': ldap_settings.get('ldap_port'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
204 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
205 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
206 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
207 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
208 'ldap_filter': ldap_settings.get('ldap_filter'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
209 'search_scope': ldap_settings.get('ldap_search_scope'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
210 'attr_login': ldap_settings.get('ldap_attr_login'),
212 'ldap_version': 3,
211 'ldap_version': 3,
213 }
212 }
214 log.debug('Checking for ldap authentication')
213 log.debug('Checking for ldap authentication')
215 try:
214 try:
216 aldap = AuthLdap(**kwargs)
215 aldap = AuthLdap(**kwargs)
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
216 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 password)
217 password)
219 log.debug('Got ldap DN response %s' % user_dn)
218 log.debug('Got ldap DN response %s' % user_dn)
220
219
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
220 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 .get(k), [''])[0]
221 .get(k), [''])[0]
223
222
224 user_attrs = {
223 user_attrs = {
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
224 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
225 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 'email': get_ldap_attr('ldap_attr_email'),
226 'email': get_ldap_attr('ldap_attr_email'),
228 }
227 }
229
228
230 # don't store LDAP password since we don't need it. Override
229 # don't store LDAP password since we don't need it. Override
231 # with some random generated password
230 # with some random generated password
232 _password = PasswordGenerator().gen_password(length=8)
231 _password = PasswordGenerator().gen_password(length=8)
233 # create this user on the fly if it doesn't exist in rhodecode
232 # create this user on the fly if it doesn't exist in rhodecode
234 # database
233 # database
235 if user_model.create_ldap(username, _password, user_dn,
234 if user_model.create_ldap(username, _password, user_dn,
236 user_attrs):
235 user_attrs):
237 log.info('created new ldap user %s' % username)
236 log.info('created new ldap user %s' % username)
238
237
239 Session.commit()
238 Session.commit()
240 return True
239 return True
241 except (LdapUsernameError, LdapPasswordError,):
240 except (LdapUsernameError, LdapPasswordError,):
242 pass
241 pass
243 except (Exception,):
242 except (Exception,):
244 log.error(traceback.format_exc())
243 log.error(traceback.format_exc())
245 pass
244 pass
246 return False
245 return False
247
246
248
247
249 def login_container_auth(username):
248 def login_container_auth(username):
250 user = User.get_by_username(username)
249 user = User.get_by_username(username)
251 if user is None:
250 if user is None:
252 user_attrs = {
251 user_attrs = {
253 'name': username,
252 'name': username,
254 'lastname': None,
253 'lastname': None,
255 'email': None,
254 'email': None,
256 }
255 }
257 user = UserModel().create_for_container_auth(username, user_attrs)
256 user = UserModel().create_for_container_auth(username, user_attrs)
258 if not user:
257 if not user:
259 return None
258 return None
260 log.info('User %s was created by container authentication' % username)
259 log.info('User %s was created by container authentication' % username)
261
260
262 if not user.active:
261 if not user.active:
263 return None
262 return None
264
263
265 user.update_lastlogin()
264 user.update_lastlogin()
266 Session.commit()
265 Session.commit()
267
266
268 log.debug('User %s is now logged in by container authentication',
267 log.debug('User %s is now logged in by container authentication',
269 user.username)
268 user.username)
270 return user
269 return user
271
270
272
271
273 def get_container_username(environ, config):
272 def get_container_username(environ, config):
274 username = None
273 username = None
275
274
276 if str2bool(config.get('container_auth_enabled', False)):
275 if str2bool(config.get('container_auth_enabled', False)):
277 from paste.httpheaders import REMOTE_USER
276 from paste.httpheaders import REMOTE_USER
278 username = REMOTE_USER(environ)
277 username = REMOTE_USER(environ)
279
278
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
279 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 username = environ.get('HTTP_X_FORWARDED_USER')
280 username = environ.get('HTTP_X_FORWARDED_USER')
282
281
283 if username:
282 if username:
284 # Removing realm and domain from username
283 # Removing realm and domain from username
285 username = username.partition('@')[0]
284 username = username.partition('@')[0]
286 username = username.rpartition('\\')[2]
285 username = username.rpartition('\\')[2]
287 log.debug('Received username %s from container' % username)
286 log.debug('Received username %s from container' % username)
288
287
289 return username
288 return username
290
289
291
290
292 class CookieStoreWrapper(object):
291 class CookieStoreWrapper(object):
293
292
294 def __init__(self, cookie_store):
293 def __init__(self, cookie_store):
295 self.cookie_store = cookie_store
294 self.cookie_store = cookie_store
296
295
297 def __repr__(self):
296 def __repr__(self):
298 return 'CookieStore<%s>' % (self.cookie_store)
297 return 'CookieStore<%s>' % (self.cookie_store)
299
298
300 def get(self, key, other=None):
299 def get(self, key, other=None):
301 if isinstance(self.cookie_store, dict):
300 if isinstance(self.cookie_store, dict):
302 return self.cookie_store.get(key, other)
301 return self.cookie_store.get(key, other)
303 elif isinstance(self.cookie_store, AuthUser):
302 elif isinstance(self.cookie_store, AuthUser):
304 return self.cookie_store.__dict__.get(key, other)
303 return self.cookie_store.__dict__.get(key, other)
305
304
306
305
307 class AuthUser(object):
306 class AuthUser(object):
308 """
307 """
309 A simple object that handles all attributes of user in RhodeCode
308 A simple object that handles all attributes of user in RhodeCode
310
309
311 It does lookup based on API key,given user, or user present in session
310 It does lookup based on API key,given user, or user present in session
312 Then it fills all required information for such user. It also checks if
311 Then it fills all required information for such user. It also checks if
313 anonymous access is enabled and if so, it returns default user as logged
312 anonymous access is enabled and if so, it returns default user as logged
314 in
313 in
315 """
314 """
316
315
317 def __init__(self, user_id=None, api_key=None, username=None):
316 def __init__(self, user_id=None, api_key=None, username=None):
318
317
319 self.user_id = user_id
318 self.user_id = user_id
320 self.api_key = None
319 self.api_key = None
321 self.username = username
320 self.username = username
322
321
323 self.name = ''
322 self.name = ''
324 self.lastname = ''
323 self.lastname = ''
325 self.email = ''
324 self.email = ''
326 self.is_authenticated = False
325 self.is_authenticated = False
327 self.admin = False
326 self.admin = False
328 self.permissions = {}
327 self.permissions = {}
329 self._api_key = api_key
328 self._api_key = api_key
330 self.propagate_data()
329 self.propagate_data()
331 self._instance = None
330 self._instance = None
332
331
333 def propagate_data(self):
332 def propagate_data(self):
334 user_model = UserModel()
333 user_model = UserModel()
335 self.anonymous_user = User.get_by_username('default', cache=True)
334 self.anonymous_user = User.get_by_username('default', cache=True)
336 is_user_loaded = False
335 is_user_loaded = False
337
336
338 # try go get user by api key
337 # try go get user by api key
339 if self._api_key and self._api_key != self.anonymous_user.api_key:
338 if self._api_key and self._api_key != self.anonymous_user.api_key:
340 log.debug('Auth User lookup by API KEY %s' % self._api_key)
339 log.debug('Auth User lookup by API KEY %s' % self._api_key)
341 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
340 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
342 # lookup by userid
341 # lookup by userid
343 elif (self.user_id is not None and
342 elif (self.user_id is not None and
344 self.user_id != self.anonymous_user.user_id):
343 self.user_id != self.anonymous_user.user_id):
345 log.debug('Auth User lookup by USER ID %s' % self.user_id)
344 log.debug('Auth User lookup by USER ID %s' % self.user_id)
346 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
345 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
347 # lookup by username
346 # lookup by username
348 elif self.username and \
347 elif self.username and \
349 str2bool(config.get('container_auth_enabled', False)):
348 str2bool(config.get('container_auth_enabled', False)):
350
349
351 log.debug('Auth User lookup by USER NAME %s' % self.username)
350 log.debug('Auth User lookup by USER NAME %s' % self.username)
352 dbuser = login_container_auth(self.username)
351 dbuser = login_container_auth(self.username)
353 if dbuser is not None:
352 if dbuser is not None:
354 for k, v in dbuser.get_dict().items():
353 for k, v in dbuser.get_dict().items():
355 setattr(self, k, v)
354 setattr(self, k, v)
356 self.set_authenticated()
355 self.set_authenticated()
357 is_user_loaded = True
356 is_user_loaded = True
358 else:
357 else:
359 log.debug('No data in %s that could been used to log in' % self)
358 log.debug('No data in %s that could been used to log in' % self)
360
359
361 if not is_user_loaded:
360 if not is_user_loaded:
362 # if we cannot authenticate user try anonymous
361 # if we cannot authenticate user try anonymous
363 if self.anonymous_user.active is True:
362 if self.anonymous_user.active is True:
364 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
363 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
365 # then we set this user is logged in
364 # then we set this user is logged in
366 self.is_authenticated = True
365 self.is_authenticated = True
367 else:
366 else:
368 self.user_id = None
367 self.user_id = None
369 self.username = None
368 self.username = None
370 self.is_authenticated = False
369 self.is_authenticated = False
371
370
372 if not self.username:
371 if not self.username:
373 self.username = 'None'
372 self.username = 'None'
374
373
375 log.debug('Auth User is now %s' % self)
374 log.debug('Auth User is now %s' % self)
376 user_model.fill_perms(self)
375 user_model.fill_perms(self)
377
376
378 @property
377 @property
379 def is_admin(self):
378 def is_admin(self):
380 return self.admin
379 return self.admin
381
380
382 def __repr__(self):
381 def __repr__(self):
383 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
382 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
384 self.is_authenticated)
383 self.is_authenticated)
385
384
386 def set_authenticated(self, authenticated=True):
385 def set_authenticated(self, authenticated=True):
387 if self.user_id != self.anonymous_user.user_id:
386 if self.user_id != self.anonymous_user.user_id:
388 self.is_authenticated = authenticated
387 self.is_authenticated = authenticated
389
388
390 def get_cookie_store(self):
389 def get_cookie_store(self):
391 return {'username': self.username,
390 return {'username': self.username,
392 'user_id': self.user_id,
391 'user_id': self.user_id,
393 'is_authenticated': self.is_authenticated}
392 'is_authenticated': self.is_authenticated}
394
393
395 @classmethod
394 @classmethod
396 def from_cookie_store(cls, cookie_store):
395 def from_cookie_store(cls, cookie_store):
397 """
396 """
398 Creates AuthUser from a cookie store
397 Creates AuthUser from a cookie store
399
398
400 :param cls:
399 :param cls:
401 :param cookie_store:
400 :param cookie_store:
402 """
401 """
403 user_id = cookie_store.get('user_id')
402 user_id = cookie_store.get('user_id')
404 username = cookie_store.get('username')
403 username = cookie_store.get('username')
405 api_key = cookie_store.get('api_key')
404 api_key = cookie_store.get('api_key')
406 return AuthUser(user_id, api_key, username)
405 return AuthUser(user_id, api_key, username)
407
406
408
407
409 def set_available_permissions(config):
408 def set_available_permissions(config):
410 """
409 """
411 This function will propagate pylons globals with all available defined
410 This function will propagate pylons globals with all available defined
412 permission given in db. We don't want to check each time from db for new
411 permission given in db. We don't want to check each time from db for new
413 permissions since adding a new permission also requires application restart
412 permissions since adding a new permission also requires application restart
414 ie. to decorate new views with the newly created permission
413 ie. to decorate new views with the newly created permission
415
414
416 :param config: current pylons config instance
415 :param config: current pylons config instance
417
416
418 """
417 """
419 log.info('getting information about all available permissions')
418 log.info('getting information about all available permissions')
420 try:
419 try:
421 sa = meta.Session
420 sa = meta.Session
422 all_perms = sa.query(Permission).all()
421 all_perms = sa.query(Permission).all()
423 except Exception:
422 except Exception:
424 pass
423 pass
425 finally:
424 finally:
426 meta.Session.remove()
425 meta.Session.remove()
427
426
428 config['available_permissions'] = [x.permission_name for x in all_perms]
427 config['available_permissions'] = [x.permission_name for x in all_perms]
429
428
430
429
431 #==============================================================================
430 #==============================================================================
432 # CHECK DECORATORS
431 # CHECK DECORATORS
433 #==============================================================================
432 #==============================================================================
434 class LoginRequired(object):
433 class LoginRequired(object):
435 """
434 """
436 Must be logged in to execute this function else
435 Must be logged in to execute this function else
437 redirect to login page
436 redirect to login page
438
437
439 :param api_access: if enabled this checks only for valid auth token
438 :param api_access: if enabled this checks only for valid auth token
440 and grants access based on valid token
439 and grants access based on valid token
441 """
440 """
442
441
443 def __init__(self, api_access=False):
442 def __init__(self, api_access=False):
444 self.api_access = api_access
443 self.api_access = api_access
445
444
446 def __call__(self, func):
445 def __call__(self, func):
447 return decorator(self.__wrapper, func)
446 return decorator(self.__wrapper, func)
448
447
449 def __wrapper(self, func, *fargs, **fkwargs):
448 def __wrapper(self, func, *fargs, **fkwargs):
450 cls = fargs[0]
449 cls = fargs[0]
451 user = cls.rhodecode_user
450 user = cls.rhodecode_user
452
451
453 api_access_ok = False
452 api_access_ok = False
454 if self.api_access:
453 if self.api_access:
455 log.debug('Checking API KEY access for %s' % cls)
454 log.debug('Checking API KEY access for %s' % cls)
456 if user.api_key == request.GET.get('api_key'):
455 if user.api_key == request.GET.get('api_key'):
457 api_access_ok = True
456 api_access_ok = True
458 else:
457 else:
459 log.debug("API KEY token not valid")
458 log.debug("API KEY token not valid")
460 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
459 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
461 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
460 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
462 if user.is_authenticated or api_access_ok:
461 if user.is_authenticated or api_access_ok:
463 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
462 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
464 log.info('user %s is authenticated and granted access to %s '
463 log.info('user %s is authenticated and granted access to %s '
465 'using %s' % (user.username, loc, reason)
464 'using %s' % (user.username, loc, reason)
466 )
465 )
467 return func(*fargs, **fkwargs)
466 return func(*fargs, **fkwargs)
468 else:
467 else:
469 log.warn('user %s NOT authenticated on func: %s' % (
468 log.warn('user %s NOT authenticated on func: %s' % (
470 user, loc)
469 user, loc)
471 )
470 )
472 p = url.current()
471 p = url.current()
473
472
474 log.debug('redirecting to login page with %s' % p)
473 log.debug('redirecting to login page with %s' % p)
475 return redirect(url('login_home', came_from=p))
474 return redirect(url('login_home', came_from=p))
476
475
477
476
478 class NotAnonymous(object):
477 class NotAnonymous(object):
479 """
478 """
480 Must be logged in to execute this function else
479 Must be logged in to execute this function else
481 redirect to login page"""
480 redirect to login page"""
482
481
483 def __call__(self, func):
482 def __call__(self, func):
484 return decorator(self.__wrapper, func)
483 return decorator(self.__wrapper, func)
485
484
486 def __wrapper(self, func, *fargs, **fkwargs):
485 def __wrapper(self, func, *fargs, **fkwargs):
487 cls = fargs[0]
486 cls = fargs[0]
488 self.user = cls.rhodecode_user
487 self.user = cls.rhodecode_user
489
488
490 log.debug('Checking if user is not anonymous @%s' % cls)
489 log.debug('Checking if user is not anonymous @%s' % cls)
491
490
492 anonymous = self.user.username == 'default'
491 anonymous = self.user.username == 'default'
493
492
494 if anonymous:
493 if anonymous:
495 p = url.current()
494 p = url.current()
496
495
497 import rhodecode.lib.helpers as h
496 import rhodecode.lib.helpers as h
498 h.flash(_('You need to be a registered user to '
497 h.flash(_('You need to be a registered user to '
499 'perform this action'),
498 'perform this action'),
500 category='warning')
499 category='warning')
501 return redirect(url('login_home', came_from=p))
500 return redirect(url('login_home', came_from=p))
502 else:
501 else:
503 return func(*fargs, **fkwargs)
502 return func(*fargs, **fkwargs)
504
503
505
504
506 class PermsDecorator(object):
505 class PermsDecorator(object):
507 """Base class for controller decorators"""
506 """Base class for controller decorators"""
508
507
509 def __init__(self, *required_perms):
508 def __init__(self, *required_perms):
510 available_perms = config['available_permissions']
509 available_perms = config['available_permissions']
511 for perm in required_perms:
510 for perm in required_perms:
512 if perm not in available_perms:
511 if perm not in available_perms:
513 raise Exception("'%s' permission is not defined" % perm)
512 raise Exception("'%s' permission is not defined" % perm)
514 self.required_perms = set(required_perms)
513 self.required_perms = set(required_perms)
515 self.user_perms = None
514 self.user_perms = None
516
515
517 def __call__(self, func):
516 def __call__(self, func):
518 return decorator(self.__wrapper, func)
517 return decorator(self.__wrapper, func)
519
518
520 def __wrapper(self, func, *fargs, **fkwargs):
519 def __wrapper(self, func, *fargs, **fkwargs):
521 cls = fargs[0]
520 cls = fargs[0]
522 self.user = cls.rhodecode_user
521 self.user = cls.rhodecode_user
523 self.user_perms = self.user.permissions
522 self.user_perms = self.user.permissions
524 log.debug('checking %s permissions %s for %s %s',
523 log.debug('checking %s permissions %s for %s %s',
525 self.__class__.__name__, self.required_perms, cls, self.user)
524 self.__class__.__name__, self.required_perms, cls, self.user)
526
525
527 if self.check_permissions():
526 if self.check_permissions():
528 log.debug('Permission granted for %s %s' % (cls, self.user))
527 log.debug('Permission granted for %s %s' % (cls, self.user))
529 return func(*fargs, **fkwargs)
528 return func(*fargs, **fkwargs)
530
529
531 else:
530 else:
532 log.debug('Permission denied for %s %s' % (cls, self.user))
531 log.debug('Permission denied for %s %s' % (cls, self.user))
533 anonymous = self.user.username == 'default'
532 anonymous = self.user.username == 'default'
534
533
535 if anonymous:
534 if anonymous:
536 p = url.current()
535 p = url.current()
537
536
538 import rhodecode.lib.helpers as h
537 import rhodecode.lib.helpers as h
539 h.flash(_('You need to be a signed in to '
538 h.flash(_('You need to be a signed in to '
540 'view this page'),
539 'view this page'),
541 category='warning')
540 category='warning')
542 return redirect(url('login_home', came_from=p))
541 return redirect(url('login_home', came_from=p))
543
542
544 else:
543 else:
545 # redirect with forbidden ret code
544 # redirect with forbidden ret code
546 return abort(403)
545 return abort(403)
547
546
548 def check_permissions(self):
547 def check_permissions(self):
549 """Dummy function for overriding"""
548 """Dummy function for overriding"""
550 raise Exception('You have to write this function in child class')
549 raise Exception('You have to write this function in child class')
551
550
552
551
553 class HasPermissionAllDecorator(PermsDecorator):
552 class HasPermissionAllDecorator(PermsDecorator):
554 """
553 """
555 Checks for access permission for all given predicates. All of them
554 Checks for access permission for all given predicates. All of them
556 have to be meet in order to fulfill the request
555 have to be meet in order to fulfill the request
557 """
556 """
558
557
559 def check_permissions(self):
558 def check_permissions(self):
560 if self.required_perms.issubset(self.user_perms.get('global')):
559 if self.required_perms.issubset(self.user_perms.get('global')):
561 return True
560 return True
562 return False
561 return False
563
562
564
563
565 class HasPermissionAnyDecorator(PermsDecorator):
564 class HasPermissionAnyDecorator(PermsDecorator):
566 """
565 """
567 Checks for access permission for any of given predicates. In order to
566 Checks for access permission for any of given predicates. In order to
568 fulfill the request any of predicates must be meet
567 fulfill the request any of predicates must be meet
569 """
568 """
570
569
571 def check_permissions(self):
570 def check_permissions(self):
572 if self.required_perms.intersection(self.user_perms.get('global')):
571 if self.required_perms.intersection(self.user_perms.get('global')):
573 return True
572 return True
574 return False
573 return False
575
574
576
575
577 class HasRepoPermissionAllDecorator(PermsDecorator):
576 class HasRepoPermissionAllDecorator(PermsDecorator):
578 """
577 """
579 Checks for access permission for all given predicates for specific
578 Checks for access permission for all given predicates for specific
580 repository. All of them have to be meet in order to fulfill the request
579 repository. All of them have to be meet in order to fulfill the request
581 """
580 """
582
581
583 def check_permissions(self):
582 def check_permissions(self):
584 repo_name = get_repo_slug(request)
583 repo_name = get_repo_slug(request)
585 try:
584 try:
586 user_perms = set([self.user_perms['repositories'][repo_name]])
585 user_perms = set([self.user_perms['repositories'][repo_name]])
587 except KeyError:
586 except KeyError:
588 return False
587 return False
589 if self.required_perms.issubset(user_perms):
588 if self.required_perms.issubset(user_perms):
590 return True
589 return True
591 return False
590 return False
592
591
593
592
594 class HasRepoPermissionAnyDecorator(PermsDecorator):
593 class HasRepoPermissionAnyDecorator(PermsDecorator):
595 """
594 """
596 Checks for access permission for any of given predicates for specific
595 Checks for access permission for any of given predicates for specific
597 repository. In order to fulfill the request any of predicates must be meet
596 repository. In order to fulfill the request any of predicates must be meet
598 """
597 """
599
598
600 def check_permissions(self):
599 def check_permissions(self):
601 repo_name = get_repo_slug(request)
600 repo_name = get_repo_slug(request)
602
601
603 try:
602 try:
604 user_perms = set([self.user_perms['repositories'][repo_name]])
603 user_perms = set([self.user_perms['repositories'][repo_name]])
605 except KeyError:
604 except KeyError:
606 return False
605 return False
607
606
608 if self.required_perms.intersection(user_perms):
607 if self.required_perms.intersection(user_perms):
609 return True
608 return True
610 return False
609 return False
611
610
612
611
613 class HasReposGroupPermissionAllDecorator(PermsDecorator):
612 class HasReposGroupPermissionAllDecorator(PermsDecorator):
614 """
613 """
615 Checks for access permission for all given predicates for specific
614 Checks for access permission for all given predicates for specific
616 repository. All of them have to be meet in order to fulfill the request
615 repository. All of them have to be meet in order to fulfill the request
617 """
616 """
618
617
619 def check_permissions(self):
618 def check_permissions(self):
620 group_name = get_repos_group_slug(request)
619 group_name = get_repos_group_slug(request)
621 try:
620 try:
622 user_perms = set([self.user_perms['repositories_groups'][group_name]])
621 user_perms = set([self.user_perms['repositories_groups'][group_name]])
623 except KeyError:
622 except KeyError:
624 return False
623 return False
625 if self.required_perms.issubset(user_perms):
624 if self.required_perms.issubset(user_perms):
626 return True
625 return True
627 return False
626 return False
628
627
629
628
630 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
629 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
631 """
630 """
632 Checks for access permission for any of given predicates for specific
631 Checks for access permission for any of given predicates for specific
633 repository. In order to fulfill the request any of predicates must be meet
632 repository. In order to fulfill the request any of predicates must be meet
634 """
633 """
635
634
636 def check_permissions(self):
635 def check_permissions(self):
637 group_name = get_repos_group_slug(request)
636 group_name = get_repos_group_slug(request)
638
637
639 try:
638 try:
640 user_perms = set([self.user_perms['repositories_groups'][group_name]])
639 user_perms = set([self.user_perms['repositories_groups'][group_name]])
641 except KeyError:
640 except KeyError:
642 return False
641 return False
643 if self.required_perms.intersection(user_perms):
642 if self.required_perms.intersection(user_perms):
644 return True
643 return True
645 return False
644 return False
646
645
647
646
648 #==============================================================================
647 #==============================================================================
649 # CHECK FUNCTIONS
648 # CHECK FUNCTIONS
650 #==============================================================================
649 #==============================================================================
651 class PermsFunction(object):
650 class PermsFunction(object):
652 """Base function for other check functions"""
651 """Base function for other check functions"""
653
652
654 def __init__(self, *perms):
653 def __init__(self, *perms):
655 available_perms = config['available_permissions']
654 available_perms = config['available_permissions']
656
655
657 for perm in perms:
656 for perm in perms:
658 if perm not in available_perms:
657 if perm not in available_perms:
659 raise Exception("'%s' permission is not defined" % perm)
658 raise Exception("'%s' permission is not defined" % perm)
660 self.required_perms = set(perms)
659 self.required_perms = set(perms)
661 self.user_perms = None
660 self.user_perms = None
662 self.repo_name = None
661 self.repo_name = None
663 self.group_name = None
662 self.group_name = None
664
663
665 def __call__(self, check_Location=''):
664 def __call__(self, check_Location=''):
666 user = request.user
665 user = request.user
667 cls_name = self.__class__.__name__
666 cls_name = self.__class__.__name__
668 check_scope = {
667 check_scope = {
669 'HasPermissionAll': '',
668 'HasPermissionAll': '',
670 'HasPermissionAny': '',
669 'HasPermissionAny': '',
671 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
670 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
672 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
671 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
673 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
672 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
674 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
673 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
675 }.get(cls_name, '?')
674 }.get(cls_name, '?')
676 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
675 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
677 self.required_perms, user, check_scope,
676 self.required_perms, user, check_scope,
678 check_Location or 'unspecified location')
677 check_Location or 'unspecified location')
679 if not user:
678 if not user:
680 log.debug('Empty request user')
679 log.debug('Empty request user')
681 return False
680 return False
682 self.user_perms = user.permissions
681 self.user_perms = user.permissions
683 if self.check_permissions():
682 if self.check_permissions():
684 log.debug('Permission granted for user: %s @ %s', user,
683 log.debug('Permission granted for user: %s @ %s', user,
685 check_Location or 'unspecified location')
684 check_Location or 'unspecified location')
686 return True
685 return True
687
686
688 else:
687 else:
689 log.debug('Permission denied for user: %s @ %s', user,
688 log.debug('Permission denied for user: %s @ %s', user,
690 check_Location or 'unspecified location')
689 check_Location or 'unspecified location')
691 return False
690 return False
692
691
693 def check_permissions(self):
692 def check_permissions(self):
694 """Dummy function for overriding"""
693 """Dummy function for overriding"""
695 raise Exception('You have to write this function in child class')
694 raise Exception('You have to write this function in child class')
696
695
697
696
698 class HasPermissionAll(PermsFunction):
697 class HasPermissionAll(PermsFunction):
699 def check_permissions(self):
698 def check_permissions(self):
700 if self.required_perms.issubset(self.user_perms.get('global')):
699 if self.required_perms.issubset(self.user_perms.get('global')):
701 return True
700 return True
702 return False
701 return False
703
702
704
703
705 class HasPermissionAny(PermsFunction):
704 class HasPermissionAny(PermsFunction):
706 def check_permissions(self):
705 def check_permissions(self):
707 if self.required_perms.intersection(self.user_perms.get('global')):
706 if self.required_perms.intersection(self.user_perms.get('global')):
708 return True
707 return True
709 return False
708 return False
710
709
711
710
712 class HasRepoPermissionAll(PermsFunction):
711 class HasRepoPermissionAll(PermsFunction):
713 def __call__(self, repo_name=None, check_Location=''):
712 def __call__(self, repo_name=None, check_Location=''):
714 self.repo_name = repo_name
713 self.repo_name = repo_name
715 return super(HasRepoPermissionAll, self).__call__(check_Location)
714 return super(HasRepoPermissionAll, self).__call__(check_Location)
716
715
717 def check_permissions(self):
716 def check_permissions(self):
718 if not self.repo_name:
717 if not self.repo_name:
719 self.repo_name = get_repo_slug(request)
718 self.repo_name = get_repo_slug(request)
720
719
721 try:
720 try:
722 self._user_perms = set(
721 self._user_perms = set(
723 [self.user_perms['repositories'][self.repo_name]]
722 [self.user_perms['repositories'][self.repo_name]]
724 )
723 )
725 except KeyError:
724 except KeyError:
726 return False
725 return False
727 if self.required_perms.issubset(self._user_perms):
726 if self.required_perms.issubset(self._user_perms):
728 return True
727 return True
729 return False
728 return False
730
729
731
730
732 class HasRepoPermissionAny(PermsFunction):
731 class HasRepoPermissionAny(PermsFunction):
733 def __call__(self, repo_name=None, check_Location=''):
732 def __call__(self, repo_name=None, check_Location=''):
734 self.repo_name = repo_name
733 self.repo_name = repo_name
735 return super(HasRepoPermissionAny, self).__call__(check_Location)
734 return super(HasRepoPermissionAny, self).__call__(check_Location)
736
735
737 def check_permissions(self):
736 def check_permissions(self):
738 if not self.repo_name:
737 if not self.repo_name:
739 self.repo_name = get_repo_slug(request)
738 self.repo_name = get_repo_slug(request)
740
739
741 try:
740 try:
742 self._user_perms = set(
741 self._user_perms = set(
743 [self.user_perms['repositories'][self.repo_name]]
742 [self.user_perms['repositories'][self.repo_name]]
744 )
743 )
745 except KeyError:
744 except KeyError:
746 return False
745 return False
747 if self.required_perms.intersection(self._user_perms):
746 if self.required_perms.intersection(self._user_perms):
748 return True
747 return True
749 return False
748 return False
750
749
751
750
752 class HasReposGroupPermissionAny(PermsFunction):
751 class HasReposGroupPermissionAny(PermsFunction):
753 def __call__(self, group_name=None, check_Location=''):
752 def __call__(self, group_name=None, check_Location=''):
754 self.group_name = group_name
753 self.group_name = group_name
755 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
754 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
756
755
757 def check_permissions(self):
756 def check_permissions(self):
758 try:
757 try:
759 self._user_perms = set(
758 self._user_perms = set(
760 [self.user_perms['repositories_groups'][self.group_name]]
759 [self.user_perms['repositories_groups'][self.group_name]]
761 )
760 )
762 except KeyError:
761 except KeyError:
763 return False
762 return False
764 if self.required_perms.intersection(self._user_perms):
763 if self.required_perms.intersection(self._user_perms):
765 return True
764 return True
766 return False
765 return False
767
766
768
767
769 class HasReposGroupPermissionAll(PermsFunction):
768 class HasReposGroupPermissionAll(PermsFunction):
770 def __call__(self, group_name=None, check_Location=''):
769 def __call__(self, group_name=None, check_Location=''):
771 self.group_name = group_name
770 self.group_name = group_name
772 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
771 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
773
772
774 def check_permissions(self):
773 def check_permissions(self):
775 try:
774 try:
776 self._user_perms = set(
775 self._user_perms = set(
777 [self.user_perms['repositories_groups'][self.group_name]]
776 [self.user_perms['repositories_groups'][self.group_name]]
778 )
777 )
779 except KeyError:
778 except KeyError:
780 return False
779 return False
781 if self.required_perms.issubset(self._user_perms):
780 if self.required_perms.issubset(self._user_perms):
782 return True
781 return True
783 return False
782 return False
784
783
785
784
786 #==============================================================================
785 #==============================================================================
787 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
786 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
788 #==============================================================================
787 #==============================================================================
789 class HasPermissionAnyMiddleware(object):
788 class HasPermissionAnyMiddleware(object):
790 def __init__(self, *perms):
789 def __init__(self, *perms):
791 self.required_perms = set(perms)
790 self.required_perms = set(perms)
792
791
793 def __call__(self, user, repo_name):
792 def __call__(self, user, repo_name):
794 # repo_name MUST be unicode, since we handle keys in permission
793 # repo_name MUST be unicode, since we handle keys in permission
795 # dict by unicode
794 # dict by unicode
796 repo_name = safe_unicode(repo_name)
795 repo_name = safe_unicode(repo_name)
797 usr = AuthUser(user.user_id)
796 usr = AuthUser(user.user_id)
798 try:
797 try:
799 self.user_perms = set([usr.permissions['repositories'][repo_name]])
798 self.user_perms = set([usr.permissions['repositories'][repo_name]])
800 except Exception:
799 except Exception:
801 log.error('Exception while accessing permissions %s' %
800 log.error('Exception while accessing permissions %s' %
802 traceback.format_exc())
801 traceback.format_exc())
803 self.user_perms = set()
802 self.user_perms = set()
804 self.username = user.username
803 self.username = user.username
805 self.repo_name = repo_name
804 self.repo_name = repo_name
806 return self.check_permissions()
805 return self.check_permissions()
807
806
808 def check_permissions(self):
807 def check_permissions(self):
809 log.debug('checking mercurial protocol '
808 log.debug('checking mercurial protocol '
810 'permissions %s for user:%s repository:%s', self.user_perms,
809 'permissions %s for user:%s repository:%s', self.user_perms,
811 self.username, self.repo_name)
810 self.username, self.repo_name)
812 if self.required_perms.intersection(self.user_perms):
811 if self.required_perms.intersection(self.user_perms):
813 log.debug('permission granted for user:%s on repo:%s' % (
812 log.debug('permission granted for user:%s on repo:%s' % (
814 self.username, self.repo_name
813 self.username, self.repo_name
815 )
814 )
816 )
815 )
817 return True
816 return True
818 log.debug('permission denied for user:%s on repo:%s' % (
817 log.debug('permission denied for user:%s on repo:%s' % (
819 self.username, self.repo_name
818 self.username, self.repo_name
820 )
819 )
821 )
820 )
822 return False
821 return False
@@ -1,301 +1,308
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import logging
22 import logging
23
23
24 import formencode
24 import formencode
25 from formencode import All
25 from formencode import All
26
26
27 from pylons.i18n.translation import _
27 from pylons.i18n.translation import _
28
28
29 from rhodecode.model import validators as v
29 from rhodecode.model import validators as v
30 from rhodecode import BACKENDS
30 from rhodecode import BACKENDS
31
31
32 log = logging.getLogger(__name__)
32 log = logging.getLogger(__name__)
33
33
34
34
35 class LoginForm(formencode.Schema):
35 class LoginForm(formencode.Schema):
36 allow_extra_fields = True
36 allow_extra_fields = True
37 filter_extra_fields = True
37 filter_extra_fields = True
38 username = v.UnicodeString(
38 username = v.UnicodeString(
39 strip=True,
39 strip=True,
40 min=1,
40 min=1,
41 not_empty=True,
41 not_empty=True,
42 messages={
42 messages={
43 'empty': _(u'Please enter a login'),
43 'empty': _(u'Please enter a login'),
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
44 'tooShort': _(u'Enter a value %(min)i characters long or more')}
45 )
45 )
46
46
47 password = v.UnicodeString(
47 password = v.UnicodeString(
48 strip=False,
48 strip=False,
49 min=3,
49 min=3,
50 not_empty=True,
50 not_empty=True,
51 messages={
51 messages={
52 'empty': _(u'Please enter a password'),
52 'empty': _(u'Please enter a password'),
53 'tooShort': _(u'Enter %(min)i characters or more')}
53 'tooShort': _(u'Enter %(min)i characters or more')}
54 )
54 )
55
55
56 remember = v.StringBoolean(if_missing=False)
56 remember = v.StringBoolean(if_missing=False)
57
57
58 chained_validators = [v.ValidAuth()]
58 chained_validators = [v.ValidAuth()]
59
59
60
60
61 def UserForm(edit=False, old_data={}):
61 def UserForm(edit=False, old_data={}):
62 class _UserForm(formencode.Schema):
62 class _UserForm(formencode.Schema):
63 allow_extra_fields = True
63 allow_extra_fields = True
64 filter_extra_fields = True
64 filter_extra_fields = True
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
65 username = All(v.UnicodeString(strip=True, min=1, not_empty=True),
66 v.ValidUsername(edit, old_data))
66 v.ValidUsername(edit, old_data))
67 if edit:
67 if edit:
68 new_password = All(
68 new_password = All(
69 v.UnicodeString(strip=False, min=6, not_empty=False)
69 v.UnicodeString(strip=False, min=6, not_empty=False)
70 )
70 )
71 password_confirmation = All(
71 password_confirmation = All(
72 v.ValidPassword(),
72 v.ValidPassword(),
73 v.UnicodeString(strip=False, min=6, not_empty=False),
73 v.UnicodeString(strip=False, min=6, not_empty=False),
74 )
74 )
75 admin = v.StringBoolean(if_missing=False)
75 admin = v.StringBoolean(if_missing=False)
76 else:
76 else:
77 password = All(
77 password = All(
78 v.ValidPassword(),
78 v.ValidPassword(),
79 v.UnicodeString(strip=False, min=6, not_empty=True)
79 v.UnicodeString(strip=False, min=6, not_empty=True)
80 )
80 )
81 password_confirmation = All(
81 password_confirmation = All(
82 v.ValidPassword(),
82 v.ValidPassword(),
83 v.UnicodeString(strip=False, min=6, not_empty=False)
83 v.UnicodeString(strip=False, min=6, not_empty=False)
84 )
84 )
85
85
86 active = v.StringBoolean(if_missing=False)
86 active = v.StringBoolean(if_missing=False)
87 name = v.UnicodeString(strip=True, min=1, not_empty=False)
87 name = v.UnicodeString(strip=True, min=1, not_empty=False)
88 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
88 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
89 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
89 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
90
90
91 chained_validators = [v.ValidPasswordsMatch()]
91 chained_validators = [v.ValidPasswordsMatch()]
92
92
93 return _UserForm
93 return _UserForm
94
94
95
95
96 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
96 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
97 class _UsersGroupForm(formencode.Schema):
97 class _UsersGroupForm(formencode.Schema):
98 allow_extra_fields = True
98 allow_extra_fields = True
99 filter_extra_fields = True
99 filter_extra_fields = True
100
100
101 users_group_name = All(
101 users_group_name = All(
102 v.UnicodeString(strip=True, min=1, not_empty=True),
102 v.UnicodeString(strip=True, min=1, not_empty=True),
103 v.ValidUsersGroup(edit, old_data)
103 v.ValidUsersGroup(edit, old_data)
104 )
104 )
105
105
106 users_group_active = v.StringBoolean(if_missing=False)
106 users_group_active = v.StringBoolean(if_missing=False)
107
107
108 if edit:
108 if edit:
109 users_group_members = v.OneOf(
109 users_group_members = v.OneOf(
110 available_members, hideList=False, testValueList=True,
110 available_members, hideList=False, testValueList=True,
111 if_missing=None, not_empty=False
111 if_missing=None, not_empty=False
112 )
112 )
113
113
114 return _UsersGroupForm
114 return _UsersGroupForm
115
115
116
116
117 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
117 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
118 class _ReposGroupForm(formencode.Schema):
118 class _ReposGroupForm(formencode.Schema):
119 allow_extra_fields = True
119 allow_extra_fields = True
120 filter_extra_fields = False
120 filter_extra_fields = False
121
121
122 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
122 group_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
123 v.SlugifyName())
123 v.SlugifyName())
124 group_description = v.UnicodeString(strip=True, min=1,
124 group_description = v.UnicodeString(strip=True, min=1,
125 not_empty=True)
125 not_empty=True)
126 group_parent_id = v.OneOf(available_groups, hideList=False,
126 group_parent_id = v.OneOf(available_groups, hideList=False,
127 testValueList=True,
127 testValueList=True,
128 if_missing=None, not_empty=False)
128 if_missing=None, not_empty=False)
129
129
130 chained_validators = [v.ValidReposGroup(edit, old_data),
130 chained_validators = [v.ValidReposGroup(edit, old_data),
131 v.ValidPerms('group')]
131 v.ValidPerms('group')]
132
132
133 return _ReposGroupForm
133 return _ReposGroupForm
134
134
135
135
136 def RegisterForm(edit=False, old_data={}):
136 def RegisterForm(edit=False, old_data={}):
137 class _RegisterForm(formencode.Schema):
137 class _RegisterForm(formencode.Schema):
138 allow_extra_fields = True
138 allow_extra_fields = True
139 filter_extra_fields = True
139 filter_extra_fields = True
140 username = All(
140 username = All(
141 v.ValidUsername(edit, old_data),
141 v.ValidUsername(edit, old_data),
142 v.UnicodeString(strip=True, min=1, not_empty=True)
142 v.UnicodeString(strip=True, min=1, not_empty=True)
143 )
143 )
144 password = All(
144 password = All(
145 v.ValidPassword(),
145 v.ValidPassword(),
146 v.UnicodeString(strip=False, min=6, not_empty=True)
146 v.UnicodeString(strip=False, min=6, not_empty=True)
147 )
147 )
148 password_confirmation = All(
148 password_confirmation = All(
149 v.ValidPassword(),
149 v.ValidPassword(),
150 v.UnicodeString(strip=False, min=6, not_empty=True)
150 v.UnicodeString(strip=False, min=6, not_empty=True)
151 )
151 )
152 active = v.StringBoolean(if_missing=False)
152 active = v.StringBoolean(if_missing=False)
153 name = v.UnicodeString(strip=True, min=1, not_empty=False)
153 name = v.UnicodeString(strip=True, min=1, not_empty=False)
154 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
154 lastname = v.UnicodeString(strip=True, min=1, not_empty=False)
155 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
155 email = All(v.Email(not_empty=True), v.UniqSystemEmail(old_data))
156
156
157 chained_validators = [v.ValidPasswordsMatch()]
157 chained_validators = [v.ValidPasswordsMatch()]
158
158
159 return _RegisterForm
159 return _RegisterForm
160
160
161
161
162 def PasswordResetForm():
162 def PasswordResetForm():
163 class _PasswordResetForm(formencode.Schema):
163 class _PasswordResetForm(formencode.Schema):
164 allow_extra_fields = True
164 allow_extra_fields = True
165 filter_extra_fields = True
165 filter_extra_fields = True
166 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
166 email = All(v.ValidSystemEmail(), v.Email(not_empty=True))
167 return _PasswordResetForm
167 return _PasswordResetForm
168
168
169
169
170 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
170 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
171 repo_groups=[], landing_revs=[]):
171 repo_groups=[], landing_revs=[]):
172 class _RepoForm(formencode.Schema):
172 class _RepoForm(formencode.Schema):
173 allow_extra_fields = True
173 allow_extra_fields = True
174 filter_extra_fields = False
174 filter_extra_fields = False
175 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
175 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
176 v.SlugifyName())
176 v.SlugifyName())
177 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
177 clone_uri = All(v.UnicodeString(strip=True, min=1, not_empty=False))
178 repo_group = v.OneOf(repo_groups, hideList=True)
178 repo_group = v.OneOf(repo_groups, hideList=True)
179 repo_type = v.OneOf(supported_backends)
179 repo_type = v.OneOf(supported_backends)
180 description = v.UnicodeString(strip=True, min=1, not_empty=False)
180 description = v.UnicodeString(strip=True, min=1, not_empty=False)
181 private = v.StringBoolean(if_missing=False)
181 private = v.StringBoolean(if_missing=False)
182 enable_statistics = v.StringBoolean(if_missing=False)
182 enable_statistics = v.StringBoolean(if_missing=False)
183 enable_downloads = v.StringBoolean(if_missing=False)
183 enable_downloads = v.StringBoolean(if_missing=False)
184 landing_rev = v.OneOf(landing_revs, hideList=True)
184 landing_rev = v.OneOf(landing_revs, hideList=True)
185
185
186 if edit:
186 if edit:
187 #this is repo owner
187 #this is repo owner
188 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
188 user = All(v.UnicodeString(not_empty=True), v.ValidRepoUser())
189
189
190 chained_validators = [v.ValidCloneUri(),
190 chained_validators = [v.ValidCloneUri(),
191 v.ValidRepoName(edit, old_data),
191 v.ValidRepoName(edit, old_data),
192 v.ValidPerms()]
192 v.ValidPerms()]
193 return _RepoForm
193 return _RepoForm
194
194
195
195
196 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
196 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
197 repo_groups=[]):
197 repo_groups=[]):
198 class _RepoForkForm(formencode.Schema):
198 class _RepoForkForm(formencode.Schema):
199 allow_extra_fields = True
199 allow_extra_fields = True
200 filter_extra_fields = False
200 filter_extra_fields = False
201 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
201 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
202 v.SlugifyName())
202 v.SlugifyName())
203 repo_group = v.OneOf(repo_groups, hideList=True)
203 repo_group = v.OneOf(repo_groups, hideList=True)
204 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
204 repo_type = All(v.ValidForkType(old_data), v.OneOf(supported_backends))
205 description = v.UnicodeString(strip=True, min=1, not_empty=True)
205 description = v.UnicodeString(strip=True, min=1, not_empty=True)
206 private = v.StringBoolean(if_missing=False)
206 private = v.StringBoolean(if_missing=False)
207 copy_permissions = v.StringBoolean(if_missing=False)
207 copy_permissions = v.StringBoolean(if_missing=False)
208 update_after_clone = v.StringBoolean(if_missing=False)
208 update_after_clone = v.StringBoolean(if_missing=False)
209 fork_parent_id = v.UnicodeString()
209 fork_parent_id = v.UnicodeString()
210 chained_validators = [v.ValidForkName(edit, old_data)]
210 chained_validators = [v.ValidForkName(edit, old_data)]
211
211
212 return _RepoForkForm
212 return _RepoForkForm
213
213
214
214
215 def RepoSettingsForm(edit=False, old_data={},
215 def RepoSettingsForm(edit=False, old_data={},
216 supported_backends=BACKENDS.keys(), repo_groups=[],
216 supported_backends=BACKENDS.keys(), repo_groups=[],
217 landing_revs=[]):
217 landing_revs=[]):
218 class _RepoForm(formencode.Schema):
218 class _RepoForm(formencode.Schema):
219 allow_extra_fields = True
219 allow_extra_fields = True
220 filter_extra_fields = False
220 filter_extra_fields = False
221 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
221 repo_name = All(v.UnicodeString(strip=True, min=1, not_empty=True),
222 v.SlugifyName())
222 v.SlugifyName())
223 description = v.UnicodeString(strip=True, min=1, not_empty=True)
223 description = v.UnicodeString(strip=True, min=1, not_empty=True)
224 repo_group = v.OneOf(repo_groups, hideList=True)
224 repo_group = v.OneOf(repo_groups, hideList=True)
225 private = v.StringBoolean(if_missing=False)
225 private = v.StringBoolean(if_missing=False)
226 landing_rev = v.OneOf(landing_revs, hideList=True)
226 landing_rev = v.OneOf(landing_revs, hideList=True)
227 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
227 chained_validators = [v.ValidRepoName(edit, old_data), v.ValidPerms(),
228 v.ValidSettings()]
228 v.ValidSettings()]
229 return _RepoForm
229 return _RepoForm
230
230
231
231
232 def ApplicationSettingsForm():
232 def ApplicationSettingsForm():
233 class _ApplicationSettingsForm(formencode.Schema):
233 class _ApplicationSettingsForm(formencode.Schema):
234 allow_extra_fields = True
234 allow_extra_fields = True
235 filter_extra_fields = False
235 filter_extra_fields = False
236 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
236 rhodecode_title = v.UnicodeString(strip=True, min=1, not_empty=True)
237 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
237 rhodecode_realm = v.UnicodeString(strip=True, min=1, not_empty=True)
238 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
238 rhodecode_ga_code = v.UnicodeString(strip=True, min=1, not_empty=False)
239
239
240 return _ApplicationSettingsForm
240 return _ApplicationSettingsForm
241
241
242
242
243 def ApplicationUiSettingsForm():
243 def ApplicationUiSettingsForm():
244 class _ApplicationUiSettingsForm(formencode.Schema):
244 class _ApplicationUiSettingsForm(formencode.Schema):
245 allow_extra_fields = True
245 allow_extra_fields = True
246 filter_extra_fields = False
246 filter_extra_fields = False
247 web_push_ssl = v.OneOf(['true', 'false'], if_missing='false')
247 web_push_ssl = v.OneOf(['true', 'false'], if_missing='false')
248 paths_root_path = All(
248 paths_root_path = All(
249 v.ValidPath(),
249 v.ValidPath(),
250 v.UnicodeString(strip=True, min=1, not_empty=True)
250 v.UnicodeString(strip=True, min=1, not_empty=True)
251 )
251 )
252 hooks_changegroup_update = v.OneOf(['True', 'False'],
252 hooks_changegroup_update = v.OneOf(['True', 'False'],
253 if_missing=False)
253 if_missing=False)
254 hooks_changegroup_repo_size = v.OneOf(['True', 'False'],
254 hooks_changegroup_repo_size = v.OneOf(['True', 'False'],
255 if_missing=False)
255 if_missing=False)
256 hooks_changegroup_push_logger = v.OneOf(['True', 'False'],
256 hooks_changegroup_push_logger = v.OneOf(['True', 'False'],
257 if_missing=False)
257 if_missing=False)
258 hooks_preoutgoing_pull_logger = v.OneOf(['True', 'False'],
258 hooks_preoutgoing_pull_logger = v.OneOf(['True', 'False'],
259 if_missing=False)
259 if_missing=False)
260
260
261 return _ApplicationUiSettingsForm
261 return _ApplicationUiSettingsForm
262
262
263
263
264 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
264 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
265 class _DefaultPermissionsForm(formencode.Schema):
265 class _DefaultPermissionsForm(formencode.Schema):
266 allow_extra_fields = True
266 allow_extra_fields = True
267 filter_extra_fields = True
267 filter_extra_fields = True
268 overwrite_default = v.StringBoolean(if_missing=False)
268 overwrite_default = v.StringBoolean(if_missing=False)
269 anonymous = v.OneOf(['True', 'False'], if_missing=False)
269 anonymous = v.OneOf(['True', 'False'], if_missing=False)
270 default_perm = v.OneOf(perms_choices)
270 default_perm = v.OneOf(perms_choices)
271 default_register = v.OneOf(register_choices)
271 default_register = v.OneOf(register_choices)
272 default_create = v.OneOf(create_choices)
272 default_create = v.OneOf(create_choices)
273
273
274 return _DefaultPermissionsForm
274 return _DefaultPermissionsForm
275
275
276
276
277 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
277 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices,
278 tls_kind_choices):
278 tls_kind_choices):
279 class _LdapSettingsForm(formencode.Schema):
279 class _LdapSettingsForm(formencode.Schema):
280 allow_extra_fields = True
280 allow_extra_fields = True
281 filter_extra_fields = True
281 filter_extra_fields = True
282 #pre_validators = [LdapLibValidator]
282 #pre_validators = [LdapLibValidator]
283 ldap_active = v.StringBoolean(if_missing=False)
283 ldap_active = v.StringBoolean(if_missing=False)
284 ldap_host = v.UnicodeString(strip=True,)
284 ldap_host = v.UnicodeString(strip=True,)
285 ldap_port = v.Number(strip=True,)
285 ldap_port = v.Number(strip=True,)
286 ldap_tls_kind = v.OneOf(tls_kind_choices)
286 ldap_tls_kind = v.OneOf(tls_kind_choices)
287 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
287 ldap_tls_reqcert = v.OneOf(tls_reqcert_choices)
288 ldap_dn_user = v.UnicodeString(strip=True,)
288 ldap_dn_user = v.UnicodeString(strip=True,)
289 ldap_dn_pass = v.UnicodeString(strip=True,)
289 ldap_dn_pass = v.UnicodeString(strip=True,)
290 ldap_base_dn = v.UnicodeString(strip=True,)
290 ldap_base_dn = v.UnicodeString(strip=True,)
291 ldap_filter = v.UnicodeString(strip=True,)
291 ldap_filter = v.UnicodeString(strip=True,)
292 ldap_search_scope = v.OneOf(search_scope_choices)
292 ldap_search_scope = v.OneOf(search_scope_choices)
293 ldap_attr_login = All(
293 ldap_attr_login = All(
294 v.AttrLoginValidator(),
294 v.AttrLoginValidator(),
295 v.UnicodeString(strip=True,)
295 v.UnicodeString(strip=True,)
296 )
296 )
297 ldap_attr_firstname = v.UnicodeString(strip=True,)
297 ldap_attr_firstname = v.UnicodeString(strip=True,)
298 ldap_attr_lastname = v.UnicodeString(strip=True,)
298 ldap_attr_lastname = v.UnicodeString(strip=True,)
299 ldap_attr_email = v.UnicodeString(strip=True,)
299 ldap_attr_email = v.UnicodeString(strip=True,)
300
300
301 return _LdapSettingsForm
301 return _LdapSettingsForm
302
303
304 def UserExtraEmailForm():
305 class _UserExtraEmailForm(formencode.Schema):
306 email = All(v.UniqSystemEmail(), v.Email)
307
308 return _UserExtraEmailForm No newline at end of file
@@ -1,613 +1,616
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 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 modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
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, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28
28
29 from pylons import url
29 from pylons import url
30 from pylons.i18n.translation import _
30 from pylons.i18n.translation import _
31
31
32 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.orm import joinedload
34
32 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
35 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
33 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.caching_query import FromCache
34
35 from rhodecode.model import BaseModel
37 from rhodecode.model import BaseModel
36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
38 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
39 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
38 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
40 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
39 UserEmailMap
41 UserEmailMap
40 from rhodecode.lib.exceptions import DefaultUserException, \
42 from rhodecode.lib.exceptions import DefaultUserException, \
41 UserOwnsReposException
43 UserOwnsReposException
42
44
43 from sqlalchemy.exc import DatabaseError
44
45 from sqlalchemy.orm import joinedload
46
45
47 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
48
47
49
48
50 PERM_WEIGHTS = {
49 PERM_WEIGHTS = {
51 'repository.none': 0,
50 'repository.none': 0,
52 'repository.read': 1,
51 'repository.read': 1,
53 'repository.write': 3,
52 'repository.write': 3,
54 'repository.admin': 4,
53 'repository.admin': 4,
55 'group.none': 0,
54 'group.none': 0,
56 'group.read': 1,
55 'group.read': 1,
57 'group.write': 3,
56 'group.write': 3,
58 'group.admin': 4,
57 'group.admin': 4,
59 }
58 }
60
59
61
60
62 class UserModel(BaseModel):
61 class UserModel(BaseModel):
63
62
64 def get(self, user_id, cache=False):
63 def get(self, user_id, cache=False):
65 user = self.sa.query(User)
64 user = self.sa.query(User)
66 if cache:
65 if cache:
67 user = user.options(FromCache("sql_cache_short",
66 user = user.options(FromCache("sql_cache_short",
68 "get_user_%s" % user_id))
67 "get_user_%s" % user_id))
69 return user.get(user_id)
68 return user.get(user_id)
70
69
71 def get_user(self, user):
70 def get_user(self, user):
72 return self._get_user(user)
71 return self._get_user(user)
73
72
74 def get_by_username(self, username, cache=False, case_insensitive=False):
73 def get_by_username(self, username, cache=False, case_insensitive=False):
75
74
76 if case_insensitive:
75 if case_insensitive:
77 user = self.sa.query(User).filter(User.username.ilike(username))
76 user = self.sa.query(User).filter(User.username.ilike(username))
78 else:
77 else:
79 user = self.sa.query(User)\
78 user = self.sa.query(User)\
80 .filter(User.username == username)
79 .filter(User.username == username)
81 if cache:
80 if cache:
82 user = user.options(FromCache("sql_cache_short",
81 user = user.options(FromCache("sql_cache_short",
83 "get_user_%s" % username))
82 "get_user_%s" % username))
84 return user.scalar()
83 return user.scalar()
85
84
86 def get_by_api_key(self, api_key, cache=False):
85 def get_by_api_key(self, api_key, cache=False):
87 return User.get_by_api_key(api_key, cache)
86 return User.get_by_api_key(api_key, cache)
88
87
89 def create(self, form_data):
88 def create(self, form_data):
90 from rhodecode.lib.auth import get_crypt_password
89 from rhodecode.lib.auth import get_crypt_password
91 try:
90 try:
92 new_user = User()
91 new_user = User()
93 for k, v in form_data.items():
92 for k, v in form_data.items():
94 if k == 'password':
93 if k == 'password':
95 v = get_crypt_password(v)
94 v = get_crypt_password(v)
96 setattr(new_user, k, v)
95 setattr(new_user, k, v)
97
96
98 new_user.api_key = generate_api_key(form_data['username'])
97 new_user.api_key = generate_api_key(form_data['username'])
99 self.sa.add(new_user)
98 self.sa.add(new_user)
100 return new_user
99 return new_user
101 except:
100 except:
102 log.error(traceback.format_exc())
101 log.error(traceback.format_exc())
103 raise
102 raise
104
103
105 def create_or_update(self, username, password, email, name, lastname,
104 def create_or_update(self, username, password, email, name, lastname,
106 active=True, admin=False, ldap_dn=None):
105 active=True, admin=False, ldap_dn=None):
107 """
106 """
108 Creates a new instance if not found, or updates current one
107 Creates a new instance if not found, or updates current one
109
108
110 :param username:
109 :param username:
111 :param password:
110 :param password:
112 :param email:
111 :param email:
113 :param active:
112 :param active:
114 :param name:
113 :param name:
115 :param lastname:
114 :param lastname:
116 :param active:
115 :param active:
117 :param admin:
116 :param admin:
118 :param ldap_dn:
117 :param ldap_dn:
119 """
118 """
120
119
121 from rhodecode.lib.auth import get_crypt_password
120 from rhodecode.lib.auth import get_crypt_password
122
121
123 log.debug('Checking for %s account in RhodeCode database' % username)
122 log.debug('Checking for %s account in RhodeCode database' % username)
124 user = User.get_by_username(username, case_insensitive=True)
123 user = User.get_by_username(username, case_insensitive=True)
125 if user is None:
124 if user is None:
126 log.debug('creating new user %s' % username)
125 log.debug('creating new user %s' % username)
127 new_user = User()
126 new_user = User()
128 else:
127 else:
129 log.debug('updating user %s' % username)
128 log.debug('updating user %s' % username)
130 new_user = user
129 new_user = user
131
130
132 try:
131 try:
133 new_user.username = username
132 new_user.username = username
134 new_user.admin = admin
133 new_user.admin = admin
135 new_user.password = get_crypt_password(password)
134 new_user.password = get_crypt_password(password)
136 new_user.api_key = generate_api_key(username)
135 new_user.api_key = generate_api_key(username)
137 new_user.email = email
136 new_user.email = email
138 new_user.active = active
137 new_user.active = active
139 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
138 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
140 new_user.name = name
139 new_user.name = name
141 new_user.lastname = lastname
140 new_user.lastname = lastname
142 self.sa.add(new_user)
141 self.sa.add(new_user)
143 return new_user
142 return new_user
144 except (DatabaseError,):
143 except (DatabaseError,):
145 log.error(traceback.format_exc())
144 log.error(traceback.format_exc())
146 raise
145 raise
147
146
148 def create_for_container_auth(self, username, attrs):
147 def create_for_container_auth(self, username, attrs):
149 """
148 """
150 Creates the given user if it's not already in the database
149 Creates the given user if it's not already in the database
151
150
152 :param username:
151 :param username:
153 :param attrs:
152 :param attrs:
154 """
153 """
155 if self.get_by_username(username, case_insensitive=True) is None:
154 if self.get_by_username(username, case_insensitive=True) is None:
156
155
157 # autogenerate email for container account without one
156 # autogenerate email for container account without one
158 generate_email = lambda usr: '%s@container_auth.account' % usr
157 generate_email = lambda usr: '%s@container_auth.account' % usr
159
158
160 try:
159 try:
161 new_user = User()
160 new_user = User()
162 new_user.username = username
161 new_user.username = username
163 new_user.password = None
162 new_user.password = None
164 new_user.api_key = generate_api_key(username)
163 new_user.api_key = generate_api_key(username)
165 new_user.email = attrs['email']
164 new_user.email = attrs['email']
166 new_user.active = attrs.get('active', True)
165 new_user.active = attrs.get('active', True)
167 new_user.name = attrs['name'] or generate_email(username)
166 new_user.name = attrs['name'] or generate_email(username)
168 new_user.lastname = attrs['lastname']
167 new_user.lastname = attrs['lastname']
169
168
170 self.sa.add(new_user)
169 self.sa.add(new_user)
171 return new_user
170 return new_user
172 except (DatabaseError,):
171 except (DatabaseError,):
173 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
174 self.sa.rollback()
173 self.sa.rollback()
175 raise
174 raise
176 log.debug('User %s already exists. Skipping creation of account'
175 log.debug('User %s already exists. Skipping creation of account'
177 ' for container auth.', username)
176 ' for container auth.', username)
178 return None
177 return None
179
178
180 def create_ldap(self, username, password, user_dn, attrs):
179 def create_ldap(self, username, password, user_dn, attrs):
181 """
180 """
182 Checks if user is in database, if not creates this user marked
181 Checks if user is in database, if not creates this user marked
183 as ldap user
182 as ldap user
184
183
185 :param username:
184 :param username:
186 :param password:
185 :param password:
187 :param user_dn:
186 :param user_dn:
188 :param attrs:
187 :param attrs:
189 """
188 """
190 from rhodecode.lib.auth import get_crypt_password
189 from rhodecode.lib.auth import get_crypt_password
191 log.debug('Checking for such ldap account in RhodeCode database')
190 log.debug('Checking for such ldap account in RhodeCode database')
192 if self.get_by_username(username, case_insensitive=True) is None:
191 if self.get_by_username(username, case_insensitive=True) is None:
193
192
194 # autogenerate email for ldap account without one
193 # autogenerate email for ldap account without one
195 generate_email = lambda usr: '%s@ldap.account' % usr
194 generate_email = lambda usr: '%s@ldap.account' % usr
196
195
197 try:
196 try:
198 new_user = User()
197 new_user = User()
199 username = username.lower()
198 username = username.lower()
200 # add ldap account always lowercase
199 # add ldap account always lowercase
201 new_user.username = username
200 new_user.username = username
202 new_user.password = get_crypt_password(password)
201 new_user.password = get_crypt_password(password)
203 new_user.api_key = generate_api_key(username)
202 new_user.api_key = generate_api_key(username)
204 new_user.email = attrs['email'] or generate_email(username)
203 new_user.email = attrs['email'] or generate_email(username)
205 new_user.active = attrs.get('active', True)
204 new_user.active = attrs.get('active', True)
206 new_user.ldap_dn = safe_unicode(user_dn)
205 new_user.ldap_dn = safe_unicode(user_dn)
207 new_user.name = attrs['name']
206 new_user.name = attrs['name']
208 new_user.lastname = attrs['lastname']
207 new_user.lastname = attrs['lastname']
209
208
210 self.sa.add(new_user)
209 self.sa.add(new_user)
211 return new_user
210 return new_user
212 except (DatabaseError,):
211 except (DatabaseError,):
213 log.error(traceback.format_exc())
212 log.error(traceback.format_exc())
214 self.sa.rollback()
213 self.sa.rollback()
215 raise
214 raise
216 log.debug('this %s user exists skipping creation of ldap account',
215 log.debug('this %s user exists skipping creation of ldap account',
217 username)
216 username)
218 return None
217 return None
219
218
220 def create_registration(self, form_data):
219 def create_registration(self, form_data):
221 from rhodecode.model.notification import NotificationModel
220 from rhodecode.model.notification import NotificationModel
222
221
223 try:
222 try:
224 form_data['admin'] = False
223 form_data['admin'] = False
225 new_user = self.create(form_data)
224 new_user = self.create(form_data)
226
225
227 self.sa.add(new_user)
226 self.sa.add(new_user)
228 self.sa.flush()
227 self.sa.flush()
229
228
230 # notification to admins
229 # notification to admins
231 subject = _('new user registration')
230 subject = _('new user registration')
232 body = ('New user registration\n'
231 body = ('New user registration\n'
233 '---------------------\n'
232 '---------------------\n'
234 '- Username: %s\n'
233 '- Username: %s\n'
235 '- Full Name: %s\n'
234 '- Full Name: %s\n'
236 '- Email: %s\n')
235 '- Email: %s\n')
237 body = body % (new_user.username, new_user.full_name,
236 body = body % (new_user.username, new_user.full_name,
238 new_user.email)
237 new_user.email)
239 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
238 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
240 kw = {'registered_user_url': edit_url}
239 kw = {'registered_user_url': edit_url}
241 NotificationModel().create(created_by=new_user, subject=subject,
240 NotificationModel().create(created_by=new_user, subject=subject,
242 body=body, recipients=None,
241 body=body, recipients=None,
243 type_=Notification.TYPE_REGISTRATION,
242 type_=Notification.TYPE_REGISTRATION,
244 email_kwargs=kw)
243 email_kwargs=kw)
245
244
246 except:
245 except:
247 log.error(traceback.format_exc())
246 log.error(traceback.format_exc())
248 raise
247 raise
249
248
250 def update(self, user_id, form_data):
249 def update(self, user_id, form_data):
251 try:
250 try:
252 user = self.get(user_id, cache=False)
251 user = self.get(user_id, cache=False)
253 if user.username == 'default':
252 if user.username == 'default':
254 raise DefaultUserException(
253 raise DefaultUserException(
255 _("You can't Edit this user since it's"
254 _("You can't Edit this user since it's"
256 " crucial for entire application"))
255 " crucial for entire application"))
257
256
258 for k, v in form_data.items():
257 for k, v in form_data.items():
259 if k == 'new_password' and v != '':
258 if k == 'new_password' and v != '':
260 user.password = v
259 user.password = v
261 user.api_key = generate_api_key(user.username)
260 user.api_key = generate_api_key(user.username)
262 else:
261 else:
263 setattr(user, k, v)
262 setattr(user, k, v)
264
263
265 self.sa.add(user)
264 self.sa.add(user)
266 except:
265 except:
267 log.error(traceback.format_exc())
266 log.error(traceback.format_exc())
268 raise
267 raise
269
268
270 def update_my_account(self, user_id, form_data):
269 def update_my_account(self, user_id, form_data):
271 from rhodecode.lib.auth import get_crypt_password
270 from rhodecode.lib.auth import get_crypt_password
272 try:
271 try:
273 user = self.get(user_id, cache=False)
272 user = self.get(user_id, cache=False)
274 if user.username == 'default':
273 if user.username == 'default':
275 raise DefaultUserException(
274 raise DefaultUserException(
276 _("You can't Edit this user since it's"
275 _("You can't Edit this user since it's"
277 " crucial for entire application")
276 " crucial for entire application")
278 )
277 )
279 for k, v in form_data.items():
278 for k, v in form_data.items():
280 if k == 'new_password' and v != '':
279 if k == 'new_password' and v != '':
281 user.password = get_crypt_password(v)
280 user.password = get_crypt_password(v)
282 user.api_key = generate_api_key(user.username)
281 user.api_key = generate_api_key(user.username)
283 else:
282 else:
284 if k not in ['admin', 'active']:
283 if k not in ['admin', 'active']:
285 setattr(user, k, v)
284 setattr(user, k, v)
286
285
287 self.sa.add(user)
286 self.sa.add(user)
288 except:
287 except:
289 log.error(traceback.format_exc())
288 log.error(traceback.format_exc())
290 raise
289 raise
291
290
292 def delete(self, user):
291 def delete(self, user):
293 user = self._get_user(user)
292 user = self._get_user(user)
294
293
295 try:
294 try:
296 if user.username == 'default':
295 if user.username == 'default':
297 raise DefaultUserException(
296 raise DefaultUserException(
298 _(u"You can't remove this user since it's"
297 _(u"You can't remove this user since it's"
299 " crucial for entire application")
298 " crucial for entire application")
300 )
299 )
301 if user.repositories:
300 if user.repositories:
302 repos = [x.repo_name for x in user.repositories]
301 repos = [x.repo_name for x in user.repositories]
303 raise UserOwnsReposException(
302 raise UserOwnsReposException(
304 _(u'user "%s" still owns %s repositories and cannot be '
303 _(u'user "%s" still owns %s repositories and cannot be '
305 'removed. Switch owners or remove those repositories. %s')
304 'removed. Switch owners or remove those repositories. %s')
306 % (user.username, len(repos), ', '.join(repos))
305 % (user.username, len(repos), ', '.join(repos))
307 )
306 )
308 self.sa.delete(user)
307 self.sa.delete(user)
309 except:
308 except:
310 log.error(traceback.format_exc())
309 log.error(traceback.format_exc())
311 raise
310 raise
312
311
313 def reset_password_link(self, data):
312 def reset_password_link(self, data):
314 from rhodecode.lib.celerylib import tasks, run_task
313 from rhodecode.lib.celerylib import tasks, run_task
315 run_task(tasks.send_password_link, data['email'])
314 run_task(tasks.send_password_link, data['email'])
316
315
317 def reset_password(self, data):
316 def reset_password(self, data):
318 from rhodecode.lib.celerylib import tasks, run_task
317 from rhodecode.lib.celerylib import tasks, run_task
319 run_task(tasks.reset_user_password, data['email'])
318 run_task(tasks.reset_user_password, data['email'])
320
319
321 def fill_data(self, auth_user, user_id=None, api_key=None):
320 def fill_data(self, auth_user, user_id=None, api_key=None):
322 """
321 """
323 Fetches auth_user by user_id,or api_key if present.
322 Fetches auth_user by user_id,or api_key if present.
324 Fills auth_user attributes with those taken from database.
323 Fills auth_user attributes with those taken from database.
325 Additionally set's is_authenitated if lookup fails
324 Additionally set's is_authenitated if lookup fails
326 present in database
325 present in database
327
326
328 :param auth_user: instance of user to set attributes
327 :param auth_user: instance of user to set attributes
329 :param user_id: user id to fetch by
328 :param user_id: user id to fetch by
330 :param api_key: api key to fetch by
329 :param api_key: api key to fetch by
331 """
330 """
332 if user_id is None and api_key is None:
331 if user_id is None and api_key is None:
333 raise Exception('You need to pass user_id or api_key')
332 raise Exception('You need to pass user_id or api_key')
334
333
335 try:
334 try:
336 if api_key:
335 if api_key:
337 dbuser = self.get_by_api_key(api_key)
336 dbuser = self.get_by_api_key(api_key)
338 else:
337 else:
339 dbuser = self.get(user_id)
338 dbuser = self.get(user_id)
340
339
341 if dbuser is not None and dbuser.active:
340 if dbuser is not None and dbuser.active:
342 log.debug('filling %s data' % dbuser)
341 log.debug('filling %s data' % dbuser)
343 for k, v in dbuser.get_dict().items():
342 for k, v in dbuser.get_dict().items():
344 setattr(auth_user, k, v)
343 setattr(auth_user, k, v)
345 else:
344 else:
346 return False
345 return False
347
346
348 except:
347 except:
349 log.error(traceback.format_exc())
348 log.error(traceback.format_exc())
350 auth_user.is_authenticated = False
349 auth_user.is_authenticated = False
351 return False
350 return False
352
351
353 return True
352 return True
354
353
355 def fill_perms(self, user):
354 def fill_perms(self, user):
356 """
355 """
357 Fills user permission attribute with permissions taken from database
356 Fills user permission attribute with permissions taken from database
358 works for permissions given for repositories, and for permissions that
357 works for permissions given for repositories, and for permissions that
359 are granted to groups
358 are granted to groups
360
359
361 :param user: user instance to fill his perms
360 :param user: user instance to fill his perms
362 """
361 """
363 RK = 'repositories'
362 RK = 'repositories'
364 GK = 'repositories_groups'
363 GK = 'repositories_groups'
365 GLOBAL = 'global'
364 GLOBAL = 'global'
366 user.permissions[RK] = {}
365 user.permissions[RK] = {}
367 user.permissions[GK] = {}
366 user.permissions[GK] = {}
368 user.permissions[GLOBAL] = set()
367 user.permissions[GLOBAL] = set()
369
368
370 #======================================================================
369 #======================================================================
371 # fetch default permissions
370 # fetch default permissions
372 #======================================================================
371 #======================================================================
373 default_user = User.get_by_username('default', cache=True)
372 default_user = User.get_by_username('default', cache=True)
374 default_user_id = default_user.user_id
373 default_user_id = default_user.user_id
375
374
376 default_repo_perms = Permission.get_default_perms(default_user_id)
375 default_repo_perms = Permission.get_default_perms(default_user_id)
377 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
376 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
378
377
379 if user.is_admin:
378 if user.is_admin:
380 #==================================================================
379 #==================================================================
381 # admin user have all default rights for repositories
380 # admin user have all default rights for repositories
382 # and groups set to admin
381 # and groups set to admin
383 #==================================================================
382 #==================================================================
384 user.permissions[GLOBAL].add('hg.admin')
383 user.permissions[GLOBAL].add('hg.admin')
385
384
386 # repositories
385 # repositories
387 for perm in default_repo_perms:
386 for perm in default_repo_perms:
388 r_k = perm.UserRepoToPerm.repository.repo_name
387 r_k = perm.UserRepoToPerm.repository.repo_name
389 p = 'repository.admin'
388 p = 'repository.admin'
390 user.permissions[RK][r_k] = p
389 user.permissions[RK][r_k] = p
391
390
392 # repositories groups
391 # repositories groups
393 for perm in default_repo_groups_perms:
392 for perm in default_repo_groups_perms:
394 rg_k = perm.UserRepoGroupToPerm.group.group_name
393 rg_k = perm.UserRepoGroupToPerm.group.group_name
395 p = 'group.admin'
394 p = 'group.admin'
396 user.permissions[GK][rg_k] = p
395 user.permissions[GK][rg_k] = p
397 return user
396 return user
398
397
399 #==================================================================
398 #==================================================================
400 # set default permissions first for repositories and groups
399 # set default permissions first for repositories and groups
401 #==================================================================
400 #==================================================================
402 uid = user.user_id
401 uid = user.user_id
403
402
404 # default global permissions
403 # default global permissions
405 default_global_perms = self.sa.query(UserToPerm)\
404 default_global_perms = self.sa.query(UserToPerm)\
406 .filter(UserToPerm.user_id == default_user_id)
405 .filter(UserToPerm.user_id == default_user_id)
407
406
408 for perm in default_global_perms:
407 for perm in default_global_perms:
409 user.permissions[GLOBAL].add(perm.permission.permission_name)
408 user.permissions[GLOBAL].add(perm.permission.permission_name)
410
409
411 # defaults for repositories, taken from default user
410 # defaults for repositories, taken from default user
412 for perm in default_repo_perms:
411 for perm in default_repo_perms:
413 r_k = perm.UserRepoToPerm.repository.repo_name
412 r_k = perm.UserRepoToPerm.repository.repo_name
414 if perm.Repository.private and not (perm.Repository.user_id == uid):
413 if perm.Repository.private and not (perm.Repository.user_id == uid):
415 # disable defaults for private repos,
414 # disable defaults for private repos,
416 p = 'repository.none'
415 p = 'repository.none'
417 elif perm.Repository.user_id == uid:
416 elif perm.Repository.user_id == uid:
418 # set admin if owner
417 # set admin if owner
419 p = 'repository.admin'
418 p = 'repository.admin'
420 else:
419 else:
421 p = perm.Permission.permission_name
420 p = perm.Permission.permission_name
422
421
423 user.permissions[RK][r_k] = p
422 user.permissions[RK][r_k] = p
424
423
425 # defaults for repositories groups taken from default user permission
424 # defaults for repositories groups taken from default user permission
426 # on given group
425 # on given group
427 for perm in default_repo_groups_perms:
426 for perm in default_repo_groups_perms:
428 rg_k = perm.UserRepoGroupToPerm.group.group_name
427 rg_k = perm.UserRepoGroupToPerm.group.group_name
429 p = perm.Permission.permission_name
428 p = perm.Permission.permission_name
430 user.permissions[GK][rg_k] = p
429 user.permissions[GK][rg_k] = p
431
430
432 #==================================================================
431 #==================================================================
433 # overwrite defaults with user permissions if any found
432 # overwrite defaults with user permissions if any found
434 #==================================================================
433 #==================================================================
435
434
436 # user global permissions
435 # user global permissions
437 user_perms = self.sa.query(UserToPerm)\
436 user_perms = self.sa.query(UserToPerm)\
438 .options(joinedload(UserToPerm.permission))\
437 .options(joinedload(UserToPerm.permission))\
439 .filter(UserToPerm.user_id == uid).all()
438 .filter(UserToPerm.user_id == uid).all()
440
439
441 for perm in user_perms:
440 for perm in user_perms:
442 user.permissions[GLOBAL].add(perm.permission.permission_name)
441 user.permissions[GLOBAL].add(perm.permission.permission_name)
443
442
444 # user explicit permissions for repositories
443 # user explicit permissions for repositories
445 user_repo_perms = \
444 user_repo_perms = \
446 self.sa.query(UserRepoToPerm, Permission, Repository)\
445 self.sa.query(UserRepoToPerm, Permission, Repository)\
447 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
446 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
448 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
447 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
449 .filter(UserRepoToPerm.user_id == uid)\
448 .filter(UserRepoToPerm.user_id == uid)\
450 .all()
449 .all()
451
450
452 for perm in user_repo_perms:
451 for perm in user_repo_perms:
453 # set admin if owner
452 # set admin if owner
454 r_k = perm.UserRepoToPerm.repository.repo_name
453 r_k = perm.UserRepoToPerm.repository.repo_name
455 if perm.Repository.user_id == uid:
454 if perm.Repository.user_id == uid:
456 p = 'repository.admin'
455 p = 'repository.admin'
457 else:
456 else:
458 p = perm.Permission.permission_name
457 p = perm.Permission.permission_name
459 user.permissions[RK][r_k] = p
458 user.permissions[RK][r_k] = p
460
459
461 # USER GROUP
460 # USER GROUP
462 #==================================================================
461 #==================================================================
463 # check if user is part of user groups for this repository and
462 # check if user is part of user groups for this repository and
464 # fill in (or replace with higher) permissions
463 # fill in (or replace with higher) permissions
465 #==================================================================
464 #==================================================================
466
465
467 # users group global
466 # users group global
468 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
467 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
469 .options(joinedload(UsersGroupToPerm.permission))\
468 .options(joinedload(UsersGroupToPerm.permission))\
470 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
469 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
471 UsersGroupMember.users_group_id))\
470 UsersGroupMember.users_group_id))\
472 .filter(UsersGroupMember.user_id == uid).all()
471 .filter(UsersGroupMember.user_id == uid).all()
473
472
474 for perm in user_perms_from_users_groups:
473 for perm in user_perms_from_users_groups:
475 user.permissions[GLOBAL].add(perm.permission.permission_name)
474 user.permissions[GLOBAL].add(perm.permission.permission_name)
476
475
477 # users group for repositories permissions
476 # users group for repositories permissions
478 user_repo_perms_from_users_groups = \
477 user_repo_perms_from_users_groups = \
479 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
478 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
480 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
479 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
481 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
480 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
482 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
481 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
483 .filter(UsersGroupMember.user_id == uid)\
482 .filter(UsersGroupMember.user_id == uid)\
484 .all()
483 .all()
485
484
486 for perm in user_repo_perms_from_users_groups:
485 for perm in user_repo_perms_from_users_groups:
487 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
486 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
488 p = perm.Permission.permission_name
487 p = perm.Permission.permission_name
489 cur_perm = user.permissions[RK][r_k]
488 cur_perm = user.permissions[RK][r_k]
490 # overwrite permission only if it's greater than permission
489 # overwrite permission only if it's greater than permission
491 # given from other sources
490 # given from other sources
492 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
491 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
493 user.permissions[RK][r_k] = p
492 user.permissions[RK][r_k] = p
494
493
495 # REPO GROUP
494 # REPO GROUP
496 #==================================================================
495 #==================================================================
497 # get access for this user for repos group and override defaults
496 # get access for this user for repos group and override defaults
498 #==================================================================
497 #==================================================================
499
498
500 # user explicit permissions for repository
499 # user explicit permissions for repository
501 user_repo_groups_perms = \
500 user_repo_groups_perms = \
502 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
501 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
503 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
502 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
504 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
503 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
505 .filter(UserRepoGroupToPerm.user_id == uid)\
504 .filter(UserRepoGroupToPerm.user_id == uid)\
506 .all()
505 .all()
507
506
508 for perm in user_repo_groups_perms:
507 for perm in user_repo_groups_perms:
509 rg_k = perm.UserRepoGroupToPerm.group.group_name
508 rg_k = perm.UserRepoGroupToPerm.group.group_name
510 p = perm.Permission.permission_name
509 p = perm.Permission.permission_name
511 cur_perm = user.permissions[GK][rg_k]
510 cur_perm = user.permissions[GK][rg_k]
512 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
511 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
513 user.permissions[GK][rg_k] = p
512 user.permissions[GK][rg_k] = p
514
513
515 # REPO GROUP + USER GROUP
514 # REPO GROUP + USER GROUP
516 #==================================================================
515 #==================================================================
517 # check if user is part of user groups for this repo group and
516 # check if user is part of user groups for this repo group and
518 # fill in (or replace with higher) permissions
517 # fill in (or replace with higher) permissions
519 #==================================================================
518 #==================================================================
520
519
521 # users group for repositories permissions
520 # users group for repositories permissions
522 user_repo_group_perms_from_users_groups = \
521 user_repo_group_perms_from_users_groups = \
523 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
522 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
524 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
523 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
525 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
524 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
526 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
525 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
527 .filter(UsersGroupMember.user_id == uid)\
526 .filter(UsersGroupMember.user_id == uid)\
528 .all()
527 .all()
529
528
530 for perm in user_repo_group_perms_from_users_groups:
529 for perm in user_repo_group_perms_from_users_groups:
531 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
530 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
532 p = perm.Permission.permission_name
531 p = perm.Permission.permission_name
533 cur_perm = user.permissions[GK][g_k]
532 cur_perm = user.permissions[GK][g_k]
534 # overwrite permission only if it's greater than permission
533 # overwrite permission only if it's greater than permission
535 # given from other sources
534 # given from other sources
536 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
535 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
537 user.permissions[GK][g_k] = p
536 user.permissions[GK][g_k] = p
538
537
539 return user
538 return user
540
539
541 def has_perm(self, user, perm):
540 def has_perm(self, user, perm):
542 if not isinstance(perm, Permission):
541 if not isinstance(perm, Permission):
543 raise Exception('perm needs to be an instance of Permission class '
542 raise Exception('perm needs to be an instance of Permission class '
544 'got %s instead' % type(perm))
543 'got %s instead' % type(perm))
545
544
546 user = self._get_user(user)
545 user = self._get_user(user)
547
546
548 return UserToPerm.query().filter(UserToPerm.user == user)\
547 return UserToPerm.query().filter(UserToPerm.user == user)\
549 .filter(UserToPerm.permission == perm).scalar() is not None
548 .filter(UserToPerm.permission == perm).scalar() is not None
550
549
551 def grant_perm(self, user, perm):
550 def grant_perm(self, user, perm):
552 """
551 """
553 Grant user global permissions
552 Grant user global permissions
554
553
555 :param user:
554 :param user:
556 :param perm:
555 :param perm:
557 """
556 """
558 user = self._get_user(user)
557 user = self._get_user(user)
559 perm = self._get_perm(perm)
558 perm = self._get_perm(perm)
560 # if this permission is already granted skip it
559 # if this permission is already granted skip it
561 _perm = UserToPerm.query()\
560 _perm = UserToPerm.query()\
562 .filter(UserToPerm.user == user)\
561 .filter(UserToPerm.user == user)\
563 .filter(UserToPerm.permission == perm)\
562 .filter(UserToPerm.permission == perm)\
564 .scalar()
563 .scalar()
565 if _perm:
564 if _perm:
566 return
565 return
567 new = UserToPerm()
566 new = UserToPerm()
568 new.user = user
567 new.user = user
569 new.permission = perm
568 new.permission = perm
570 self.sa.add(new)
569 self.sa.add(new)
571
570
572 def revoke_perm(self, user, perm):
571 def revoke_perm(self, user, perm):
573 """
572 """
574 Revoke users global permissions
573 Revoke users global permissions
575
574
576 :param user:
575 :param user:
577 :param perm:
576 :param perm:
578 """
577 """
579 user = self._get_user(user)
578 user = self._get_user(user)
580 perm = self._get_perm(perm)
579 perm = self._get_perm(perm)
581
580
582 obj = UserToPerm.query()\
581 obj = UserToPerm.query()\
583 .filter(UserToPerm.user == user)\
582 .filter(UserToPerm.user == user)\
584 .filter(UserToPerm.permission == perm)\
583 .filter(UserToPerm.permission == perm)\
585 .scalar()
584 .scalar()
586 if obj:
585 if obj:
587 self.sa.delete(obj)
586 self.sa.delete(obj)
588
587
589 def add_extra_email(self, user, email):
588 def add_extra_email(self, user, email):
590 """
589 """
591 Adds email address to UserEmailMap
590 Adds email address to UserEmailMap
592
591
593 :param user:
592 :param user:
594 :param email:
593 :param email:
595 """
594 """
595 from rhodecode.model import forms
596 form = forms.UserExtraEmailForm()()
597 data = form.to_python(dict(email=email))
596 user = self._get_user(user)
598 user = self._get_user(user)
599
597 obj = UserEmailMap()
600 obj = UserEmailMap()
598 obj.user = user
601 obj.user = user
599 obj.email = email
602 obj.email = data['email']
600 self.sa.add(obj)
603 self.sa.add(obj)
601 return obj
604 return obj
602
605
603 def delete_extra_email(self, user, email_id):
606 def delete_extra_email(self, user, email_id):
604 """
607 """
605 Removes email address from UserEmailMap
608 Removes email address from UserEmailMap
606
609
607 :param user:
610 :param user:
608 :param email_id:
611 :param email_id:
609 """
612 """
610 user = self._get_user(user)
613 user = self._get_user(user)
611 obj = UserEmailMap.query().get(email_id)
614 obj = UserEmailMap.query().get(email_id)
612 if obj:
615 if obj:
613 self.sa.delete(obj)
616 self.sa.delete(obj)
@@ -1,592 +1,593
1 """
1 """
2 Set of generic validators
2 Set of generic validators
3 """
3 """
4 import os
4 import os
5 import re
5 import re
6 import formencode
6 import formencode
7 import logging
7 import logging
8 from pylons.i18n.translation import _
8 from pylons.i18n.translation import _
9 from webhelpers.pylonslib.secure_form import authentication_token
9 from webhelpers.pylonslib.secure_form import authentication_token
10
10
11 from formencode.validators import (
11 from formencode.validators import (
12 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
12 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
13 )
13 )
14
14
15 from rhodecode.lib.utils import repo_name_slug
15 from rhodecode.lib.utils import repo_name_slug
16 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User
16 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User
17 from rhodecode.lib.auth import authenticate
18 from rhodecode.lib.exceptions import LdapImportError
17 from rhodecode.lib.exceptions import LdapImportError
19 from rhodecode.config.routing import ADMIN_PREFIX
18 from rhodecode.config.routing import ADMIN_PREFIX
20 # silence warnings and pylint
19 # silence warnings and pylint
21 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
20 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set
22
21
23 log = logging.getLogger(__name__)
22 log = logging.getLogger(__name__)
24
23
25
24
26 class StateObj(object):
25 class StateObj(object):
27 """
26 """
28 this is needed to translate the messages using _() in validators
27 this is needed to translate the messages using _() in validators
29 """
28 """
30 _ = staticmethod(_)
29 _ = staticmethod(_)
31
30
32
31
33 def M(self, key, state=None, **kwargs):
32 def M(self, key, state=None, **kwargs):
34 """
33 """
35 returns string from self.message based on given key,
34 returns string from self.message based on given key,
36 passed kw params are used to substitute %(named)s params inside
35 passed kw params are used to substitute %(named)s params inside
37 translated strings
36 translated strings
38
37
39 :param msg:
38 :param msg:
40 :param state:
39 :param state:
41 """
40 """
42 if state is None:
41 if state is None:
43 state = StateObj()
42 state = StateObj()
44 else:
43 else:
45 state._ = staticmethod(_)
44 state._ = staticmethod(_)
46 #inject validator into state object
45 #inject validator into state object
47 return self.message(key, state, **kwargs)
46 return self.message(key, state, **kwargs)
48
47
49
48
50 def ValidUsername(edit=False, old_data={}):
49 def ValidUsername(edit=False, old_data={}):
51 class _validator(formencode.validators.FancyValidator):
50 class _validator(formencode.validators.FancyValidator):
52 messages = {
51 messages = {
53 'username_exists': _(u'Username "%(username)s" already exists'),
52 'username_exists': _(u'Username "%(username)s" already exists'),
54 'system_invalid_username':
53 'system_invalid_username':
55 _(u'Username "%(username)s" is forbidden'),
54 _(u'Username "%(username)s" is forbidden'),
56 'invalid_username':
55 'invalid_username':
57 _(u'Username may only contain alphanumeric characters '
56 _(u'Username may only contain alphanumeric characters '
58 'underscores, periods or dashes and must begin with '
57 'underscores, periods or dashes and must begin with '
59 'alphanumeric character')
58 'alphanumeric character')
60 }
59 }
61
60
62 def validate_python(self, value, state):
61 def validate_python(self, value, state):
63 if value in ['default', 'new_user']:
62 if value in ['default', 'new_user']:
64 msg = M(self, 'system_invalid_username', state, username=value)
63 msg = M(self, 'system_invalid_username', state, username=value)
65 raise formencode.Invalid(msg, value, state)
64 raise formencode.Invalid(msg, value, state)
66 #check if user is unique
65 #check if user is unique
67 old_un = None
66 old_un = None
68 if edit:
67 if edit:
69 old_un = User.get(old_data.get('user_id')).username
68 old_un = User.get(old_data.get('user_id')).username
70
69
71 if old_un != value or not edit:
70 if old_un != value or not edit:
72 if User.get_by_username(value, case_insensitive=True):
71 if User.get_by_username(value, case_insensitive=True):
73 msg = M(self, 'username_exists', state, username=value)
72 msg = M(self, 'username_exists', state, username=value)
74 raise formencode.Invalid(msg, value, state)
73 raise formencode.Invalid(msg, value, state)
75
74
76 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
75 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
77 msg = M(self, 'invalid_username', state)
76 msg = M(self, 'invalid_username', state)
78 raise formencode.Invalid(msg, value, state)
77 raise formencode.Invalid(msg, value, state)
79 return _validator
78 return _validator
80
79
81
80
82 def ValidRepoUser():
81 def ValidRepoUser():
83 class _validator(formencode.validators.FancyValidator):
82 class _validator(formencode.validators.FancyValidator):
84 messages = {
83 messages = {
85 'invalid_username': _(u'Username %(username)s is not valid')
84 'invalid_username': _(u'Username %(username)s is not valid')
86 }
85 }
87
86
88 def validate_python(self, value, state):
87 def validate_python(self, value, state):
89 try:
88 try:
90 User.query().filter(User.active == True)\
89 User.query().filter(User.active == True)\
91 .filter(User.username == value).one()
90 .filter(User.username == value).one()
92 except Exception:
91 except Exception:
93 msg = M(self, 'invalid_username', state, username=value)
92 msg = M(self, 'invalid_username', state, username=value)
94 raise formencode.Invalid(msg, value, state,
93 raise formencode.Invalid(msg, value, state,
95 error_dict=dict(username=msg)
94 error_dict=dict(username=msg)
96 )
95 )
97
96
98 return _validator
97 return _validator
99
98
100
99
101 def ValidUsersGroup(edit=False, old_data={}):
100 def ValidUsersGroup(edit=False, old_data={}):
102 class _validator(formencode.validators.FancyValidator):
101 class _validator(formencode.validators.FancyValidator):
103 messages = {
102 messages = {
104 'invalid_group': _(u'Invalid users group name'),
103 'invalid_group': _(u'Invalid users group name'),
105 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
104 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
106 'invalid_usersgroup_name':
105 'invalid_usersgroup_name':
107 _(u'users group name may only contain alphanumeric '
106 _(u'users group name may only contain alphanumeric '
108 'characters underscores, periods or dashes and must begin '
107 'characters underscores, periods or dashes and must begin '
109 'with alphanumeric character')
108 'with alphanumeric character')
110 }
109 }
111
110
112 def validate_python(self, value, state):
111 def validate_python(self, value, state):
113 if value in ['default']:
112 if value in ['default']:
114 msg = M(self, 'invalid_group', state)
113 msg = M(self, 'invalid_group', state)
115 raise formencode.Invalid(msg, value, state,
114 raise formencode.Invalid(msg, value, state,
116 error_dict=dict(users_group_name=msg)
115 error_dict=dict(users_group_name=msg)
117 )
116 )
118 #check if group is unique
117 #check if group is unique
119 old_ugname = None
118 old_ugname = None
120 if edit:
119 if edit:
121 old_id = old_data.get('users_group_id')
120 old_id = old_data.get('users_group_id')
122 old_ugname = UsersGroup.get(old_id).users_group_name
121 old_ugname = UsersGroup.get(old_id).users_group_name
123
122
124 if old_ugname != value or not edit:
123 if old_ugname != value or not edit:
125 is_existing_group = UsersGroup.get_by_group_name(value,
124 is_existing_group = UsersGroup.get_by_group_name(value,
126 case_insensitive=True)
125 case_insensitive=True)
127 if is_existing_group:
126 if is_existing_group:
128 msg = M(self, 'group_exist', state, usersgroup=value)
127 msg = M(self, 'group_exist', state, usersgroup=value)
129 raise formencode.Invalid(msg, value, state,
128 raise formencode.Invalid(msg, value, state,
130 error_dict=dict(users_group_name=msg)
129 error_dict=dict(users_group_name=msg)
131 )
130 )
132
131
133 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
132 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
134 msg = M(self, 'invalid_usersgroup_name', state)
133 msg = M(self, 'invalid_usersgroup_name', state)
135 raise formencode.Invalid(msg, value, state,
134 raise formencode.Invalid(msg, value, state,
136 error_dict=dict(users_group_name=msg)
135 error_dict=dict(users_group_name=msg)
137 )
136 )
138
137
139 return _validator
138 return _validator
140
139
141
140
142 def ValidReposGroup(edit=False, old_data={}):
141 def ValidReposGroup(edit=False, old_data={}):
143 class _validator(formencode.validators.FancyValidator):
142 class _validator(formencode.validators.FancyValidator):
144 messages = {
143 messages = {
145 'group_parent_id': _(u'Cannot assign this group as parent'),
144 'group_parent_id': _(u'Cannot assign this group as parent'),
146 'group_exists': _(u'Group "%(group_name)s" already exists'),
145 'group_exists': _(u'Group "%(group_name)s" already exists'),
147 'repo_exists':
146 'repo_exists':
148 _(u'Repository with name "%(group_name)s" already exists')
147 _(u'Repository with name "%(group_name)s" already exists')
149 }
148 }
150
149
151 def validate_python(self, value, state):
150 def validate_python(self, value, state):
152 # TODO WRITE VALIDATIONS
151 # TODO WRITE VALIDATIONS
153 group_name = value.get('group_name')
152 group_name = value.get('group_name')
154 group_parent_id = value.get('group_parent_id')
153 group_parent_id = value.get('group_parent_id')
155
154
156 # slugify repo group just in case :)
155 # slugify repo group just in case :)
157 slug = repo_name_slug(group_name)
156 slug = repo_name_slug(group_name)
158
157
159 # check for parent of self
158 # check for parent of self
160 parent_of_self = lambda: (
159 parent_of_self = lambda: (
161 old_data['group_id'] == int(group_parent_id)
160 old_data['group_id'] == int(group_parent_id)
162 if group_parent_id else False
161 if group_parent_id else False
163 )
162 )
164 if edit and parent_of_self():
163 if edit and parent_of_self():
165 msg = M(self, 'group_parent_id', state)
164 msg = M(self, 'group_parent_id', state)
166 raise formencode.Invalid(msg, value, state,
165 raise formencode.Invalid(msg, value, state,
167 error_dict=dict(group_parent_id=msg)
166 error_dict=dict(group_parent_id=msg)
168 )
167 )
169
168
170 old_gname = None
169 old_gname = None
171 if edit:
170 if edit:
172 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
171 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
173
172
174 if old_gname != group_name or not edit:
173 if old_gname != group_name or not edit:
175
174
176 # check group
175 # check group
177 gr = RepoGroup.query()\
176 gr = RepoGroup.query()\
178 .filter(RepoGroup.group_name == slug)\
177 .filter(RepoGroup.group_name == slug)\
179 .filter(RepoGroup.group_parent_id == group_parent_id)\
178 .filter(RepoGroup.group_parent_id == group_parent_id)\
180 .scalar()
179 .scalar()
181
180
182 if gr:
181 if gr:
183 msg = M(self, 'group_exists', state, group_name=slug)
182 msg = M(self, 'group_exists', state, group_name=slug)
184 raise formencode.Invalid(msg, value, state,
183 raise formencode.Invalid(msg, value, state,
185 error_dict=dict(group_name=msg)
184 error_dict=dict(group_name=msg)
186 )
185 )
187
186
188 # check for same repo
187 # check for same repo
189 repo = Repository.query()\
188 repo = Repository.query()\
190 .filter(Repository.repo_name == slug)\
189 .filter(Repository.repo_name == slug)\
191 .scalar()
190 .scalar()
192
191
193 if repo:
192 if repo:
194 msg = M(self, 'repo_exists', state, group_name=slug)
193 msg = M(self, 'repo_exists', state, group_name=slug)
195 raise formencode.Invalid(msg, value, state,
194 raise formencode.Invalid(msg, value, state,
196 error_dict=dict(group_name=msg)
195 error_dict=dict(group_name=msg)
197 )
196 )
198
197
199 return _validator
198 return _validator
200
199
201
200
202 def ValidPassword():
201 def ValidPassword():
203 class _validator(formencode.validators.FancyValidator):
202 class _validator(formencode.validators.FancyValidator):
204 messages = {
203 messages = {
205 'invalid_password':
204 'invalid_password':
206 _(u'Invalid characters (non-ascii) in password')
205 _(u'Invalid characters (non-ascii) in password')
207 }
206 }
208
207
209 def validate_python(self, value, state):
208 def validate_python(self, value, state):
210 try:
209 try:
211 (value or '').decode('ascii')
210 (value or '').decode('ascii')
212 except UnicodeError:
211 except UnicodeError:
213 msg = M(self, 'invalid_password', state)
212 msg = M(self, 'invalid_password', state)
214 raise formencode.Invalid(msg, value, state,)
213 raise formencode.Invalid(msg, value, state,)
215 return _validator
214 return _validator
216
215
217
216
218 def ValidPasswordsMatch():
217 def ValidPasswordsMatch():
219 class _validator(formencode.validators.FancyValidator):
218 class _validator(formencode.validators.FancyValidator):
220 messages = {
219 messages = {
221 'password_mismatch': _(u'Passwords do not match'),
220 'password_mismatch': _(u'Passwords do not match'),
222 }
221 }
223
222
224 def validate_python(self, value, state):
223 def validate_python(self, value, state):
225
224
226 pass_val = value.get('password') or value.get('new_password')
225 pass_val = value.get('password') or value.get('new_password')
227 if pass_val != value['password_confirmation']:
226 if pass_val != value['password_confirmation']:
228 msg = M(self, 'password_mismatch', state)
227 msg = M(self, 'password_mismatch', state)
229 raise formencode.Invalid(msg, value, state,
228 raise formencode.Invalid(msg, value, state,
230 error_dict=dict(password_confirmation=msg)
229 error_dict=dict(password_confirmation=msg)
231 )
230 )
232 return _validator
231 return _validator
233
232
234
233
235 def ValidAuth():
234 def ValidAuth():
236 class _validator(formencode.validators.FancyValidator):
235 class _validator(formencode.validators.FancyValidator):
237 messages = {
236 messages = {
238 'invalid_password': _(u'invalid password'),
237 'invalid_password': _(u'invalid password'),
239 'invalid_username': _(u'invalid user name'),
238 'invalid_username': _(u'invalid user name'),
240 'disabled_account': _(u'Your account is disabled')
239 'disabled_account': _(u'Your account is disabled')
241 }
240 }
242
241
243 def validate_python(self, value, state):
242 def validate_python(self, value, state):
243 from rhodecode.lib.auth import authenticate
244
244 password = value['password']
245 password = value['password']
245 username = value['username']
246 username = value['username']
246
247
247 if not authenticate(username, password):
248 if not authenticate(username, password):
248 user = User.get_by_username(username)
249 user = User.get_by_username(username)
249 if user and user.active is False:
250 if user and user.active is False:
250 log.warning('user %s is disabled' % username)
251 log.warning('user %s is disabled' % username)
251 msg = M(self, 'disabled_account', state)
252 msg = M(self, 'disabled_account', state)
252 raise formencode.Invalid(msg, value, state,
253 raise formencode.Invalid(msg, value, state,
253 error_dict=dict(username=msg)
254 error_dict=dict(username=msg)
254 )
255 )
255 else:
256 else:
256 log.warning('user %s failed to authenticate' % username)
257 log.warning('user %s failed to authenticate' % username)
257 msg = M(self, 'invalid_username', state)
258 msg = M(self, 'invalid_username', state)
258 msg2 = M(self, 'invalid_password', state)
259 msg2 = M(self, 'invalid_password', state)
259 raise formencode.Invalid(msg, value, state,
260 raise formencode.Invalid(msg, value, state,
260 error_dict=dict(username=msg, password=msg2)
261 error_dict=dict(username=msg, password=msg2)
261 )
262 )
262 return _validator
263 return _validator
263
264
264
265
265 def ValidAuthToken():
266 def ValidAuthToken():
266 class _validator(formencode.validators.FancyValidator):
267 class _validator(formencode.validators.FancyValidator):
267 messages = {
268 messages = {
268 'invalid_token': _(u'Token mismatch')
269 'invalid_token': _(u'Token mismatch')
269 }
270 }
270
271
271 def validate_python(self, value, state):
272 def validate_python(self, value, state):
272 if value != authentication_token():
273 if value != authentication_token():
273 msg = M(self, 'invalid_token', state)
274 msg = M(self, 'invalid_token', state)
274 raise formencode.Invalid(msg, value, state)
275 raise formencode.Invalid(msg, value, state)
275 return _validator
276 return _validator
276
277
277
278
278 def ValidRepoName(edit=False, old_data={}):
279 def ValidRepoName(edit=False, old_data={}):
279 class _validator(formencode.validators.FancyValidator):
280 class _validator(formencode.validators.FancyValidator):
280 messages = {
281 messages = {
281 'invalid_repo_name':
282 'invalid_repo_name':
282 _(u'Repository name %(repo)s is disallowed'),
283 _(u'Repository name %(repo)s is disallowed'),
283 'repository_exists':
284 'repository_exists':
284 _(u'Repository named %(repo)s already exists'),
285 _(u'Repository named %(repo)s already exists'),
285 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
286 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
286 'exists in group "%(group)s"'),
287 'exists in group "%(group)s"'),
287 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
288 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
288 'already exists')
289 'already exists')
289 }
290 }
290
291
291 def _to_python(self, value, state):
292 def _to_python(self, value, state):
292 repo_name = repo_name_slug(value.get('repo_name', ''))
293 repo_name = repo_name_slug(value.get('repo_name', ''))
293 repo_group = value.get('repo_group')
294 repo_group = value.get('repo_group')
294 if repo_group:
295 if repo_group:
295 gr = RepoGroup.get(repo_group)
296 gr = RepoGroup.get(repo_group)
296 group_path = gr.full_path
297 group_path = gr.full_path
297 group_name = gr.group_name
298 group_name = gr.group_name
298 # value needs to be aware of group name in order to check
299 # value needs to be aware of group name in order to check
299 # db key This is an actual just the name to store in the
300 # db key This is an actual just the name to store in the
300 # database
301 # database
301 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
302 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
302 else:
303 else:
303 group_name = group_path = ''
304 group_name = group_path = ''
304 repo_name_full = repo_name
305 repo_name_full = repo_name
305
306
306 value['repo_name'] = repo_name
307 value['repo_name'] = repo_name
307 value['repo_name_full'] = repo_name_full
308 value['repo_name_full'] = repo_name_full
308 value['group_path'] = group_path
309 value['group_path'] = group_path
309 value['group_name'] = group_name
310 value['group_name'] = group_name
310 return value
311 return value
311
312
312 def validate_python(self, value, state):
313 def validate_python(self, value, state):
313
314
314 repo_name = value.get('repo_name')
315 repo_name = value.get('repo_name')
315 repo_name_full = value.get('repo_name_full')
316 repo_name_full = value.get('repo_name_full')
316 group_path = value.get('group_path')
317 group_path = value.get('group_path')
317 group_name = value.get('group_name')
318 group_name = value.get('group_name')
318
319
319 if repo_name in [ADMIN_PREFIX, '']:
320 if repo_name in [ADMIN_PREFIX, '']:
320 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
321 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
321 raise formencode.Invalid(msg, value, state,
322 raise formencode.Invalid(msg, value, state,
322 error_dict=dict(repo_name=msg)
323 error_dict=dict(repo_name=msg)
323 )
324 )
324
325
325 rename = old_data.get('repo_name') != repo_name_full
326 rename = old_data.get('repo_name') != repo_name_full
326 create = not edit
327 create = not edit
327 if rename or create:
328 if rename or create:
328
329
329 if group_path != '':
330 if group_path != '':
330 if Repository.get_by_repo_name(repo_name_full):
331 if Repository.get_by_repo_name(repo_name_full):
331 msg = M(self, 'repository_in_group_exists', state,
332 msg = M(self, 'repository_in_group_exists', state,
332 repo=repo_name, group=group_name)
333 repo=repo_name, group=group_name)
333 raise formencode.Invalid(msg, value, state,
334 raise formencode.Invalid(msg, value, state,
334 error_dict=dict(repo_name=msg)
335 error_dict=dict(repo_name=msg)
335 )
336 )
336 elif RepoGroup.get_by_group_name(repo_name_full):
337 elif RepoGroup.get_by_group_name(repo_name_full):
337 msg = M(self, 'same_group_exists', state,
338 msg = M(self, 'same_group_exists', state,
338 repo=repo_name)
339 repo=repo_name)
339 raise formencode.Invalid(msg, value, state,
340 raise formencode.Invalid(msg, value, state,
340 error_dict=dict(repo_name=msg)
341 error_dict=dict(repo_name=msg)
341 )
342 )
342
343
343 elif Repository.get_by_repo_name(repo_name_full):
344 elif Repository.get_by_repo_name(repo_name_full):
344 msg = M(self, 'repository_exists', state,
345 msg = M(self, 'repository_exists', state,
345 repo=repo_name)
346 repo=repo_name)
346 raise formencode.Invalid(msg, value, state,
347 raise formencode.Invalid(msg, value, state,
347 error_dict=dict(repo_name=msg)
348 error_dict=dict(repo_name=msg)
348 )
349 )
349 return value
350 return value
350 return _validator
351 return _validator
351
352
352
353
353 def ValidForkName(*args, **kwargs):
354 def ValidForkName(*args, **kwargs):
354 return ValidRepoName(*args, **kwargs)
355 return ValidRepoName(*args, **kwargs)
355
356
356
357
357 def SlugifyName():
358 def SlugifyName():
358 class _validator(formencode.validators.FancyValidator):
359 class _validator(formencode.validators.FancyValidator):
359
360
360 def _to_python(self, value, state):
361 def _to_python(self, value, state):
361 return repo_name_slug(value)
362 return repo_name_slug(value)
362
363
363 def validate_python(self, value, state):
364 def validate_python(self, value, state):
364 pass
365 pass
365
366
366 return _validator
367 return _validator
367
368
368
369
369 def ValidCloneUri():
370 def ValidCloneUri():
370 from rhodecode.lib.utils import make_ui
371 from rhodecode.lib.utils import make_ui
371
372
372 def url_handler(repo_type, url, proto, ui=None):
373 def url_handler(repo_type, url, proto, ui=None):
373 if repo_type == 'hg':
374 if repo_type == 'hg':
374 from mercurial.httprepo import httprepository, httpsrepository
375 from mercurial.httprepo import httprepository, httpsrepository
375 if proto == 'https':
376 if proto == 'https':
376 httpsrepository(make_ui('db'), url).capabilities
377 httpsrepository(make_ui('db'), url).capabilities
377 elif proto == 'http':
378 elif proto == 'http':
378 httprepository(make_ui('db'), url).capabilities
379 httprepository(make_ui('db'), url).capabilities
379 elif repo_type == 'git':
380 elif repo_type == 'git':
380 #TODO: write a git url validator
381 #TODO: write a git url validator
381 pass
382 pass
382
383
383 class _validator(formencode.validators.FancyValidator):
384 class _validator(formencode.validators.FancyValidator):
384 messages = {
385 messages = {
385 'clone_uri': _(u'invalid clone url'),
386 'clone_uri': _(u'invalid clone url'),
386 'invalid_clone_uri': _(u'Invalid clone url, provide a '
387 'invalid_clone_uri': _(u'Invalid clone url, provide a '
387 'valid clone http\s url')
388 'valid clone http\s url')
388 }
389 }
389
390
390 def validate_python(self, value, state):
391 def validate_python(self, value, state):
391 repo_type = value.get('repo_type')
392 repo_type = value.get('repo_type')
392 url = value.get('clone_uri')
393 url = value.get('clone_uri')
393
394
394 if not url:
395 if not url:
395 pass
396 pass
396 elif url.startswith('https') or url.startswith('http'):
397 elif url.startswith('https') or url.startswith('http'):
397 _type = 'https' if url.startswith('https') else 'http'
398 _type = 'https' if url.startswith('https') else 'http'
398 try:
399 try:
399 url_handler(repo_type, url, _type, make_ui('db'))
400 url_handler(repo_type, url, _type, make_ui('db'))
400 except Exception:
401 except Exception:
401 log.exception('Url validation failed')
402 log.exception('Url validation failed')
402 msg = M(self, 'clone_uri')
403 msg = M(self, 'clone_uri')
403 raise formencode.Invalid(msg, value, state,
404 raise formencode.Invalid(msg, value, state,
404 error_dict=dict(clone_uri=msg)
405 error_dict=dict(clone_uri=msg)
405 )
406 )
406 else:
407 else:
407 msg = M(self, 'invalid_clone_uri', state)
408 msg = M(self, 'invalid_clone_uri', state)
408 raise formencode.Invalid(msg, value, state,
409 raise formencode.Invalid(msg, value, state,
409 error_dict=dict(clone_uri=msg)
410 error_dict=dict(clone_uri=msg)
410 )
411 )
411 return _validator
412 return _validator
412
413
413
414
414 def ValidForkType(old_data={}):
415 def ValidForkType(old_data={}):
415 class _validator(formencode.validators.FancyValidator):
416 class _validator(formencode.validators.FancyValidator):
416 messages = {
417 messages = {
417 'invalid_fork_type': _(u'Fork have to be the same type as parent')
418 'invalid_fork_type': _(u'Fork have to be the same type as parent')
418 }
419 }
419
420
420 def validate_python(self, value, state):
421 def validate_python(self, value, state):
421 if old_data['repo_type'] != value:
422 if old_data['repo_type'] != value:
422 msg = M(self, 'invalid_fork_type', state)
423 msg = M(self, 'invalid_fork_type', state)
423 raise formencode.Invalid(msg, value, state,
424 raise formencode.Invalid(msg, value, state,
424 error_dict=dict(repo_type=msg)
425 error_dict=dict(repo_type=msg)
425 )
426 )
426 return _validator
427 return _validator
427
428
428
429
429 def ValidPerms(type_='repo'):
430 def ValidPerms(type_='repo'):
430 if type_ == 'group':
431 if type_ == 'group':
431 EMPTY_PERM = 'group.none'
432 EMPTY_PERM = 'group.none'
432 elif type_ == 'repo':
433 elif type_ == 'repo':
433 EMPTY_PERM = 'repository.none'
434 EMPTY_PERM = 'repository.none'
434
435
435 class _validator(formencode.validators.FancyValidator):
436 class _validator(formencode.validators.FancyValidator):
436 messages = {
437 messages = {
437 'perm_new_member_name':
438 'perm_new_member_name':
438 _(u'This username or users group name is not valid')
439 _(u'This username or users group name is not valid')
439 }
440 }
440
441
441 def to_python(self, value, state):
442 def to_python(self, value, state):
442 perms_update = []
443 perms_update = []
443 perms_new = []
444 perms_new = []
444 # build a list of permission to update and new permission to create
445 # build a list of permission to update and new permission to create
445 for k, v in value.items():
446 for k, v in value.items():
446 # means new added member to permissions
447 # means new added member to permissions
447 if k.startswith('perm_new_member'):
448 if k.startswith('perm_new_member'):
448 new_perm = value.get('perm_new_member', False)
449 new_perm = value.get('perm_new_member', False)
449 new_member = value.get('perm_new_member_name', False)
450 new_member = value.get('perm_new_member_name', False)
450 new_type = value.get('perm_new_member_type')
451 new_type = value.get('perm_new_member_type')
451
452
452 if new_member and new_perm:
453 if new_member and new_perm:
453 if (new_member, new_perm, new_type) not in perms_new:
454 if (new_member, new_perm, new_type) not in perms_new:
454 perms_new.append((new_member, new_perm, new_type))
455 perms_new.append((new_member, new_perm, new_type))
455 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
456 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
456 member = k[7:]
457 member = k[7:]
457 t = {'u': 'user',
458 t = {'u': 'user',
458 'g': 'users_group'
459 'g': 'users_group'
459 }[k[0]]
460 }[k[0]]
460 if member == 'default':
461 if member == 'default':
461 if value.get('private'):
462 if value.get('private'):
462 # set none for default when updating to
463 # set none for default when updating to
463 # private repo
464 # private repo
464 v = EMPTY_PERM
465 v = EMPTY_PERM
465 perms_update.append((member, v, t))
466 perms_update.append((member, v, t))
466
467
467 value['perms_updates'] = perms_update
468 value['perms_updates'] = perms_update
468 value['perms_new'] = perms_new
469 value['perms_new'] = perms_new
469
470
470 # update permissions
471 # update permissions
471 for k, v, t in perms_new:
472 for k, v, t in perms_new:
472 try:
473 try:
473 if t is 'user':
474 if t is 'user':
474 self.user_db = User.query()\
475 self.user_db = User.query()\
475 .filter(User.active == True)\
476 .filter(User.active == True)\
476 .filter(User.username == k).one()
477 .filter(User.username == k).one()
477 if t is 'users_group':
478 if t is 'users_group':
478 self.user_db = UsersGroup.query()\
479 self.user_db = UsersGroup.query()\
479 .filter(UsersGroup.users_group_active == True)\
480 .filter(UsersGroup.users_group_active == True)\
480 .filter(UsersGroup.users_group_name == k).one()
481 .filter(UsersGroup.users_group_name == k).one()
481
482
482 except Exception:
483 except Exception:
483 log.exception('Updated permission failed')
484 log.exception('Updated permission failed')
484 msg = M(self, 'perm_new_member_type', state)
485 msg = M(self, 'perm_new_member_type', state)
485 raise formencode.Invalid(msg, value, state,
486 raise formencode.Invalid(msg, value, state,
486 error_dict=dict(perm_new_member_name=msg)
487 error_dict=dict(perm_new_member_name=msg)
487 )
488 )
488 return value
489 return value
489 return _validator
490 return _validator
490
491
491
492
492 def ValidSettings():
493 def ValidSettings():
493 class _validator(formencode.validators.FancyValidator):
494 class _validator(formencode.validators.FancyValidator):
494 def _to_python(self, value, state):
495 def _to_python(self, value, state):
495 # settings form can't edit user
496 # settings form can't edit user
496 if 'user' in value:
497 if 'user' in value:
497 del value['user']
498 del value['user']
498 return value
499 return value
499
500
500 def validate_python(self, value, state):
501 def validate_python(self, value, state):
501 pass
502 pass
502 return _validator
503 return _validator
503
504
504
505
505 def ValidPath():
506 def ValidPath():
506 class _validator(formencode.validators.FancyValidator):
507 class _validator(formencode.validators.FancyValidator):
507 messages = {
508 messages = {
508 'invalid_path': _(u'This is not a valid path')
509 'invalid_path': _(u'This is not a valid path')
509 }
510 }
510
511
511 def validate_python(self, value, state):
512 def validate_python(self, value, state):
512 if not os.path.isdir(value):
513 if not os.path.isdir(value):
513 msg = M(self, 'invalid_path', state)
514 msg = M(self, 'invalid_path', state)
514 raise formencode.Invalid(msg, value, state,
515 raise formencode.Invalid(msg, value, state,
515 error_dict=dict(paths_root_path=msg)
516 error_dict=dict(paths_root_path=msg)
516 )
517 )
517 return _validator
518 return _validator
518
519
519
520
520 def UniqSystemEmail(old_data={}):
521 def UniqSystemEmail(old_data={}):
521 class _validator(formencode.validators.FancyValidator):
522 class _validator(formencode.validators.FancyValidator):
522 messages = {
523 messages = {
523 'email_taken': _(u'This e-mail address is already taken')
524 'email_taken': _(u'This e-mail address is already taken')
524 }
525 }
525
526
526 def _to_python(self, value, state):
527 def _to_python(self, value, state):
527 return value.lower()
528 return value.lower()
528
529
529 def validate_python(self, value, state):
530 def validate_python(self, value, state):
530 if (old_data.get('email') or '').lower() != value:
531 if (old_data.get('email') or '').lower() != value:
531 user = User.get_by_email(value, case_insensitive=True)
532 user = User.get_by_email(value, case_insensitive=True)
532 if user:
533 if user:
533 msg = M(self, 'email_taken', state)
534 msg = M(self, 'email_taken', state)
534 raise formencode.Invalid(msg, value, state,
535 raise formencode.Invalid(msg, value, state,
535 error_dict=dict(email=msg)
536 error_dict=dict(email=msg)
536 )
537 )
537 return _validator
538 return _validator
538
539
539
540
540 def ValidSystemEmail():
541 def ValidSystemEmail():
541 class _validator(formencode.validators.FancyValidator):
542 class _validator(formencode.validators.FancyValidator):
542 messages = {
543 messages = {
543 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
544 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
544 }
545 }
545
546
546 def _to_python(self, value, state):
547 def _to_python(self, value, state):
547 return value.lower()
548 return value.lower()
548
549
549 def validate_python(self, value, state):
550 def validate_python(self, value, state):
550 user = User.get_by_email(value, case_insensitive=True)
551 user = User.get_by_email(value, case_insensitive=True)
551 if user is None:
552 if user is None:
552 msg = M(self, 'non_existing_email', state, email=value)
553 msg = M(self, 'non_existing_email', state, email=value)
553 raise formencode.Invalid(msg, value, state,
554 raise formencode.Invalid(msg, value, state,
554 error_dict=dict(email=msg)
555 error_dict=dict(email=msg)
555 )
556 )
556
557
557 return _validator
558 return _validator
558
559
559
560
560 def LdapLibValidator():
561 def LdapLibValidator():
561 class _validator(formencode.validators.FancyValidator):
562 class _validator(formencode.validators.FancyValidator):
562 messages = {
563 messages = {
563
564
564 }
565 }
565
566
566 def validate_python(self, value, state):
567 def validate_python(self, value, state):
567 try:
568 try:
568 import ldap
569 import ldap
569 ldap # pyflakes silence !
570 ldap # pyflakes silence !
570 except ImportError:
571 except ImportError:
571 raise LdapImportError()
572 raise LdapImportError()
572
573
573 return _validator
574 return _validator
574
575
575
576
576 def AttrLoginValidator():
577 def AttrLoginValidator():
577 class _validator(formencode.validators.FancyValidator):
578 class _validator(formencode.validators.FancyValidator):
578 messages = {
579 messages = {
579 'invalid_cn':
580 'invalid_cn':
580 _(u'The LDAP Login attribute of the CN must be specified - '
581 _(u'The LDAP Login attribute of the CN must be specified - '
581 'this is the name of the attribute that is equivalent '
582 'this is the name of the attribute that is equivalent '
582 'to "username"')
583 'to "username"')
583 }
584 }
584
585
585 def validate_python(self, value, state):
586 def validate_python(self, value, state):
586 if not value or not isinstance(value, (str, unicode)):
587 if not value or not isinstance(value, (str, unicode)):
587 msg = M(self, 'invalid_cn', state)
588 msg = M(self, 'invalid_cn', state)
588 raise formencode.Invalid(msg, value, state,
589 raise formencode.Invalid(msg, value, state,
589 error_dict=dict(ldap_attr_login=msg)
590 error_dict=dict(ldap_attr_login=msg)
590 )
591 )
591
592
592 return _validator
593 return _validator
General Comments 0
You need to be logged in to leave comments. Login now