##// END OF EJS Templates
Added pre-create user hook....
marcink -
r4074:3b136af3 default
parent child Browse files
Show More
@@ -1,192 +1,215 b''
1 # Additional mappings that are not present in the pygments lexers
1 # Additional mappings that are not present in the pygments lexers
2 # used for building stats
2 # used for building stats
3 # format is {'ext':['Names']} eg. {'py':['Python']} note: there can be
3 # format is {'ext':['Names']} eg. {'py':['Python']} note: there can be
4 # more than one name for extension
4 # more than one name for extension
5 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
5 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
6 # build by pygments
6 # build by pygments
7 EXTRA_MAPPINGS = {}
7 EXTRA_MAPPINGS = {}
8
8
9 # additional lexer definitions for custom files
9 # additional lexer definitions for custom files
10 # it's overrides pygments lexers, and uses defined name of lexer to colorize the
10 # it's overrides pygments lexers, and uses defined name of lexer to colorize the
11 # files. Format is {'ext': 'lexer_name'}
11 # files. Format is {'ext': 'lexer_name'}
12 # List of lexers can be printed running:
12 # List of lexers can be printed running:
13 # python -c "import pprint;from pygments import lexers;pprint.pprint([(x[0], x[1]) for x in lexers.get_all_lexers()]);"
13 # python -c "import pprint;from pygments import lexers;pprint.pprint([(x[0], x[1]) for x in lexers.get_all_lexers()]);"
14
14
15 EXTRA_LEXERS = {}
15 EXTRA_LEXERS = {}
16
16
17 #==============================================================================
17 #==============================================================================
18 # WHOOSH INDEX EXTENSIONS
18 # WHOOSH INDEX EXTENSIONS
19 #==============================================================================
19 #==============================================================================
20 # if INDEX_EXTENSIONS is [] it'll use pygments lexers extensions by default.
20 # if INDEX_EXTENSIONS is [] it'll use pygments lexers extensions by default.
21 # To set your own just add to this list extensions to index with content
21 # To set your own just add to this list extensions to index with content
22 INDEX_EXTENSIONS = []
22 INDEX_EXTENSIONS = []
23
23
24 # additional extensions for indexing besides the default from pygments
24 # additional extensions for indexing besides the default from pygments
25 # those get's added to INDEX_EXTENSIONS
25 # those get's added to INDEX_EXTENSIONS
26 EXTRA_INDEX_EXTENSIONS = []
26 EXTRA_INDEX_EXTENSIONS = []
27
27
28
28
29 #==============================================================================
29 #==============================================================================
30 # POST CREATE REPOSITORY HOOK
30 # POST CREATE REPOSITORY HOOK
31 #==============================================================================
31 #==============================================================================
32 # this function will be executed after each repository is created
32 # this function will be executed after each repository is created
33 def _crrepohook(*args, **kwargs):
33 def _crrepohook(*args, **kwargs):
34 """
34 """
35 Post create repository HOOK
35 Post create repository HOOK
36 kwargs available:
36 kwargs available:
37 :param repo_name:
37 :param repo_name:
38 :param repo_type:
38 :param repo_type:
39 :param description:
39 :param description:
40 :param private:
40 :param private:
41 :param created_on:
41 :param created_on:
42 :param enable_downloads:
42 :param enable_downloads:
43 :param repo_id:
43 :param repo_id:
44 :param user_id:
44 :param user_id:
45 :param enable_statistics:
45 :param enable_statistics:
46 :param clone_uri:
46 :param clone_uri:
47 :param fork_id:
47 :param fork_id:
48 :param group_id:
48 :param group_id:
49 :param created_by:
49 :param created_by:
50 """
50 """
51 return 0
51 return 0
52 CREATE_REPO_HOOK = _crrepohook
52 CREATE_REPO_HOOK = _crrepohook
53
53
54
54
55 #==============================================================================
55 #==============================================================================
56 # PRE CREATE USER HOOK
57 #==============================================================================
58 # this function will be executed before each user is created
59 def _pre_cruserhook(*args, **kwargs):
60 """
61 Pre create user HOOK, it returns a tuple of bool, reason.
62 If bool is False the user creation will be stopped and reason
63 will be displayed to the user.
64 kwargs available:
65 :param username:
66 :param password:
67 :param email:
68 :param firstname:
69 :param lastname:
70 :param active:
71 :param admin:
72 :param created_by:
73 """
74 reason = 'allowed'
75 return True, reason
76 PRE_CREATE_USER_HOOK = _pre_cruserhook
77
78 #==============================================================================
56 # POST CREATE USER HOOK
79 # POST CREATE USER HOOK
57 #==============================================================================
80 #==============================================================================
58 # this function will be executed after each user is created
81 # this function will be executed after each user is created
59 def _cruserhook(*args, **kwargs):
82 def _cruserhook(*args, **kwargs):
60 """
83 """
61 Post create user HOOK
84 Post create user HOOK
62 kwargs available:
85 kwargs available:
63 :param username:
86 :param username:
64 :param full_name_or_username:
87 :param full_name_or_username:
65 :param full_contact:
88 :param full_contact:
66 :param user_id:
89 :param user_id:
67 :param name:
90 :param name:
68 :param firstname:
91 :param firstname:
69 :param short_contact:
92 :param short_contact:
70 :param admin:
93 :param admin:
71 :param lastname:
94 :param lastname:
72 :param ip_addresses:
95 :param ip_addresses:
73 :param ldap_dn:
96 :param ldap_dn:
74 :param email:
97 :param email:
75 :param api_key:
98 :param api_key:
76 :param last_login:
99 :param last_login:
77 :param full_name:
100 :param full_name:
78 :param active:
101 :param active:
79 :param password:
102 :param password:
80 :param emails:
103 :param emails:
81 :param inherit_default_permissions:
104 :param inherit_default_permissions:
82 :param created_by:
105 :param created_by:
83 """
106 """
84 return 0
107 return 0
85 CREATE_USER_HOOK = _cruserhook
108 CREATE_USER_HOOK = _cruserhook
86
109
87
110
88 #==============================================================================
111 #==============================================================================
89 # POST DELETE REPOSITORY HOOK
112 # POST DELETE REPOSITORY HOOK
90 #==============================================================================
113 #==============================================================================
91 # this function will be executed after each repository deletion
114 # this function will be executed after each repository deletion
92 def _dlrepohook(*args, **kwargs):
115 def _dlrepohook(*args, **kwargs):
93 """
116 """
94 Post delete repository HOOK
117 Post delete repository HOOK
95 kwargs available:
118 kwargs available:
96 :param repo_name:
119 :param repo_name:
97 :param repo_type:
120 :param repo_type:
98 :param description:
121 :param description:
99 :param private:
122 :param private:
100 :param created_on:
123 :param created_on:
101 :param enable_downloads:
124 :param enable_downloads:
102 :param repo_id:
125 :param repo_id:
103 :param user_id:
126 :param user_id:
104 :param enable_statistics:
127 :param enable_statistics:
105 :param clone_uri:
128 :param clone_uri:
106 :param fork_id:
129 :param fork_id:
107 :param group_id:
130 :param group_id:
108 :param deleted_by:
131 :param deleted_by:
109 :param deleted_on:
132 :param deleted_on:
110 """
133 """
111 return 0
134 return 0
112 DELETE_REPO_HOOK = _dlrepohook
135 DELETE_REPO_HOOK = _dlrepohook
113
136
114
137
115 #==============================================================================
138 #==============================================================================
116 # POST DELETE USER HOOK
139 # POST DELETE USER HOOK
117 #==============================================================================
140 #==============================================================================
118 # this function will be executed after each user is deleted
141 # this function will be executed after each user is deleted
119 def _dluserhook(*args, **kwargs):
142 def _dluserhook(*args, **kwargs):
120 """
143 """
121 Post delete user HOOK
144 Post delete user HOOK
122 kwargs available:
145 kwargs available:
123 :param username:
146 :param username:
124 :param full_name_or_username:
147 :param full_name_or_username:
125 :param full_contact:
148 :param full_contact:
126 :param user_id:
149 :param user_id:
127 :param name:
150 :param name:
128 :param firstname:
151 :param firstname:
129 :param short_contact:
152 :param short_contact:
130 :param admin:
153 :param admin:
131 :param lastname:
154 :param lastname:
132 :param ip_addresses:
155 :param ip_addresses:
133 :param ldap_dn:
156 :param ldap_dn:
134 :param email:
157 :param email:
135 :param api_key:
158 :param api_key:
136 :param last_login:
159 :param last_login:
137 :param full_name:
160 :param full_name:
138 :param active:
161 :param active:
139 :param password:
162 :param password:
140 :param emails:
163 :param emails:
141 :param inherit_default_permissions:
164 :param inherit_default_permissions:
142 :param deleted_by:
165 :param deleted_by:
143 """
166 """
144 return 0
167 return 0
145 DELETE_USER_HOOK = _dluserhook
168 DELETE_USER_HOOK = _dluserhook
146
169
147
170
148 #==============================================================================
171 #==============================================================================
149 # POST PUSH HOOK
172 # POST PUSH HOOK
150 #==============================================================================
173 #==============================================================================
151
174
152 # this function will be executed after each push it's executed after the
175 # this function will be executed after each push it's executed after the
153 # build-in hook that RhodeCode uses for logging pushes
176 # build-in hook that RhodeCode uses for logging pushes
154 def _pushhook(*args, **kwargs):
177 def _pushhook(*args, **kwargs):
155 """
178 """
156 Post push hook
179 Post push hook
157 kwargs available:
180 kwargs available:
158
181
159 :param server_url: url of instance that triggered this hook
182 :param server_url: url of instance that triggered this hook
160 :param config: path to .ini config used
183 :param config: path to .ini config used
161 :param scm: type of VS 'git' or 'hg'
184 :param scm: type of VS 'git' or 'hg'
162 :param username: name of user who pushed
185 :param username: name of user who pushed
163 :param ip: ip of who pushed
186 :param ip: ip of who pushed
164 :param action: push
187 :param action: push
165 :param repository: repository name
188 :param repository: repository name
166 :param pushed_revs: list of pushed revisions
189 :param pushed_revs: list of pushed revisions
167 """
190 """
168 return 0
191 return 0
169 PUSH_HOOK = _pushhook
192 PUSH_HOOK = _pushhook
170
193
171
194
172 #==============================================================================
195 #==============================================================================
173 # POST PULL HOOK
196 # POST PULL HOOK
174 #==============================================================================
197 #==============================================================================
175
198
176 # this function will be executed after each push it's executed after the
199 # this function will be executed after each push it's executed after the
177 # build-in hook that RhodeCode uses for logging pulls
200 # build-in hook that RhodeCode uses for logging pulls
178 def _pullhook(*args, **kwargs):
201 def _pullhook(*args, **kwargs):
179 """
202 """
180 Post pull hook
203 Post pull hook
181 kwargs available::
204 kwargs available::
182
205
183 :param server_url: url of instance that triggered this hook
206 :param server_url: url of instance that triggered this hook
184 :param config: path to .ini config used
207 :param config: path to .ini config used
185 :param scm: type of VS 'git' or 'hg'
208 :param scm: type of VS 'git' or 'hg'
186 :param username: name of user who pulled
209 :param username: name of user who pulled
187 :param ip: ip of who pulled
210 :param ip: ip of who pulled
188 :param action: pull
211 :param action: pull
189 :param repository: repository name
212 :param repository: repository name
190 """
213 """
191 return 0
214 return 0
192 PULL_HOOK = _pullhook
215 PULL_HOOK = _pullhook
@@ -1,361 +1,363 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.users
3 rhodecode.controllers.admin.users
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Users crud controller for pylons
6 Users crud controller for pylons
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 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 from pylons import response
29 from pylons import response
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from pylons import request, session, tmpl_context as c, url, config
32 from pylons import request, session, tmpl_context as c, url, config
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35
35
36 import rhodecode
36 import rhodecode
37 from rhodecode.lib.exceptions import DefaultUserException, \
37 from rhodecode.lib.exceptions import DefaultUserException, \
38 UserOwnsReposException
38 UserOwnsReposException, UserCreationError
39 from rhodecode.lib import helpers as h
39 from rhodecode.lib import helpers as h
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
41 AuthUser
41 AuthUser
42 from rhodecode.lib.base import BaseController, render
42 from rhodecode.lib.base import BaseController, render
43
43
44 from rhodecode.model.db import User, UserEmailMap, UserIpMap, UserToPerm
44 from rhodecode.model.db import User, UserEmailMap, UserIpMap, UserToPerm
45 from rhodecode.model.forms import UserForm, CustomDefaultPermissionsForm
45 from rhodecode.model.forms import UserForm, CustomDefaultPermissionsForm
46 from rhodecode.model.user import UserModel
46 from rhodecode.model.user import UserModel
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.lib.utils import action_logger
48 from rhodecode.lib.utils import action_logger
49 from rhodecode.lib.compat import json
49 from rhodecode.lib.compat import json
50 from rhodecode.lib.utils2 import datetime_to_time, str2bool
50 from rhodecode.lib.utils2 import datetime_to_time, str2bool
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class UsersController(BaseController):
55 class UsersController(BaseController):
56 """REST Controller styled on the Atom Publishing Protocol"""
56 """REST Controller styled on the Atom Publishing Protocol"""
57 # To properly map this controller, ensure your config/routing.py
57 # To properly map this controller, ensure your config/routing.py
58 # file has a resource setup:
58 # file has a resource setup:
59 # map.resource('user', 'users')
59 # map.resource('user', 'users')
60
60
61 @LoginRequired()
61 @LoginRequired()
62 @HasPermissionAllDecorator('hg.admin')
62 @HasPermissionAllDecorator('hg.admin')
63 def __before__(self):
63 def __before__(self):
64 super(UsersController, self).__before__()
64 super(UsersController, self).__before__()
65 c.available_permissions = config['available_permissions']
65 c.available_permissions = config['available_permissions']
66
66
67 def index(self, format='html'):
67 def index(self, format='html'):
68 """GET /users: All items in the collection"""
68 """GET /users: All items in the collection"""
69 # url('users')
69 # url('users')
70
70
71 c.users_list = User.query().order_by(User.username)\
71 c.users_list = User.query().order_by(User.username)\
72 .filter(User.username != User.DEFAULT_USER)\
72 .filter(User.username != User.DEFAULT_USER)\
73 .all()
73 .all()
74
74
75 users_data = []
75 users_data = []
76 total_records = len(c.users_list)
76 total_records = len(c.users_list)
77 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
77 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
78 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
78 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
79
79
80 grav_tmpl = lambda user_email, size: (
80 grav_tmpl = lambda user_email, size: (
81 template.get_def("user_gravatar")
81 template.get_def("user_gravatar")
82 .render(user_email, size, _=_, h=h, c=c))
82 .render(user_email, size, _=_, h=h, c=c))
83
83
84 user_lnk = lambda user_id, username: (
84 user_lnk = lambda user_id, username: (
85 template.get_def("user_name")
85 template.get_def("user_name")
86 .render(user_id, username, _=_, h=h, c=c))
86 .render(user_id, username, _=_, h=h, c=c))
87
87
88 user_actions = lambda user_id, username: (
88 user_actions = lambda user_id, username: (
89 template.get_def("user_actions")
89 template.get_def("user_actions")
90 .render(user_id, username, _=_, h=h, c=c))
90 .render(user_id, username, _=_, h=h, c=c))
91
91
92 for user in c.users_list:
92 for user in c.users_list:
93
93
94 users_data.append({
94 users_data.append({
95 "gravatar": grav_tmpl(user. email, 24),
95 "gravatar": grav_tmpl(user. email, 24),
96 "raw_username": user.username,
96 "raw_username": user.username,
97 "username": user_lnk(user.user_id, user.username),
97 "username": user_lnk(user.user_id, user.username),
98 "firstname": user.name,
98 "firstname": user.name,
99 "lastname": user.lastname,
99 "lastname": user.lastname,
100 "last_login": h.fmt_date(user.last_login),
100 "last_login": h.fmt_date(user.last_login),
101 "last_login_raw": datetime_to_time(user.last_login),
101 "last_login_raw": datetime_to_time(user.last_login),
102 "active": h.boolicon(user.active),
102 "active": h.boolicon(user.active),
103 "admin": h.boolicon(user.admin),
103 "admin": h.boolicon(user.admin),
104 "ldap": h.boolicon(bool(user.ldap_dn)),
104 "ldap": h.boolicon(bool(user.ldap_dn)),
105 "action": user_actions(user.user_id, user.username),
105 "action": user_actions(user.user_id, user.username),
106 })
106 })
107
107
108 c.data = json.dumps({
108 c.data = json.dumps({
109 "totalRecords": total_records,
109 "totalRecords": total_records,
110 "startIndex": 0,
110 "startIndex": 0,
111 "sort": None,
111 "sort": None,
112 "dir": "asc",
112 "dir": "asc",
113 "records": users_data
113 "records": users_data
114 })
114 })
115
115
116 return render('admin/users/users.html')
116 return render('admin/users/users.html')
117
117
118 def create(self):
118 def create(self):
119 """POST /users: Create a new item"""
119 """POST /users: Create a new item"""
120 # url('users')
120 # url('users')
121
121
122 user_model = UserModel()
122 user_model = UserModel()
123 user_form = UserForm()()
123 user_form = UserForm()()
124 try:
124 try:
125 form_result = user_form.to_python(dict(request.POST))
125 form_result = user_form.to_python(dict(request.POST))
126 user_model.create(form_result)
126 user_model.create(form_result)
127 usr = form_result['username']
127 usr = form_result['username']
128 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
128 action_logger(self.rhodecode_user, 'admin_created_user:%s' % usr,
129 None, self.ip_addr, self.sa)
129 None, self.ip_addr, self.sa)
130 h.flash(_('Created user %s') % usr,
130 h.flash(_('Created user %s') % usr,
131 category='success')
131 category='success')
132 Session().commit()
132 Session().commit()
133 except formencode.Invalid, errors:
133 except formencode.Invalid, errors:
134 return htmlfill.render(
134 return htmlfill.render(
135 render('admin/users/user_add.html'),
135 render('admin/users/user_add.html'),
136 defaults=errors.value,
136 defaults=errors.value,
137 errors=errors.error_dict or {},
137 errors=errors.error_dict or {},
138 prefix_error=False,
138 prefix_error=False,
139 encoding="UTF-8")
139 encoding="UTF-8")
140 except UserCreationError, e:
141 h.flash(e, 'error')
140 except Exception:
142 except Exception:
141 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
142 h.flash(_('Error occurred during creation of user %s') \
144 h.flash(_('Error occurred during creation of user %s') \
143 % request.POST.get('username'), category='error')
145 % request.POST.get('username'), category='error')
144 return redirect(url('users'))
146 return redirect(url('users'))
145
147
146 def new(self, format='html'):
148 def new(self, format='html'):
147 """GET /users/new: Form to create a new item"""
149 """GET /users/new: Form to create a new item"""
148 # url('new_user')
150 # url('new_user')
149 return render('admin/users/user_add.html')
151 return render('admin/users/user_add.html')
150
152
151 def update(self, id):
153 def update(self, id):
152 """PUT /users/id: Update an existing item"""
154 """PUT /users/id: Update an existing item"""
153 # Forms posted to this method should contain a hidden field:
155 # Forms posted to this method should contain a hidden field:
154 # <input type="hidden" name="_method" value="PUT" />
156 # <input type="hidden" name="_method" value="PUT" />
155 # Or using helpers:
157 # Or using helpers:
156 # h.form(url('update_user', id=ID),
158 # h.form(url('update_user', id=ID),
157 # method='put')
159 # method='put')
158 # url('user', id=ID)
160 # url('user', id=ID)
159 user_model = UserModel()
161 user_model = UserModel()
160 c.user = user_model.get(id)
162 c.user = user_model.get(id)
161 c.ldap_dn = c.user.ldap_dn
163 c.ldap_dn = c.user.ldap_dn
162 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
164 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
163 _form = UserForm(edit=True, old_data={'user_id': id,
165 _form = UserForm(edit=True, old_data={'user_id': id,
164 'email': c.user.email})()
166 'email': c.user.email})()
165 form_result = {}
167 form_result = {}
166 try:
168 try:
167 form_result = _form.to_python(dict(request.POST))
169 form_result = _form.to_python(dict(request.POST))
168 skip_attrs = []
170 skip_attrs = []
169 if c.ldap_dn:
171 if c.ldap_dn:
170 #forbid updating username for ldap accounts
172 #forbid updating username for ldap accounts
171 skip_attrs = ['username']
173 skip_attrs = ['username']
172 user_model.update(id, form_result, skip_attrs=skip_attrs)
174 user_model.update(id, form_result, skip_attrs=skip_attrs)
173 usr = form_result['username']
175 usr = form_result['username']
174 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
176 action_logger(self.rhodecode_user, 'admin_updated_user:%s' % usr,
175 None, self.ip_addr, self.sa)
177 None, self.ip_addr, self.sa)
176 h.flash(_('User updated successfully'), category='success')
178 h.flash(_('User updated successfully'), category='success')
177 Session().commit()
179 Session().commit()
178 except formencode.Invalid, errors:
180 except formencode.Invalid, errors:
179 c.user_email_map = UserEmailMap.query()\
181 c.user_email_map = UserEmailMap.query()\
180 .filter(UserEmailMap.user == c.user).all()
182 .filter(UserEmailMap.user == c.user).all()
181 c.user_ip_map = UserIpMap.query()\
183 c.user_ip_map = UserIpMap.query()\
182 .filter(UserIpMap.user == c.user).all()
184 .filter(UserIpMap.user == c.user).all()
183 defaults = errors.value
185 defaults = errors.value
184 e = errors.error_dict or {}
186 e = errors.error_dict or {}
185 defaults.update({
187 defaults.update({
186 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
188 'create_repo_perm': user_model.has_perm(id, 'hg.create.repository'),
187 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
189 'fork_repo_perm': user_model.has_perm(id, 'hg.fork.repository'),
188 '_method': 'put'
190 '_method': 'put'
189 })
191 })
190 return htmlfill.render(
192 return htmlfill.render(
191 render('admin/users/user_edit.html'),
193 render('admin/users/user_edit.html'),
192 defaults=defaults,
194 defaults=defaults,
193 errors=e,
195 errors=e,
194 prefix_error=False,
196 prefix_error=False,
195 encoding="UTF-8")
197 encoding="UTF-8")
196 except Exception:
198 except Exception:
197 log.error(traceback.format_exc())
199 log.error(traceback.format_exc())
198 h.flash(_('Error occurred during update of user %s') \
200 h.flash(_('Error occurred during update of user %s') \
199 % form_result.get('username'), category='error')
201 % form_result.get('username'), category='error')
200 return redirect(url('edit_user', id=id))
202 return redirect(url('edit_user', id=id))
201
203
202 def delete(self, id):
204 def delete(self, id):
203 """DELETE /users/id: Delete an existing item"""
205 """DELETE /users/id: Delete an existing item"""
204 # Forms posted to this method should contain a hidden field:
206 # Forms posted to this method should contain a hidden field:
205 # <input type="hidden" name="_method" value="DELETE" />
207 # <input type="hidden" name="_method" value="DELETE" />
206 # Or using helpers:
208 # Or using helpers:
207 # h.form(url('delete_user', id=ID),
209 # h.form(url('delete_user', id=ID),
208 # method='delete')
210 # method='delete')
209 # url('user', id=ID)
211 # url('user', id=ID)
210 usr = User.get_or_404(id)
212 usr = User.get_or_404(id)
211 try:
213 try:
212 UserModel().delete(usr)
214 UserModel().delete(usr)
213 Session().commit()
215 Session().commit()
214 h.flash(_('Successfully deleted user'), category='success')
216 h.flash(_('Successfully deleted user'), category='success')
215 except (UserOwnsReposException, DefaultUserException), e:
217 except (UserOwnsReposException, DefaultUserException), e:
216 h.flash(e, category='warning')
218 h.flash(e, category='warning')
217 except Exception:
219 except Exception:
218 log.error(traceback.format_exc())
220 log.error(traceback.format_exc())
219 h.flash(_('An error occurred during deletion of user'),
221 h.flash(_('An error occurred during deletion of user'),
220 category='error')
222 category='error')
221 return redirect(url('users'))
223 return redirect(url('users'))
222
224
223 def show(self, id, format='html'):
225 def show(self, id, format='html'):
224 """GET /users/id: Show a specific item"""
226 """GET /users/id: Show a specific item"""
225 # url('user', id=ID)
227 # url('user', id=ID)
226 User.get_or_404(-1)
228 User.get_or_404(-1)
227
229
228 def edit(self, id, format='html'):
230 def edit(self, id, format='html'):
229 """GET /users/id/edit: Form to edit an existing item"""
231 """GET /users/id/edit: Form to edit an existing item"""
230 # url('edit_user', id=ID)
232 # url('edit_user', id=ID)
231 c.user = User.get_or_404(id)
233 c.user = User.get_or_404(id)
232
234
233 if c.user.username == 'default':
235 if c.user.username == 'default':
234 h.flash(_("You can't edit this user"), category='warning')
236 h.flash(_("You can't edit this user"), category='warning')
235 return redirect(url('users'))
237 return redirect(url('users'))
236
238
237 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
239 c.perm_user = AuthUser(user_id=id, ip_addr=self.ip_addr)
238 c.user.permissions = {}
240 c.user.permissions = {}
239 c.granted_permissions = UserModel().fill_perms(c.user)\
241 c.granted_permissions = UserModel().fill_perms(c.user)\
240 .permissions['global']
242 .permissions['global']
241 c.user_email_map = UserEmailMap.query()\
243 c.user_email_map = UserEmailMap.query()\
242 .filter(UserEmailMap.user == c.user).all()
244 .filter(UserEmailMap.user == c.user).all()
243 c.user_ip_map = UserIpMap.query()\
245 c.user_ip_map = UserIpMap.query()\
244 .filter(UserIpMap.user == c.user).all()
246 .filter(UserIpMap.user == c.user).all()
245 umodel = UserModel()
247 umodel = UserModel()
246 c.ldap_dn = c.user.ldap_dn
248 c.ldap_dn = c.user.ldap_dn
247 defaults = c.user.get_dict()
249 defaults = c.user.get_dict()
248 defaults.update({
250 defaults.update({
249 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'),
251 'create_repo_perm': umodel.has_perm(c.user, 'hg.create.repository'),
250 'create_user_group_perm': umodel.has_perm(c.user, 'hg.usergroup.create.true'),
252 'create_user_group_perm': umodel.has_perm(c.user, 'hg.usergroup.create.true'),
251 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'),
253 'fork_repo_perm': umodel.has_perm(c.user, 'hg.fork.repository'),
252 })
254 })
253
255
254 return htmlfill.render(
256 return htmlfill.render(
255 render('admin/users/user_edit.html'),
257 render('admin/users/user_edit.html'),
256 defaults=defaults,
258 defaults=defaults,
257 encoding="UTF-8",
259 encoding="UTF-8",
258 force_defaults=False
260 force_defaults=False
259 )
261 )
260
262
261 def update_perm(self, id):
263 def update_perm(self, id):
262 """PUT /users_perm/id: Update an existing item"""
264 """PUT /users_perm/id: Update an existing item"""
263 # url('user_perm', id=ID, method='put')
265 # url('user_perm', id=ID, method='put')
264 user = User.get_or_404(id)
266 user = User.get_or_404(id)
265
267
266 try:
268 try:
267 form = CustomDefaultPermissionsForm()()
269 form = CustomDefaultPermissionsForm()()
268 form_result = form.to_python(request.POST)
270 form_result = form.to_python(request.POST)
269
271
270 inherit_perms = form_result['inherit_default_permissions']
272 inherit_perms = form_result['inherit_default_permissions']
271 user.inherit_default_permissions = inherit_perms
273 user.inherit_default_permissions = inherit_perms
272 Session().add(user)
274 Session().add(user)
273 user_model = UserModel()
275 user_model = UserModel()
274
276
275 defs = UserToPerm.query()\
277 defs = UserToPerm.query()\
276 .filter(UserToPerm.user == user)\
278 .filter(UserToPerm.user == user)\
277 .all()
279 .all()
278 for ug in defs:
280 for ug in defs:
279 Session().delete(ug)
281 Session().delete(ug)
280
282
281 if form_result['create_repo_perm']:
283 if form_result['create_repo_perm']:
282 user_model.grant_perm(id, 'hg.create.repository')
284 user_model.grant_perm(id, 'hg.create.repository')
283 else:
285 else:
284 user_model.grant_perm(id, 'hg.create.none')
286 user_model.grant_perm(id, 'hg.create.none')
285 if form_result['create_user_group_perm']:
287 if form_result['create_user_group_perm']:
286 user_model.grant_perm(id, 'hg.usergroup.create.true')
288 user_model.grant_perm(id, 'hg.usergroup.create.true')
287 else:
289 else:
288 user_model.grant_perm(id, 'hg.usergroup.create.false')
290 user_model.grant_perm(id, 'hg.usergroup.create.false')
289 if form_result['fork_repo_perm']:
291 if form_result['fork_repo_perm']:
290 user_model.grant_perm(id, 'hg.fork.repository')
292 user_model.grant_perm(id, 'hg.fork.repository')
291 else:
293 else:
292 user_model.grant_perm(id, 'hg.fork.none')
294 user_model.grant_perm(id, 'hg.fork.none')
293 h.flash(_("Updated permissions"), category='success')
295 h.flash(_("Updated permissions"), category='success')
294 Session().commit()
296 Session().commit()
295 except Exception:
297 except Exception:
296 log.error(traceback.format_exc())
298 log.error(traceback.format_exc())
297 h.flash(_('An error occurred during permissions saving'),
299 h.flash(_('An error occurred during permissions saving'),
298 category='error')
300 category='error')
299 return redirect(url('edit_user', id=id))
301 return redirect(url('edit_user', id=id))
300
302
301 def add_email(self, id):
303 def add_email(self, id):
302 """POST /user_emails:Add an existing item"""
304 """POST /user_emails:Add an existing item"""
303 # url('user_emails', id=ID, method='put')
305 # url('user_emails', id=ID, method='put')
304
306
305 email = request.POST.get('new_email')
307 email = request.POST.get('new_email')
306 user_model = UserModel()
308 user_model = UserModel()
307
309
308 try:
310 try:
309 user_model.add_extra_email(id, email)
311 user_model.add_extra_email(id, email)
310 Session().commit()
312 Session().commit()
311 h.flash(_("Added email %s to user") % email, category='success')
313 h.flash(_("Added email %s to user") % email, category='success')
312 except formencode.Invalid, error:
314 except formencode.Invalid, error:
313 msg = error.error_dict['email']
315 msg = error.error_dict['email']
314 h.flash(msg, category='error')
316 h.flash(msg, category='error')
315 except Exception:
317 except Exception:
316 log.error(traceback.format_exc())
318 log.error(traceback.format_exc())
317 h.flash(_('An error occurred during email saving'),
319 h.flash(_('An error occurred during email saving'),
318 category='error')
320 category='error')
319 return redirect(url('edit_user', id=id))
321 return redirect(url('edit_user', id=id))
320
322
321 def delete_email(self, id):
323 def delete_email(self, id):
322 """DELETE /user_emails_delete/id: Delete an existing item"""
324 """DELETE /user_emails_delete/id: Delete an existing item"""
323 # url('user_emails_delete', id=ID, method='delete')
325 # url('user_emails_delete', id=ID, method='delete')
324 user_model = UserModel()
326 user_model = UserModel()
325 user_model.delete_extra_email(id, request.POST.get('del_email'))
327 user_model.delete_extra_email(id, request.POST.get('del_email'))
326 Session().commit()
328 Session().commit()
327 h.flash(_("Removed email from user"), category='success')
329 h.flash(_("Removed email from user"), category='success')
328 return redirect(url('edit_user', id=id))
330 return redirect(url('edit_user', id=id))
329
331
330 def add_ip(self, id):
332 def add_ip(self, id):
331 """POST /user_ips:Add an existing item"""
333 """POST /user_ips:Add an existing item"""
332 # url('user_ips', id=ID, method='put')
334 # url('user_ips', id=ID, method='put')
333
335
334 ip = request.POST.get('new_ip')
336 ip = request.POST.get('new_ip')
335 user_model = UserModel()
337 user_model = UserModel()
336
338
337 try:
339 try:
338 user_model.add_extra_ip(id, ip)
340 user_model.add_extra_ip(id, ip)
339 Session().commit()
341 Session().commit()
340 h.flash(_("Added ip %s to user") % ip, category='success')
342 h.flash(_("Added ip %s to user") % ip, category='success')
341 except formencode.Invalid, error:
343 except formencode.Invalid, error:
342 msg = error.error_dict['ip']
344 msg = error.error_dict['ip']
343 h.flash(msg, category='error')
345 h.flash(msg, category='error')
344 except Exception:
346 except Exception:
345 log.error(traceback.format_exc())
347 log.error(traceback.format_exc())
346 h.flash(_('An error occurred during ip saving'),
348 h.flash(_('An error occurred during ip saving'),
347 category='error')
349 category='error')
348 if 'default_user' in request.POST:
350 if 'default_user' in request.POST:
349 return redirect(url('edit_permission', id='default'))
351 return redirect(url('edit_permission', id='default'))
350 return redirect(url('edit_user', id=id))
352 return redirect(url('edit_user', id=id))
351
353
352 def delete_ip(self, id):
354 def delete_ip(self, id):
353 """DELETE /user_ips_delete/id: Delete an existing item"""
355 """DELETE /user_ips_delete/id: Delete an existing item"""
354 # url('user_ips_delete', id=ID, method='delete')
356 # url('user_ips_delete', id=ID, method='delete')
355 user_model = UserModel()
357 user_model = UserModel()
356 user_model.delete_extra_ip(id, request.POST.get('del_ip'))
358 user_model.delete_extra_ip(id, request.POST.get('del_ip'))
357 Session().commit()
359 Session().commit()
358 h.flash(_("Removed ip from user"), category='success')
360 h.flash(_("Removed ip from user"), category='success')
359 if 'default_user' in request.POST:
361 if 'default_user' in request.POST:
360 return redirect(url('edit_permission', id='default'))
362 return redirect(url('edit_permission', id='default'))
361 return redirect(url('edit_user', id=id))
363 return redirect(url('edit_user', id=id))
@@ -1,191 +1,204 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.login
3 rhodecode.controllers.login
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Login controller for rhodeocode
6 Login controller for rhodeocode
7
7
8 :created_on: Apr 22, 2010
8 :created_on: Apr 22, 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 formencode
27 import formencode
28 import datetime
28 import datetime
29 import urlparse
29 import urlparse
30
30
31 from formencode import htmlfill
31 from formencode import htmlfill
32 from webob.exc import HTTPFound
32 from webob.exc import HTTPFound
33 from pylons.i18n.translation import _
33 from pylons.i18n.translation import _
34 from pylons.controllers.util import abort, redirect
34 from pylons.controllers.util import abort, redirect
35 from pylons import request, response, session, tmpl_context as c, url
35 from pylons import request, response, session, tmpl_context as c, url
36
36
37 import rhodecode.lib.helpers as h
37 import rhodecode.lib.helpers as h
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
38 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
39 from rhodecode.lib.base import BaseController, render
39 from rhodecode.lib.base import BaseController, render
40 from rhodecode.lib.exceptions import UserCreationError
40 from rhodecode.model.db import User
41 from rhodecode.model.db import User
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
42 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
42 from rhodecode.model.user import UserModel
43 from rhodecode.model.user import UserModel
43 from rhodecode.model.meta import Session
44 from rhodecode.model.meta import Session
44
45
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 class LoginController(BaseController):
50 class LoginController(BaseController):
50
51
51 def __before__(self):
52 def __before__(self):
52 super(LoginController, self).__before__()
53 super(LoginController, self).__before__()
53
54
54 def index(self):
55 def index(self):
55 # redirect if already logged in
56 # redirect if already logged in
56 c.came_from = request.GET.get('came_from')
57 c.came_from = request.GET.get('came_from')
57 not_default = self.rhodecode_user.username != 'default'
58 not_default = self.rhodecode_user.username != 'default'
58 ip_allowed = self.rhodecode_user.ip_allowed
59 ip_allowed = self.rhodecode_user.ip_allowed
59 if self.rhodecode_user.is_authenticated and not_default and ip_allowed:
60 if self.rhodecode_user.is_authenticated and not_default and ip_allowed:
60 return redirect(url('home'))
61 return redirect(url('home'))
61
62
62 if request.POST:
63 if request.POST:
63 # import Login Form validator class
64 # import Login Form validator class
64 login_form = LoginForm()
65 login_form = LoginForm()
65 try:
66 try:
66 session.invalidate()
67 session.invalidate()
67 c.form_result = login_form.to_python(dict(request.POST))
68 c.form_result = login_form.to_python(dict(request.POST))
68 # form checks for username/password, now we're authenticated
69 # form checks for username/password, now we're authenticated
69 username = c.form_result['username']
70 username = c.form_result['username']
70 user = User.get_by_username(username, case_insensitive=True)
71 user = User.get_by_username(username, case_insensitive=True)
71 auth_user = AuthUser(user.user_id)
72 auth_user = AuthUser(user.user_id)
72 auth_user.set_authenticated()
73 auth_user.set_authenticated()
73 cs = auth_user.get_cookie_store()
74 cs = auth_user.get_cookie_store()
74 session['rhodecode_user'] = cs
75 session['rhodecode_user'] = cs
75 user.update_lastlogin()
76 user.update_lastlogin()
76 Session().commit()
77 Session().commit()
77
78
78 # If they want to be remembered, update the cookie
79 # If they want to be remembered, update the cookie
79 if c.form_result['remember']:
80 if c.form_result['remember']:
80 _year = (datetime.datetime.now() +
81 _year = (datetime.datetime.now() +
81 datetime.timedelta(seconds=60 * 60 * 24 * 365))
82 datetime.timedelta(seconds=60 * 60 * 24 * 365))
82 session._set_cookie_expires(_year)
83 session._set_cookie_expires(_year)
83
84
84 session.save()
85 session.save()
85
86
86 log.info('user %s is now authenticated and stored in '
87 log.info('user %s is now authenticated and stored in '
87 'session, session attrs %s' % (username, cs))
88 'session, session attrs %s' % (username, cs))
88
89
89 # dumps session attrs back to cookie
90 # dumps session attrs back to cookie
90 session._update_cookie_out()
91 session._update_cookie_out()
91
92
92 # we set new cookie
93 # we set new cookie
93 headers = None
94 headers = None
94 if session.request['set_cookie']:
95 if session.request['set_cookie']:
95 # send set-cookie headers back to response to update cookie
96 # send set-cookie headers back to response to update cookie
96 headers = [('Set-Cookie', session.request['cookie_out'])]
97 headers = [('Set-Cookie', session.request['cookie_out'])]
97
98
98 allowed_schemes = ['http', 'https']
99 allowed_schemes = ['http', 'https']
99 if c.came_from:
100 if c.came_from:
100 parsed = urlparse.urlparse(c.came_from)
101 parsed = urlparse.urlparse(c.came_from)
101 server_parsed = urlparse.urlparse(url.current())
102 server_parsed = urlparse.urlparse(url.current())
102 if parsed.scheme and parsed.scheme not in allowed_schemes:
103 if parsed.scheme and parsed.scheme not in allowed_schemes:
103 log.error(
104 log.error(
104 'Suspicious URL scheme detected %s for url %s' %
105 'Suspicious URL scheme detected %s for url %s' %
105 (parsed.scheme, parsed))
106 (parsed.scheme, parsed))
106 c.came_from = url('home')
107 c.came_from = url('home')
107 elif server_parsed.netloc != parsed.netloc:
108 elif server_parsed.netloc != parsed.netloc:
108 log.error('Suspicious NETLOC detected %s for url %s'
109 log.error('Suspicious NETLOC detected %s for url %s'
109 'server url is: %s' %
110 'server url is: %s' %
110 (parsed.netloc, parsed, server_parsed))
111 (parsed.netloc, parsed, server_parsed))
111 c.came_from = url('home')
112 c.came_from = url('home')
112 raise HTTPFound(location=c.came_from, headers=headers)
113 raise HTTPFound(location=c.came_from, headers=headers)
113 else:
114 else:
114 raise HTTPFound(location=url('home'), headers=headers)
115 raise HTTPFound(location=url('home'), headers=headers)
115
116
116 except formencode.Invalid, errors:
117 except formencode.Invalid, errors:
117 return htmlfill.render(
118 return htmlfill.render(
118 render('/login.html'),
119 render('/login.html'),
119 defaults=errors.value,
120 defaults=errors.value,
120 errors=errors.error_dict or {},
121 errors=errors.error_dict or {},
121 prefix_error=False,
122 prefix_error=False,
122 encoding="UTF-8")
123 encoding="UTF-8")
124 except UserCreationError, e:
125 # container auth or other auth functions that create users on
126 # the fly can throw this exception signaling that there's issue
127 # with user creation, explanation should be provided in
128 # Exception itself
129 h.flash(e, 'error')
123
130
124 return render('/login.html')
131 return render('/login.html')
125
132
126 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
133 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
127 'hg.register.manual_activate')
134 'hg.register.manual_activate')
128 def register(self):
135 def register(self):
129 c.auto_active = 'hg.register.auto_activate' in User.get_default_user()\
136 c.auto_active = 'hg.register.auto_activate' in User.get_default_user()\
130 .AuthUser.permissions['global']
137 .AuthUser.permissions['global']
131
138
132 if request.POST:
139 if request.POST:
133 register_form = RegisterForm()()
140 register_form = RegisterForm()()
134 try:
141 try:
135 form_result = register_form.to_python(dict(request.POST))
142 form_result = register_form.to_python(dict(request.POST))
136 form_result['active'] = c.auto_active
143 form_result['active'] = c.auto_active
137 UserModel().create_registration(form_result)
144 UserModel().create_registration(form_result)
138 h.flash(_('You have successfully registered into RhodeCode'),
145 h.flash(_('You have successfully registered into RhodeCode'),
139 category='success')
146 category='success')
140 Session().commit()
147 Session().commit()
141 return redirect(url('login_home'))
148 return redirect(url('login_home'))
142
149
143 except formencode.Invalid, errors:
150 except formencode.Invalid, errors:
144 return htmlfill.render(
151 return htmlfill.render(
145 render('/register.html'),
152 render('/register.html'),
146 defaults=errors.value,
153 defaults=errors.value,
147 errors=errors.error_dict or {},
154 errors=errors.error_dict or {},
148 prefix_error=False,
155 prefix_error=False,
149 encoding="UTF-8")
156 encoding="UTF-8")
157 except UserCreationError, e:
158 # container auth or other auth functions that create users on
159 # the fly can throw this exception signaling that there's issue
160 # with user creation, explanation should be provided in
161 # Exception itself
162 h.flash(e, 'error')
150
163
151 return render('/register.html')
164 return render('/register.html')
152
165
153 def password_reset(self):
166 def password_reset(self):
154 if request.POST:
167 if request.POST:
155 password_reset_form = PasswordResetForm()()
168 password_reset_form = PasswordResetForm()()
156 try:
169 try:
157 form_result = password_reset_form.to_python(dict(request.POST))
170 form_result = password_reset_form.to_python(dict(request.POST))
158 UserModel().reset_password_link(form_result)
171 UserModel().reset_password_link(form_result)
159 h.flash(_('Your password reset link was sent'),
172 h.flash(_('Your password reset link was sent'),
160 category='success')
173 category='success')
161 return redirect(url('login_home'))
174 return redirect(url('login_home'))
162
175
163 except formencode.Invalid, errors:
176 except formencode.Invalid, errors:
164 return htmlfill.render(
177 return htmlfill.render(
165 render('/password_reset.html'),
178 render('/password_reset.html'),
166 defaults=errors.value,
179 defaults=errors.value,
167 errors=errors.error_dict or {},
180 errors=errors.error_dict or {},
168 prefix_error=False,
181 prefix_error=False,
169 encoding="UTF-8")
182 encoding="UTF-8")
170
183
171 return render('/password_reset.html')
184 return render('/password_reset.html')
172
185
173 def password_reset_confirmation(self):
186 def password_reset_confirmation(self):
174 if request.GET and request.GET.get('key'):
187 if request.GET and request.GET.get('key'):
175 try:
188 try:
176 user = User.get_by_api_key(request.GET.get('key'))
189 user = User.get_by_api_key(request.GET.get('key'))
177 data = dict(email=user.email)
190 data = dict(email=user.email)
178 UserModel().reset_password(data)
191 UserModel().reset_password(data)
179 h.flash(_('Your password reset was successful, '
192 h.flash(_('Your password reset was successful, '
180 'new password has been sent to your email'),
193 'new password has been sent to your email'),
181 category='success')
194 category='success')
182 except Exception, e:
195 except Exception, e:
183 log.error(e)
196 log.error(e)
184 return redirect(url('reset_password'))
197 return redirect(url('reset_password'))
185
198
186 return redirect(url('login_home'))
199 return redirect(url('login_home'))
187
200
188 def logout(self):
201 def logout(self):
189 session.delete()
202 session.delete()
190 log.info('Logging out and deleting session for user')
203 log.info('Logging out and deleting session for user')
191 redirect(url('home'))
204 redirect(url('home'))
@@ -1,350 +1,361 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
10 from paste.httpexceptions import HTTPUnauthorized, HTTPForbidden
11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
11 from paste.httpheaders import WWW_AUTHENTICATE, AUTHORIZATION
12
12
13 from pylons import config, tmpl_context as c, request, session, url
13 from pylons import config, tmpl_context as c, request, session, url
14 from pylons.controllers import WSGIController
14 from pylons.controllers import WSGIController
15 from pylons.controllers.util import redirect
15 from pylons.controllers.util import redirect
16 from pylons.templating import render_mako as render
16 from pylons.templating import render_mako as render
17
17
18 from rhodecode import __version__, BACKENDS
18 from rhodecode import __version__, BACKENDS
19
19
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
20 from rhodecode.lib.utils2 import str2bool, safe_unicode, AttributeDict,\
21 safe_str, safe_int
21 safe_str, safe_int
22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
22 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
23 HasPermissionAnyMiddleware, CookieStoreWrapper
23 HasPermissionAnyMiddleware, CookieStoreWrapper
24 from rhodecode.lib.utils import get_repo_slug
24 from rhodecode.lib.utils import get_repo_slug
25 from rhodecode.lib.exceptions import UserCreationError
25 from rhodecode.model import meta
26 from rhodecode.model import meta
26
27
27 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
28 from rhodecode.model.db import Repository, RhodeCodeUi, User, RhodeCodeSetting
28 from rhodecode.model.notification import NotificationModel
29 from rhodecode.model.notification import NotificationModel
29 from rhodecode.model.scm import ScmModel
30 from rhodecode.model.scm import ScmModel
30 from rhodecode.model.meta import Session
31 from rhodecode.model.meta import Session
31
32
32 log = logging.getLogger(__name__)
33 log = logging.getLogger(__name__)
33
34
34
35
35 def _filter_proxy(ip):
36 def _filter_proxy(ip):
36 """
37 """
37 HEADERS can have multiple ips inside the left-most being the original
38 HEADERS can have multiple ips inside the left-most being the original
38 client, and each successive proxy that passed the request adding the IP
39 client, and each successive proxy that passed the request adding the IP
39 address where it received the request from.
40 address where it received the request from.
40
41
41 :param ip:
42 :param ip:
42 """
43 """
43 if ',' in ip:
44 if ',' in ip:
44 _ips = ip.split(',')
45 _ips = ip.split(',')
45 _first_ip = _ips[0].strip()
46 _first_ip = _ips[0].strip()
46 log.debug('Got multiple IPs %s, using %s' % (','.join(_ips), _first_ip))
47 log.debug('Got multiple IPs %s, using %s' % (','.join(_ips), _first_ip))
47 return _first_ip
48 return _first_ip
48 return ip
49 return ip
49
50
50
51
51 def _get_ip_addr(environ):
52 def _get_ip_addr(environ):
52 proxy_key = 'HTTP_X_REAL_IP'
53 proxy_key = 'HTTP_X_REAL_IP'
53 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
54 proxy_key2 = 'HTTP_X_FORWARDED_FOR'
54 def_key = 'REMOTE_ADDR'
55 def_key = 'REMOTE_ADDR'
55
56
56 ip = environ.get(proxy_key)
57 ip = environ.get(proxy_key)
57 if ip:
58 if ip:
58 return _filter_proxy(ip)
59 return _filter_proxy(ip)
59
60
60 ip = environ.get(proxy_key2)
61 ip = environ.get(proxy_key2)
61 if ip:
62 if ip:
62 return _filter_proxy(ip)
63 return _filter_proxy(ip)
63
64
64 ip = environ.get(def_key, '0.0.0.0')
65 ip = environ.get(def_key, '0.0.0.0')
65 return _filter_proxy(ip)
66 return _filter_proxy(ip)
66
67
67
68
68 def _get_access_path(environ):
69 def _get_access_path(environ):
69 path = environ.get('PATH_INFO')
70 path = environ.get('PATH_INFO')
70 org_req = environ.get('pylons.original_request')
71 org_req = environ.get('pylons.original_request')
71 if org_req:
72 if org_req:
72 path = org_req.environ.get('PATH_INFO')
73 path = org_req.environ.get('PATH_INFO')
73 return path
74 return path
74
75
75
76
76 class BasicAuth(AuthBasicAuthenticator):
77 class BasicAuth(AuthBasicAuthenticator):
77
78
78 def __init__(self, realm, authfunc, auth_http_code=None):
79 def __init__(self, realm, authfunc, auth_http_code=None):
79 self.realm = realm
80 self.realm = realm
80 self.authfunc = authfunc
81 self.authfunc = authfunc
81 self._rc_auth_http_code = auth_http_code
82 self._rc_auth_http_code = auth_http_code
82
83
83 def build_authentication(self):
84 def build_authentication(self):
84 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
85 head = WWW_AUTHENTICATE.tuples('Basic realm="%s"' % self.realm)
85 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
86 if self._rc_auth_http_code and self._rc_auth_http_code == '403':
86 # return 403 if alternative http return code is specified in
87 # return 403 if alternative http return code is specified in
87 # RhodeCode config
88 # RhodeCode config
88 return HTTPForbidden(headers=head)
89 return HTTPForbidden(headers=head)
89 return HTTPUnauthorized(headers=head)
90 return HTTPUnauthorized(headers=head)
90
91
91 def authenticate(self, environ):
92 def authenticate(self, environ):
92 authorization = AUTHORIZATION(environ)
93 authorization = AUTHORIZATION(environ)
93 if not authorization:
94 if not authorization:
94 return self.build_authentication()
95 return self.build_authentication()
95 (authmeth, auth) = authorization.split(' ', 1)
96 (authmeth, auth) = authorization.split(' ', 1)
96 if 'basic' != authmeth.lower():
97 if 'basic' != authmeth.lower():
97 return self.build_authentication()
98 return self.build_authentication()
98 auth = auth.strip().decode('base64')
99 auth = auth.strip().decode('base64')
99 _parts = auth.split(':', 1)
100 _parts = auth.split(':', 1)
100 if len(_parts) == 2:
101 if len(_parts) == 2:
101 username, password = _parts
102 username, password = _parts
102 if self.authfunc(environ, username, password):
103 if self.authfunc(environ, username, password):
103 return username
104 return username
104 return self.build_authentication()
105 return self.build_authentication()
105
106
106 __call__ = authenticate
107 __call__ = authenticate
107
108
108
109
109 class BaseVCSController(object):
110 class BaseVCSController(object):
110
111
111 def __init__(self, application, config):
112 def __init__(self, application, config):
112 self.application = application
113 self.application = application
113 self.config = config
114 self.config = config
114 # base path of repo locations
115 # base path of repo locations
115 self.basepath = self.config['base_path']
116 self.basepath = self.config['base_path']
116 #authenticate this mercurial request using authfunc
117 #authenticate this mercurial request using authfunc
117 self.authenticate = BasicAuth('', authfunc,
118 self.authenticate = BasicAuth('', authfunc,
118 config.get('auth_ret_code'))
119 config.get('auth_ret_code'))
119 self.ip_addr = '0.0.0.0'
120 self.ip_addr = '0.0.0.0'
120
121
121 def _handle_request(self, environ, start_response):
122 def _handle_request(self, environ, start_response):
122 raise NotImplementedError()
123 raise NotImplementedError()
123
124
124 def _get_by_id(self, repo_name):
125 def _get_by_id(self, repo_name):
125 """
126 """
126 Get's a special pattern _<ID> from clone url and tries to replace it
127 Get's a special pattern _<ID> from clone url and tries to replace it
127 with a repository_name for support of _<ID> non changable urls
128 with a repository_name for support of _<ID> non changable urls
128
129
129 :param repo_name:
130 :param repo_name:
130 """
131 """
131 try:
132 try:
132 data = repo_name.split('/')
133 data = repo_name.split('/')
133 if len(data) >= 2:
134 if len(data) >= 2:
134 by_id = data[1].split('_')
135 by_id = data[1].split('_')
135 if len(by_id) == 2 and by_id[1].isdigit():
136 if len(by_id) == 2 and by_id[1].isdigit():
136 _repo_name = Repository.get(by_id[1]).repo_name
137 _repo_name = Repository.get(by_id[1]).repo_name
137 data[1] = _repo_name
138 data[1] = _repo_name
138 except Exception:
139 except Exception:
139 log.debug('Failed to extract repo_name from id %s' % (
140 log.debug('Failed to extract repo_name from id %s' % (
140 traceback.format_exc()
141 traceback.format_exc()
141 )
142 )
142 )
143 )
143
144
144 return '/'.join(data)
145 return '/'.join(data)
145
146
146 def _invalidate_cache(self, repo_name):
147 def _invalidate_cache(self, repo_name):
147 """
148 """
148 Set's cache for this repository for invalidation on next access
149 Set's cache for this repository for invalidation on next access
149
150
150 :param repo_name: full repo name, also a cache key
151 :param repo_name: full repo name, also a cache key
151 """
152 """
152 ScmModel().mark_for_invalidation(repo_name)
153 ScmModel().mark_for_invalidation(repo_name)
153
154
154 def _check_permission(self, action, user, repo_name, ip_addr=None):
155 def _check_permission(self, action, user, repo_name, ip_addr=None):
155 """
156 """
156 Checks permissions using action (push/pull) user and repository
157 Checks permissions using action (push/pull) user and repository
157 name
158 name
158
159
159 :param action: push or pull action
160 :param action: push or pull action
160 :param user: user instance
161 :param user: user instance
161 :param repo_name: repository name
162 :param repo_name: repository name
162 """
163 """
163 #check IP
164 #check IP
164 authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
165 authuser = AuthUser(user_id=user.user_id, ip_addr=ip_addr)
165 if not authuser.ip_allowed:
166 if not authuser.ip_allowed:
166 return False
167 return False
167 else:
168 else:
168 log.info('Access for IP:%s allowed' % (ip_addr))
169 log.info('Access for IP:%s allowed' % (ip_addr))
169 if action == 'push':
170 if action == 'push':
170 if not HasPermissionAnyMiddleware('repository.write',
171 if not HasPermissionAnyMiddleware('repository.write',
171 'repository.admin')(user,
172 'repository.admin')(user,
172 repo_name):
173 repo_name):
173 return False
174 return False
174
175
175 else:
176 else:
176 #any other action need at least read permission
177 #any other action need at least read permission
177 if not HasPermissionAnyMiddleware('repository.read',
178 if not HasPermissionAnyMiddleware('repository.read',
178 'repository.write',
179 'repository.write',
179 'repository.admin')(user,
180 'repository.admin')(user,
180 repo_name):
181 repo_name):
181 return False
182 return False
182
183
183 return True
184 return True
184
185
185 def _get_ip_addr(self, environ):
186 def _get_ip_addr(self, environ):
186 return _get_ip_addr(environ)
187 return _get_ip_addr(environ)
187
188
188 def _check_ssl(self, environ, start_response):
189 def _check_ssl(self, environ, start_response):
189 """
190 """
190 Checks the SSL check flag and returns False if SSL is not present
191 Checks the SSL check flag and returns False if SSL is not present
191 and required True otherwise
192 and required True otherwise
192 """
193 """
193 org_proto = environ['wsgi._org_proto']
194 org_proto = environ['wsgi._org_proto']
194 #check if we have SSL required ! if not it's a bad request !
195 #check if we have SSL required ! if not it's a bad request !
195 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
196 require_ssl = str2bool(RhodeCodeUi.get_by_key('push_ssl').ui_value)
196 if require_ssl and org_proto == 'http':
197 if require_ssl and org_proto == 'http':
197 log.debug('proto is %s and SSL is required BAD REQUEST !'
198 log.debug('proto is %s and SSL is required BAD REQUEST !'
198 % org_proto)
199 % org_proto)
199 return False
200 return False
200 return True
201 return True
201
202
202 def _check_locking_state(self, environ, action, repo, user_id):
203 def _check_locking_state(self, environ, action, repo, user_id):
203 """
204 """
204 Checks locking on this repository, if locking is enabled and lock is
205 Checks locking on this repository, if locking is enabled and lock is
205 present returns a tuple of make_lock, locked, locked_by.
206 present returns a tuple of make_lock, locked, locked_by.
206 make_lock can have 3 states None (do nothing) True, make lock
207 make_lock can have 3 states None (do nothing) True, make lock
207 False release lock, This value is later propagated to hooks, which
208 False release lock, This value is later propagated to hooks, which
208 do the locking. Think about this as signals passed to hooks what to do.
209 do the locking. Think about this as signals passed to hooks what to do.
209
210
210 """
211 """
211 locked = False # defines that locked error should be thrown to user
212 locked = False # defines that locked error should be thrown to user
212 make_lock = None
213 make_lock = None
213 repo = Repository.get_by_repo_name(repo)
214 repo = Repository.get_by_repo_name(repo)
214 user = User.get(user_id)
215 user = User.get(user_id)
215
216
216 # this is kind of hacky, but due to how mercurial handles client-server
217 # this is kind of hacky, but due to how mercurial handles client-server
217 # server see all operation on changeset; bookmarks, phases and
218 # server see all operation on changeset; bookmarks, phases and
218 # obsolescence marker in different transaction, we don't want to check
219 # obsolescence marker in different transaction, we don't want to check
219 # locking on those
220 # locking on those
220 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
221 obsolete_call = environ['QUERY_STRING'] in ['cmd=listkeys',]
221 locked_by = repo.locked
222 locked_by = repo.locked
222 if repo and repo.enable_locking and not obsolete_call:
223 if repo and repo.enable_locking and not obsolete_call:
223 if action == 'push':
224 if action == 'push':
224 #check if it's already locked !, if it is compare users
225 #check if it's already locked !, if it is compare users
225 user_id, _date = repo.locked
226 user_id, _date = repo.locked
226 if user.user_id == user_id:
227 if user.user_id == user_id:
227 log.debug('Got push from user %s, now unlocking' % (user))
228 log.debug('Got push from user %s, now unlocking' % (user))
228 # unlock if we have push from user who locked
229 # unlock if we have push from user who locked
229 make_lock = False
230 make_lock = False
230 else:
231 else:
231 # we're not the same user who locked, ban with 423 !
232 # we're not the same user who locked, ban with 423 !
232 locked = True
233 locked = True
233 if action == 'pull':
234 if action == 'pull':
234 if repo.locked[0] and repo.locked[1]:
235 if repo.locked[0] and repo.locked[1]:
235 locked = True
236 locked = True
236 else:
237 else:
237 log.debug('Setting lock on repo %s by %s' % (repo, user))
238 log.debug('Setting lock on repo %s by %s' % (repo, user))
238 make_lock = True
239 make_lock = True
239
240
240 else:
241 else:
241 log.debug('Repository %s do not have locking enabled' % (repo))
242 log.debug('Repository %s do not have locking enabled' % (repo))
242 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
243 log.debug('FINAL locking values make_lock:%s,locked:%s,locked_by:%s'
243 % (make_lock, locked, locked_by))
244 % (make_lock, locked, locked_by))
244 return make_lock, locked, locked_by
245 return make_lock, locked, locked_by
245
246
246 def __call__(self, environ, start_response):
247 def __call__(self, environ, start_response):
247 start = time.time()
248 start = time.time()
248 try:
249 try:
249 return self._handle_request(environ, start_response)
250 return self._handle_request(environ, start_response)
250 finally:
251 finally:
251 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
252 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
252 log.debug('Request time: %.3fs' % (time.time() - start))
253 log.debug('Request time: %.3fs' % (time.time() - start))
253 meta.Session.remove()
254 meta.Session.remove()
254
255
255
256
256 class BaseController(WSGIController):
257 class BaseController(WSGIController):
257
258
258 def __before__(self):
259 def __before__(self):
259 """
260 """
260 __before__ is called before controller methods and after __call__
261 __before__ is called before controller methods and after __call__
261 """
262 """
262 c.rhodecode_version = __version__
263 c.rhodecode_version = __version__
263 c.rhodecode_instanceid = config.get('instance_id')
264 c.rhodecode_instanceid = config.get('instance_id')
264 c.rhodecode_name = config.get('rhodecode_title')
265 c.rhodecode_name = config.get('rhodecode_title')
265 c.rhodecode_bugtracker = config.get('bugtracker', 'http://bitbucket.org/marcinkuzminski/rhodecode/issues')
266 c.rhodecode_bugtracker = config.get('bugtracker', 'http://bitbucket.org/marcinkuzminski/rhodecode/issues')
266 c.use_gravatar = str2bool(config.get('use_gravatar'))
267 c.use_gravatar = str2bool(config.get('use_gravatar'))
267 c.ga_code = config.get('rhodecode_ga_code')
268 c.ga_code = config.get('rhodecode_ga_code')
268 # Visual options
269 # Visual options
269 c.visual = AttributeDict({})
270 c.visual = AttributeDict({})
270 rc_config = RhodeCodeSetting.get_app_settings()
271 rc_config = RhodeCodeSetting.get_app_settings()
271 ## DB stored
272 ## DB stored
272 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
273 c.visual.show_public_icon = str2bool(rc_config.get('rhodecode_show_public_icon'))
273 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
274 c.visual.show_private_icon = str2bool(rc_config.get('rhodecode_show_private_icon'))
274 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
275 c.visual.stylify_metatags = str2bool(rc_config.get('rhodecode_stylify_metatags'))
275 c.visual.dashboard_items = safe_int(rc_config.get('rhodecode_dashboard_items', 100))
276 c.visual.dashboard_items = safe_int(rc_config.get('rhodecode_dashboard_items', 100))
276 c.visual.repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
277 c.visual.repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
277 c.visual.show_version = str2bool(rc_config.get('rhodecode_show_version'))
278 c.visual.show_version = str2bool(rc_config.get('rhodecode_show_version'))
278
279
279 ## INI stored
280 ## INI stored
280 self.cut_off_limit = int(config.get('cut_off_limit'))
281 self.cut_off_limit = int(config.get('cut_off_limit'))
281 c.visual.allow_repo_location_change = str2bool(config.get('allow_repo_location_change', True))
282 c.visual.allow_repo_location_change = str2bool(config.get('allow_repo_location_change', True))
282 c.visual.allow_custom_hooks_settings = str2bool(config.get('allow_custom_hooks_settings', True))
283 c.visual.allow_custom_hooks_settings = str2bool(config.get('allow_custom_hooks_settings', True))
283
284
284 c.repo_name = get_repo_slug(request) # can be empty
285 c.repo_name = get_repo_slug(request) # can be empty
285 c.backends = BACKENDS.keys()
286 c.backends = BACKENDS.keys()
286 c.unread_notifications = NotificationModel()\
287 c.unread_notifications = NotificationModel()\
287 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
288 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
288 self.sa = meta.Session
289 self.sa = meta.Session
289 self.scm_model = ScmModel(self.sa)
290 self.scm_model = ScmModel(self.sa)
290
291
291 def __call__(self, environ, start_response):
292 def __call__(self, environ, start_response):
292 """Invoke the Controller"""
293 """Invoke the Controller"""
293 # WSGIController.__call__ dispatches to the Controller method
294 # WSGIController.__call__ dispatches to the Controller method
294 # the request is routed to. This routing information is
295 # the request is routed to. This routing information is
295 # available in environ['pylons.routes_dict']
296 # available in environ['pylons.routes_dict']
296 try:
297 try:
297 self.ip_addr = _get_ip_addr(environ)
298 self.ip_addr = _get_ip_addr(environ)
298 # make sure that we update permissions each time we call controller
299 # make sure that we update permissions each time we call controller
299 api_key = request.GET.get('api_key')
300 api_key = request.GET.get('api_key')
300 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
301 cookie_store = CookieStoreWrapper(session.get('rhodecode_user'))
301 user_id = cookie_store.get('user_id', None)
302 user_id = cookie_store.get('user_id', None)
302 username = get_container_username(environ, config)
303 username = get_container_username(environ, config)
303 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
304 try:
305 auth_user = AuthUser(user_id, api_key, username, self.ip_addr)
306 except UserCreationError, e:
307 from rhodecode.lib import helpers as h
308 h.flash(e, 'error')
309 # container auth or other auth functions that create users on
310 # the fly can throw this exception signaling that there's issue
311 # with user creation, explanation should be provided in
312 # Exception itself
313 auth_user = AuthUser(ip_addr=self.ip_addr)
314
304 request.user = auth_user
315 request.user = auth_user
305 self.rhodecode_user = c.rhodecode_user = auth_user
316 self.rhodecode_user = c.rhodecode_user = auth_user
306 if not self.rhodecode_user.is_authenticated and \
317 if not self.rhodecode_user.is_authenticated and \
307 self.rhodecode_user.user_id is not None:
318 self.rhodecode_user.user_id is not None:
308 self.rhodecode_user.set_authenticated(
319 self.rhodecode_user.set_authenticated(
309 cookie_store.get('is_authenticated')
320 cookie_store.get('is_authenticated')
310 )
321 )
311 log.info('IP: %s User: %s accessed %s' % (
322 log.info('IP: %s User: %s accessed %s' % (
312 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
323 self.ip_addr, auth_user, safe_unicode(_get_access_path(environ)))
313 )
324 )
314 return WSGIController.__call__(self, environ, start_response)
325 return WSGIController.__call__(self, environ, start_response)
315 finally:
326 finally:
316 meta.Session.remove()
327 meta.Session.remove()
317
328
318
329
319 class BaseRepoController(BaseController):
330 class BaseRepoController(BaseController):
320 """
331 """
321 Base class for controllers responsible for loading all needed data for
332 Base class for controllers responsible for loading all needed data for
322 repository loaded items are
333 repository loaded items are
323
334
324 c.rhodecode_repo: instance of scm repository
335 c.rhodecode_repo: instance of scm repository
325 c.rhodecode_db_repo: instance of db
336 c.rhodecode_db_repo: instance of db
326 c.repository_followers: number of followers
337 c.repository_followers: number of followers
327 c.repository_forks: number of forks
338 c.repository_forks: number of forks
328 c.repository_following: weather the current user is following the current repo
339 c.repository_following: weather the current user is following the current repo
329 """
340 """
330
341
331 def __before__(self):
342 def __before__(self):
332 super(BaseRepoController, self).__before__()
343 super(BaseRepoController, self).__before__()
333 if c.repo_name:
344 if c.repo_name:
334
345
335 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
346 dbr = c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
336 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
347 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
337 # update last change according to VCS data
348 # update last change according to VCS data
338 dbr.update_changeset_cache(dbr.get_changeset())
349 dbr.update_changeset_cache(dbr.get_changeset())
339 if c.rhodecode_repo is None:
350 if c.rhodecode_repo is None:
340 log.error('%s this repository is present in database but it '
351 log.error('%s this repository is present in database but it '
341 'cannot be created as an scm instance', c.repo_name)
352 'cannot be created as an scm instance', c.repo_name)
342
353
343 redirect(url('home'))
354 redirect(url('home'))
344
355
345 # some globals counter for menu
356 # some globals counter for menu
346 c.repository_followers = self.scm_model.get_followers(dbr)
357 c.repository_followers = self.scm_model.get_followers(dbr)
347 c.repository_forks = self.scm_model.get_forks(dbr)
358 c.repository_forks = self.scm_model.get_forks(dbr)
348 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
359 c.repository_pull_requests = self.scm_model.get_pull_requests(dbr)
349 c.repository_following = self.scm_model.is_following_repo(c.repo_name,
360 c.repository_following = self.scm_model.is_following_repo(c.repo_name,
350 self.rhodecode_user.user_id)
361 self.rhodecode_user.user_id)
@@ -1,92 +1,100 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.exceptions
3 rhodecode.lib.exceptions
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Set of custom exceptions used in RhodeCode
6 Set of custom exceptions used in RhodeCode
7
7
8 :created_on: Nov 17, 2010
8 :created_on: Nov 17, 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 from webob.exc import HTTPClientError
26 from webob.exc import HTTPClientError
27
27
28
28
29 class LdapUsernameError(Exception):
29 class LdapUsernameError(Exception):
30 pass
30 pass
31
31
32
32
33 class LdapPasswordError(Exception):
33 class LdapPasswordError(Exception):
34 pass
34 pass
35
35
36
36
37 class LdapConnectionError(Exception):
37 class LdapConnectionError(Exception):
38 pass
38 pass
39
39
40
40
41 class LdapImportError(Exception):
41 class LdapImportError(Exception):
42 pass
42 pass
43
43
44
44
45 class DefaultUserException(Exception):
45 class DefaultUserException(Exception):
46 pass
46 pass
47
47
48
48
49 class UserOwnsReposException(Exception):
49 class UserOwnsReposException(Exception):
50 pass
50 pass
51
51
52
52
53 class UserGroupsAssignedException(Exception):
53 class UserGroupsAssignedException(Exception):
54 pass
54 pass
55
55
56
56
57 class StatusChangeOnClosedPullRequestError(Exception):
57 class StatusChangeOnClosedPullRequestError(Exception):
58 pass
58 pass
59
59
60
60
61 class AttachedForksError(Exception):
61 class AttachedForksError(Exception):
62 pass
62 pass
63
63
64
64
65 class RepoGroupAssignmentError(Exception):
65 class RepoGroupAssignmentError(Exception):
66 pass
66 pass
67
67
68
68
69 class NonRelativePathError(Exception):
69 class NonRelativePathError(Exception):
70 pass
70 pass
71
71
72
72
73 class HTTPLockedRC(HTTPClientError):
73 class HTTPLockedRC(HTTPClientError):
74 """
74 """
75 Special Exception For locked Repos in RhodeCode, the return code can
75 Special Exception For locked Repos in RhodeCode, the return code can
76 be overwritten by _code keyword argument passed into constructors
76 be overwritten by _code keyword argument passed into constructors
77 """
77 """
78 code = 423
78 code = 423
79 title = explanation = 'Repository Locked'
79 title = explanation = 'Repository Locked'
80
80
81 def __init__(self, reponame, username, *args, **kwargs):
81 def __init__(self, reponame, username, *args, **kwargs):
82 from rhodecode import CONFIG
82 from rhodecode import CONFIG
83 from rhodecode.lib.utils2 import safe_int
83 from rhodecode.lib.utils2 import safe_int
84 _code = CONFIG.get('lock_ret_code')
84 _code = CONFIG.get('lock_ret_code')
85 self.code = safe_int(_code, self.code)
85 self.code = safe_int(_code, self.code)
86 self.title = self.explanation = ('Repository `%s` locked by '
86 self.title = self.explanation = ('Repository `%s` locked by '
87 'user `%s`' % (reponame, username))
87 'user `%s`' % (reponame, username))
88 super(HTTPLockedRC, self).__init__(*args, **kwargs)
88 super(HTTPLockedRC, self).__init__(*args, **kwargs)
89
89
90
90
91 class IMCCommitError(Exception):
91 class IMCCommitError(Exception):
92 pass
92 pass
93
94
95 class UserCreationError(Exception):
96 pass
97
98
99 class RepositoryCreationError(Exception):
100 pass
@@ -1,463 +1,472 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.hooks
3 rhodecode.lib.hooks
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Hooks runned by rhodecode
6 Hooks runned by rhodecode
7
7
8 :created_on: Aug 6, 2010
8 :created_on: Aug 6, 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 import os
25 import os
26 import sys
26 import sys
27 import time
27 import time
28 import binascii
28 import binascii
29 import traceback
29 import traceback
30 from inspect import isfunction
30 from inspect import isfunction
31
31
32 from rhodecode.lib.vcs.utils.hgcompat import nullrev, revrange
32 from rhodecode.lib.vcs.utils.hgcompat import nullrev, revrange
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib.utils import action_logger
34 from rhodecode.lib.utils import action_logger
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
36 from rhodecode.lib.compat import json
36 from rhodecode.lib.compat import json
37 from rhodecode.lib.exceptions import HTTPLockedRC
37 from rhodecode.lib.exceptions import HTTPLockedRC, UserCreationError
38 from rhodecode.lib.utils2 import safe_str, _extract_extras
38 from rhodecode.lib.utils2 import safe_str, _extract_extras
39 from rhodecode.model.db import Repository, User
39 from rhodecode.model.db import Repository, User
40
40
41
41
42 def _get_scm_size(alias, root_path):
42 def _get_scm_size(alias, root_path):
43
43
44 if not alias.startswith('.'):
44 if not alias.startswith('.'):
45 alias += '.'
45 alias += '.'
46
46
47 size_scm, size_root = 0, 0
47 size_scm, size_root = 0, 0
48 for path, dirs, files in os.walk(safe_str(root_path)):
48 for path, dirs, files in os.walk(safe_str(root_path)):
49 if path.find(alias) != -1:
49 if path.find(alias) != -1:
50 for f in files:
50 for f in files:
51 try:
51 try:
52 size_scm += os.path.getsize(os.path.join(path, f))
52 size_scm += os.path.getsize(os.path.join(path, f))
53 except OSError:
53 except OSError:
54 pass
54 pass
55 else:
55 else:
56 for f in files:
56 for f in files:
57 try:
57 try:
58 size_root += os.path.getsize(os.path.join(path, f))
58 size_root += os.path.getsize(os.path.join(path, f))
59 except OSError:
59 except OSError:
60 pass
60 pass
61
61
62 size_scm_f = h.format_byte_size(size_scm)
62 size_scm_f = h.format_byte_size(size_scm)
63 size_root_f = h.format_byte_size(size_root)
63 size_root_f = h.format_byte_size(size_root)
64 size_total_f = h.format_byte_size(size_root + size_scm)
64 size_total_f = h.format_byte_size(size_root + size_scm)
65
65
66 return size_scm_f, size_root_f, size_total_f
66 return size_scm_f, size_root_f, size_total_f
67
67
68
68
69 def repo_size(ui, repo, hooktype=None, **kwargs):
69 def repo_size(ui, repo, hooktype=None, **kwargs):
70 """
70 """
71 Presents size of repository after push
71 Presents size of repository after push
72
72
73 :param ui:
73 :param ui:
74 :param repo:
74 :param repo:
75 :param hooktype:
75 :param hooktype:
76 """
76 """
77
77
78 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
78 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
79
79
80 last_cs = repo[len(repo) - 1]
80 last_cs = repo[len(repo) - 1]
81
81
82 msg = ('Repository size .hg:%s repo:%s total:%s\n'
82 msg = ('Repository size .hg:%s repo:%s total:%s\n'
83 'Last revision is now r%s:%s\n') % (
83 'Last revision is now r%s:%s\n') % (
84 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
84 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
85 )
85 )
86
86
87 sys.stdout.write(msg)
87 sys.stdout.write(msg)
88
88
89
89
90 def pre_push(ui, repo, **kwargs):
90 def pre_push(ui, repo, **kwargs):
91 # pre push function, currently used to ban pushing when
91 # pre push function, currently used to ban pushing when
92 # repository is locked
92 # repository is locked
93 ex = _extract_extras()
93 ex = _extract_extras()
94
94
95 usr = User.get_by_username(ex.username)
95 usr = User.get_by_username(ex.username)
96 if ex.locked_by[0] and usr.user_id != int(ex.locked_by[0]):
96 if ex.locked_by[0] and usr.user_id != int(ex.locked_by[0]):
97 locked_by = User.get(ex.locked_by[0]).username
97 locked_by = User.get(ex.locked_by[0]).username
98 # this exception is interpreted in git/hg middlewares and based
98 # this exception is interpreted in git/hg middlewares and based
99 # on that proper return code is server to client
99 # on that proper return code is server to client
100 _http_ret = HTTPLockedRC(ex.repository, locked_by)
100 _http_ret = HTTPLockedRC(ex.repository, locked_by)
101 if str(_http_ret.code).startswith('2'):
101 if str(_http_ret.code).startswith('2'):
102 #2xx Codes don't raise exceptions
102 #2xx Codes don't raise exceptions
103 sys.stdout.write(_http_ret.title)
103 sys.stdout.write(_http_ret.title)
104 else:
104 else:
105 raise _http_ret
105 raise _http_ret
106
106
107
107
108 def pre_pull(ui, repo, **kwargs):
108 def pre_pull(ui, repo, **kwargs):
109 # pre push function, currently used to ban pushing when
109 # pre push function, currently used to ban pushing when
110 # repository is locked
110 # repository is locked
111 ex = _extract_extras()
111 ex = _extract_extras()
112 if ex.locked_by[0]:
112 if ex.locked_by[0]:
113 locked_by = User.get(ex.locked_by[0]).username
113 locked_by = User.get(ex.locked_by[0]).username
114 # this exception is interpreted in git/hg middlewares and based
114 # this exception is interpreted in git/hg middlewares and based
115 # on that proper return code is server to client
115 # on that proper return code is server to client
116 _http_ret = HTTPLockedRC(ex.repository, locked_by)
116 _http_ret = HTTPLockedRC(ex.repository, locked_by)
117 if str(_http_ret.code).startswith('2'):
117 if str(_http_ret.code).startswith('2'):
118 #2xx Codes don't raise exceptions
118 #2xx Codes don't raise exceptions
119 sys.stdout.write(_http_ret.title)
119 sys.stdout.write(_http_ret.title)
120 else:
120 else:
121 raise _http_ret
121 raise _http_ret
122
122
123
123
124 def log_pull_action(ui, repo, **kwargs):
124 def log_pull_action(ui, repo, **kwargs):
125 """
125 """
126 Logs user last pull action
126 Logs user last pull action
127
127
128 :param ui:
128 :param ui:
129 :param repo:
129 :param repo:
130 """
130 """
131 ex = _extract_extras()
131 ex = _extract_extras()
132
132
133 user = User.get_by_username(ex.username)
133 user = User.get_by_username(ex.username)
134 action = 'pull'
134 action = 'pull'
135 action_logger(user, action, ex.repository, ex.ip, commit=True)
135 action_logger(user, action, ex.repository, ex.ip, commit=True)
136 # extension hook call
136 # extension hook call
137 from rhodecode import EXTENSIONS
137 from rhodecode import EXTENSIONS
138 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
138 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
139 if isfunction(callback):
139 if isfunction(callback):
140 kw = {}
140 kw = {}
141 kw.update(ex)
141 kw.update(ex)
142 callback(**kw)
142 callback(**kw)
143
143
144 if ex.make_lock is not None and ex.make_lock:
144 if ex.make_lock is not None and ex.make_lock:
145 Repository.lock(Repository.get_by_repo_name(ex.repository), user.user_id)
145 Repository.lock(Repository.get_by_repo_name(ex.repository), user.user_id)
146 #msg = 'Made lock on repo `%s`' % repository
146 #msg = 'Made lock on repo `%s`' % repository
147 #sys.stdout.write(msg)
147 #sys.stdout.write(msg)
148
148
149 if ex.locked_by[0]:
149 if ex.locked_by[0]:
150 locked_by = User.get(ex.locked_by[0]).username
150 locked_by = User.get(ex.locked_by[0]).username
151 _http_ret = HTTPLockedRC(ex.repository, locked_by)
151 _http_ret = HTTPLockedRC(ex.repository, locked_by)
152 if str(_http_ret.code).startswith('2'):
152 if str(_http_ret.code).startswith('2'):
153 #2xx Codes don't raise exceptions
153 #2xx Codes don't raise exceptions
154 sys.stdout.write(_http_ret.title)
154 sys.stdout.write(_http_ret.title)
155 return 0
155 return 0
156
156
157
157
158 def log_push_action(ui, repo, **kwargs):
158 def log_push_action(ui, repo, **kwargs):
159 """
159 """
160 Maps user last push action to new changeset id, from mercurial
160 Maps user last push action to new changeset id, from mercurial
161
161
162 :param ui:
162 :param ui:
163 :param repo: repo object containing the `ui` object
163 :param repo: repo object containing the `ui` object
164 """
164 """
165
165
166 ex = _extract_extras()
166 ex = _extract_extras()
167
167
168 action = ex.action + ':%s'
168 action = ex.action + ':%s'
169
169
170 if ex.scm == 'hg':
170 if ex.scm == 'hg':
171 node = kwargs['node']
171 node = kwargs['node']
172
172
173 def get_revs(repo, rev_opt):
173 def get_revs(repo, rev_opt):
174 if rev_opt:
174 if rev_opt:
175 revs = revrange(repo, rev_opt)
175 revs = revrange(repo, rev_opt)
176
176
177 if len(revs) == 0:
177 if len(revs) == 0:
178 return (nullrev, nullrev)
178 return (nullrev, nullrev)
179 return (max(revs), min(revs))
179 return (max(revs), min(revs))
180 else:
180 else:
181 return (len(repo) - 1, 0)
181 return (len(repo) - 1, 0)
182
182
183 stop, start = get_revs(repo, [node + ':'])
183 stop, start = get_revs(repo, [node + ':'])
184 h = binascii.hexlify
184 h = binascii.hexlify
185 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
185 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
186 elif ex.scm == 'git':
186 elif ex.scm == 'git':
187 revs = kwargs.get('_git_revs', [])
187 revs = kwargs.get('_git_revs', [])
188 if '_git_revs' in kwargs:
188 if '_git_revs' in kwargs:
189 kwargs.pop('_git_revs')
189 kwargs.pop('_git_revs')
190
190
191 action = action % ','.join(revs)
191 action = action % ','.join(revs)
192
192
193 action_logger(ex.username, action, ex.repository, ex.ip, commit=True)
193 action_logger(ex.username, action, ex.repository, ex.ip, commit=True)
194
194
195 # extension hook call
195 # extension hook call
196 from rhodecode import EXTENSIONS
196 from rhodecode import EXTENSIONS
197 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
197 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
198 if isfunction(callback):
198 if isfunction(callback):
199 kw = {'pushed_revs': revs}
199 kw = {'pushed_revs': revs}
200 kw.update(ex)
200 kw.update(ex)
201 callback(**kw)
201 callback(**kw)
202
202
203 if ex.make_lock is not None and not ex.make_lock:
203 if ex.make_lock is not None and not ex.make_lock:
204 Repository.unlock(Repository.get_by_repo_name(ex.repository))
204 Repository.unlock(Repository.get_by_repo_name(ex.repository))
205 msg = 'Released lock on repo `%s`\n' % ex.repository
205 msg = 'Released lock on repo `%s`\n' % ex.repository
206 sys.stdout.write(msg)
206 sys.stdout.write(msg)
207
207
208 if ex.locked_by[0]:
208 if ex.locked_by[0]:
209 locked_by = User.get(ex.locked_by[0]).username
209 locked_by = User.get(ex.locked_by[0]).username
210 _http_ret = HTTPLockedRC(ex.repository, locked_by)
210 _http_ret = HTTPLockedRC(ex.repository, locked_by)
211 if str(_http_ret.code).startswith('2'):
211 if str(_http_ret.code).startswith('2'):
212 #2xx Codes don't raise exceptions
212 #2xx Codes don't raise exceptions
213 sys.stdout.write(_http_ret.title)
213 sys.stdout.write(_http_ret.title)
214
214
215 return 0
215 return 0
216
216
217
217
218 def log_create_repository(repository_dict, created_by, **kwargs):
218 def log_create_repository(repository_dict, created_by, **kwargs):
219 """
219 """
220 Post create repository Hook. This is a dummy function for admins to re-use
220 Post create repository Hook. This is a dummy function for admins to re-use
221 if needed. It's taken from rhodecode-extensions module and executed
221 if needed. It's taken from rhodecode-extensions module and executed
222 if present
222 if present
223
223
224 :param repository: dict dump of repository object
224 :param repository: dict dump of repository object
225 :param created_by: username who created repository
225 :param created_by: username who created repository
226
226
227 available keys of repository_dict:
227 available keys of repository_dict:
228
228
229 'repo_type',
229 'repo_type',
230 'description',
230 'description',
231 'private',
231 'private',
232 'created_on',
232 'created_on',
233 'enable_downloads',
233 'enable_downloads',
234 'repo_id',
234 'repo_id',
235 'user_id',
235 'user_id',
236 'enable_statistics',
236 'enable_statistics',
237 'clone_uri',
237 'clone_uri',
238 'fork_id',
238 'fork_id',
239 'group_id',
239 'group_id',
240 'repo_name'
240 'repo_name'
241
241
242 """
242 """
243 from rhodecode import EXTENSIONS
243 from rhodecode import EXTENSIONS
244 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
244 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
245 if isfunction(callback):
245 if isfunction(callback):
246 kw = {}
246 kw = {}
247 kw.update(repository_dict)
247 kw.update(repository_dict)
248 kw.update({'created_by': created_by})
248 kw.update({'created_by': created_by})
249 kw.update(kwargs)
249 kw.update(kwargs)
250 return callback(**kw)
250 return callback(**kw)
251
251
252 return 0
252 return 0
253
253
254
254
255 def check_allowed_create_user(user_dict, created_by, **kwargs):
256 from rhodecode import EXTENSIONS
257 callback = getattr(EXTENSIONS, 'PRE_CREATE_USER_HOOK', None)
258 if isfunction(callback):
259 allowed, reason = callback(created_by=created_by, **user_dict)
260 if not allowed:
261 raise UserCreationError(reason)
262
263
255 def log_create_user(user_dict, created_by, **kwargs):
264 def log_create_user(user_dict, created_by, **kwargs):
256 """
265 """
257 Post create user Hook. This is a dummy function for admins to re-use
266 Post create user Hook. This is a dummy function for admins to re-use
258 if needed. It's taken from rhodecode-extensions module and executed
267 if needed. It's taken from rhodecode-extensions module and executed
259 if present
268 if present
260
269
261 :param user_dict: dict dump of user object
270 :param user_dict: dict dump of user object
262
271
263 available keys for user_dict:
272 available keys for user_dict:
264
273
265 'username',
274 'username',
266 'full_name_or_username',
275 'full_name_or_username',
267 'full_contact',
276 'full_contact',
268 'user_id',
277 'user_id',
269 'name',
278 'name',
270 'firstname',
279 'firstname',
271 'short_contact',
280 'short_contact',
272 'admin',
281 'admin',
273 'lastname',
282 'lastname',
274 'ip_addresses',
283 'ip_addresses',
275 'ldap_dn',
284 'ldap_dn',
276 'email',
285 'email',
277 'api_key',
286 'api_key',
278 'last_login',
287 'last_login',
279 'full_name',
288 'full_name',
280 'active',
289 'active',
281 'password',
290 'password',
282 'emails',
291 'emails',
283 'inherit_default_permissions'
292 'inherit_default_permissions'
284
293
285 """
294 """
286 from rhodecode import EXTENSIONS
295 from rhodecode import EXTENSIONS
287 callback = getattr(EXTENSIONS, 'CREATE_USER_HOOK', None)
296 callback = getattr(EXTENSIONS, 'CREATE_USER_HOOK', None)
288 if isfunction(callback):
297 if isfunction(callback):
289 return callback(created_by=created_by, **user_dict)
298 return callback(created_by=created_by, **user_dict)
290
299
291 return 0
300 return 0
292
301
293
302
294 def log_delete_repository(repository_dict, deleted_by, **kwargs):
303 def log_delete_repository(repository_dict, deleted_by, **kwargs):
295 """
304 """
296 Post delete repository Hook. This is a dummy function for admins to re-use
305 Post delete repository Hook. This is a dummy function for admins to re-use
297 if needed. It's taken from rhodecode-extensions module and executed
306 if needed. It's taken from rhodecode-extensions module and executed
298 if present
307 if present
299
308
300 :param repository: dict dump of repository object
309 :param repository: dict dump of repository object
301 :param deleted_by: username who deleted the repository
310 :param deleted_by: username who deleted the repository
302
311
303 available keys of repository_dict:
312 available keys of repository_dict:
304
313
305 'repo_type',
314 'repo_type',
306 'description',
315 'description',
307 'private',
316 'private',
308 'created_on',
317 'created_on',
309 'enable_downloads',
318 'enable_downloads',
310 'repo_id',
319 'repo_id',
311 'user_id',
320 'user_id',
312 'enable_statistics',
321 'enable_statistics',
313 'clone_uri',
322 'clone_uri',
314 'fork_id',
323 'fork_id',
315 'group_id',
324 'group_id',
316 'repo_name'
325 'repo_name'
317
326
318 """
327 """
319 from rhodecode import EXTENSIONS
328 from rhodecode import EXTENSIONS
320 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
329 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
321 if isfunction(callback):
330 if isfunction(callback):
322 kw = {}
331 kw = {}
323 kw.update(repository_dict)
332 kw.update(repository_dict)
324 kw.update({'deleted_by': deleted_by,
333 kw.update({'deleted_by': deleted_by,
325 'deleted_on': time.time()})
334 'deleted_on': time.time()})
326 kw.update(kwargs)
335 kw.update(kwargs)
327 return callback(**kw)
336 return callback(**kw)
328
337
329 return 0
338 return 0
330
339
331
340
332 def log_delete_user(user_dict, deleted_by, **kwargs):
341 def log_delete_user(user_dict, deleted_by, **kwargs):
333 """
342 """
334 Post delete user Hook. This is a dummy function for admins to re-use
343 Post delete user Hook. This is a dummy function for admins to re-use
335 if needed. It's taken from rhodecode-extensions module and executed
344 if needed. It's taken from rhodecode-extensions module and executed
336 if present
345 if present
337
346
338 :param user_dict: dict dump of user object
347 :param user_dict: dict dump of user object
339
348
340 available keys for user_dict:
349 available keys for user_dict:
341
350
342 'username',
351 'username',
343 'full_name_or_username',
352 'full_name_or_username',
344 'full_contact',
353 'full_contact',
345 'user_id',
354 'user_id',
346 'name',
355 'name',
347 'firstname',
356 'firstname',
348 'short_contact',
357 'short_contact',
349 'admin',
358 'admin',
350 'lastname',
359 'lastname',
351 'ip_addresses',
360 'ip_addresses',
352 'ldap_dn',
361 'ldap_dn',
353 'email',
362 'email',
354 'api_key',
363 'api_key',
355 'last_login',
364 'last_login',
356 'full_name',
365 'full_name',
357 'active',
366 'active',
358 'password',
367 'password',
359 'emails',
368 'emails',
360 'inherit_default_permissions'
369 'inherit_default_permissions'
361
370
362 """
371 """
363 from rhodecode import EXTENSIONS
372 from rhodecode import EXTENSIONS
364 callback = getattr(EXTENSIONS, 'DELETE_USER_HOOK', None)
373 callback = getattr(EXTENSIONS, 'DELETE_USER_HOOK', None)
365 if isfunction(callback):
374 if isfunction(callback):
366 return callback(deleted_by=deleted_by, **user_dict)
375 return callback(deleted_by=deleted_by, **user_dict)
367
376
368 return 0
377 return 0
369
378
370
379
371 handle_git_pre_receive = (lambda repo_path, revs, env:
380 handle_git_pre_receive = (lambda repo_path, revs, env:
372 handle_git_receive(repo_path, revs, env, hook_type='pre'))
381 handle_git_receive(repo_path, revs, env, hook_type='pre'))
373 handle_git_post_receive = (lambda repo_path, revs, env:
382 handle_git_post_receive = (lambda repo_path, revs, env:
374 handle_git_receive(repo_path, revs, env, hook_type='post'))
383 handle_git_receive(repo_path, revs, env, hook_type='post'))
375
384
376
385
377 def handle_git_receive(repo_path, revs, env, hook_type='post'):
386 def handle_git_receive(repo_path, revs, env, hook_type='post'):
378 """
387 """
379 A really hacky method that is runned by git post-receive hook and logs
388 A really hacky method that is runned by git post-receive hook and logs
380 an push action together with pushed revisions. It's executed by subprocess
389 an push action together with pushed revisions. It's executed by subprocess
381 thus needs all info to be able to create a on the fly pylons enviroment,
390 thus needs all info to be able to create a on the fly pylons enviroment,
382 connect to database and run the logging code. Hacky as sh*t but works.
391 connect to database and run the logging code. Hacky as sh*t but works.
383
392
384 :param repo_path:
393 :param repo_path:
385 :param revs:
394 :param revs:
386 :param env:
395 :param env:
387 """
396 """
388 from paste.deploy import appconfig
397 from paste.deploy import appconfig
389 from sqlalchemy import engine_from_config
398 from sqlalchemy import engine_from_config
390 from rhodecode.config.environment import load_environment
399 from rhodecode.config.environment import load_environment
391 from rhodecode.model import init_model
400 from rhodecode.model import init_model
392 from rhodecode.model.db import RhodeCodeUi
401 from rhodecode.model.db import RhodeCodeUi
393 from rhodecode.lib.utils import make_ui
402 from rhodecode.lib.utils import make_ui
394 extras = _extract_extras(env)
403 extras = _extract_extras(env)
395
404
396 path, ini_name = os.path.split(extras['config'])
405 path, ini_name = os.path.split(extras['config'])
397 conf = appconfig('config:%s' % ini_name, relative_to=path)
406 conf = appconfig('config:%s' % ini_name, relative_to=path)
398 load_environment(conf.global_conf, conf.local_conf)
407 load_environment(conf.global_conf, conf.local_conf)
399
408
400 engine = engine_from_config(conf, 'sqlalchemy.db1.')
409 engine = engine_from_config(conf, 'sqlalchemy.db1.')
401 init_model(engine)
410 init_model(engine)
402
411
403 baseui = make_ui('db')
412 baseui = make_ui('db')
404 # fix if it's not a bare repo
413 # fix if it's not a bare repo
405 if repo_path.endswith(os.sep + '.git'):
414 if repo_path.endswith(os.sep + '.git'):
406 repo_path = repo_path[:-5]
415 repo_path = repo_path[:-5]
407
416
408 repo = Repository.get_by_full_path(repo_path)
417 repo = Repository.get_by_full_path(repo_path)
409 if not repo:
418 if not repo:
410 raise OSError('Repository %s not found in database'
419 raise OSError('Repository %s not found in database'
411 % (safe_str(repo_path)))
420 % (safe_str(repo_path)))
412
421
413 _hooks = dict(baseui.configitems('hooks')) or {}
422 _hooks = dict(baseui.configitems('hooks')) or {}
414
423
415 if hook_type == 'pre':
424 if hook_type == 'pre':
416 repo = repo.scm_instance
425 repo = repo.scm_instance
417 else:
426 else:
418 #post push shouldn't use the cached instance never
427 #post push shouldn't use the cached instance never
419 repo = repo.scm_instance_no_cache()
428 repo = repo.scm_instance_no_cache()
420
429
421 if hook_type == 'pre':
430 if hook_type == 'pre':
422 pre_push(baseui, repo)
431 pre_push(baseui, repo)
423
432
424 # if push hook is enabled via web interface
433 # if push hook is enabled via web interface
425 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
434 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
426
435
427 rev_data = []
436 rev_data = []
428 for l in revs:
437 for l in revs:
429 old_rev, new_rev, ref = l.split(' ')
438 old_rev, new_rev, ref = l.split(' ')
430 _ref_data = ref.split('/')
439 _ref_data = ref.split('/')
431 if _ref_data[1] in ['tags', 'heads']:
440 if _ref_data[1] in ['tags', 'heads']:
432 rev_data.append({'old_rev': old_rev,
441 rev_data.append({'old_rev': old_rev,
433 'new_rev': new_rev,
442 'new_rev': new_rev,
434 'ref': ref,
443 'ref': ref,
435 'type': _ref_data[1],
444 'type': _ref_data[1],
436 'name': _ref_data[2].strip()})
445 'name': _ref_data[2].strip()})
437
446
438 git_revs = []
447 git_revs = []
439 for push_ref in rev_data:
448 for push_ref in rev_data:
440 _type = push_ref['type']
449 _type = push_ref['type']
441 if _type == 'heads':
450 if _type == 'heads':
442 if push_ref['old_rev'] == EmptyChangeset().raw_id:
451 if push_ref['old_rev'] == EmptyChangeset().raw_id:
443 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
452 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
444 heads = repo.run_git_command(cmd)[0]
453 heads = repo.run_git_command(cmd)[0]
445 heads = heads.replace(push_ref['ref'], '')
454 heads = heads.replace(push_ref['ref'], '')
446 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
455 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
447 heads.splitlines()))
456 heads.splitlines()))
448 cmd = (('log %(new_rev)s' % push_ref) +
457 cmd = (('log %(new_rev)s' % push_ref) +
449 ' --reverse --pretty=format:"%H" --not ' + heads)
458 ' --reverse --pretty=format:"%H" --not ' + heads)
450 git_revs += repo.run_git_command(cmd)[0].splitlines()
459 git_revs += repo.run_git_command(cmd)[0].splitlines()
451
460
452 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
461 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
453 #delete branch case
462 #delete branch case
454 git_revs += ['delete_branch=>%s' % push_ref['name']]
463 git_revs += ['delete_branch=>%s' % push_ref['name']]
455 else:
464 else:
456 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
465 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
457 ' --reverse --pretty=format:"%H"')
466 ' --reverse --pretty=format:"%H"')
458 git_revs += repo.run_git_command(cmd)[0].splitlines()
467 git_revs += repo.run_git_command(cmd)[0].splitlines()
459
468
460 elif _type == 'tags':
469 elif _type == 'tags':
461 git_revs += ['tag=>%s' % push_ref['name']]
470 git_revs += ['tag=>%s' % push_ref['name']]
462
471
463 log_push_action(baseui, repo, _git_revs=git_revs)
472 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,831 +1,869 b''
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 import itertools
28 import itertools
29 import collections
29 import collections
30 from pylons import url
30 from pylons import url
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
35
35
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key, get_current_rhodecode_user
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key, get_current_rhodecode_user
37 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
39 from rhodecode.model.db import User, Repository, Permission, \
40 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
40 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
41 Notification, RepoGroup, UserRepoGroupToPerm, UserGroupRepoGroupToPerm, \
41 Notification, RepoGroup, UserGroupRepoGroupToPerm, \
42 UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
42 UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
43 from rhodecode.lib.exceptions import DefaultUserException, \
43 from rhodecode.lib.exceptions import DefaultUserException, \
44 UserOwnsReposException
44 UserOwnsReposException
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46
46
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
51
51
52
52
53 class UserModel(BaseModel):
53 class UserModel(BaseModel):
54 cls = User
54 cls = User
55
55
56 def get(self, user_id, cache=False):
56 def get(self, user_id, cache=False):
57 user = self.sa.query(User)
57 user = self.sa.query(User)
58 if cache:
58 if cache:
59 user = user.options(FromCache("sql_cache_short",
59 user = user.options(FromCache("sql_cache_short",
60 "get_user_%s" % user_id))
60 "get_user_%s" % user_id))
61 return user.get(user_id)
61 return user.get(user_id)
62
62
63 def get_user(self, user):
63 def get_user(self, user):
64 return self._get_user(user)
64 return self._get_user(user)
65
65
66 def get_by_username(self, username, cache=False, case_insensitive=False):
66 def get_by_username(self, username, cache=False, case_insensitive=False):
67
67
68 if case_insensitive:
68 if case_insensitive:
69 user = self.sa.query(User).filter(User.username.ilike(username))
69 user = self.sa.query(User).filter(User.username.ilike(username))
70 else:
70 else:
71 user = self.sa.query(User).filter(User.username == username)
71 user = self.sa.query(User).filter(User.username == username)
72 if cache:
72 if cache:
73 user = user.options(FromCache("sql_cache_short",
73 user = user.options(FromCache("sql_cache_short",
74 "get_user_%s" % username))
74 "get_user_%s" % username))
75 return user.scalar()
75 return user.scalar()
76
76
77 def get_by_email(self, email, cache=False, case_insensitive=False):
77 def get_by_email(self, email, cache=False, case_insensitive=False):
78 return User.get_by_email(email, case_insensitive, cache)
78 return User.get_by_email(email, case_insensitive, cache)
79
79
80 def get_by_api_key(self, api_key, cache=False):
80 def get_by_api_key(self, api_key, cache=False):
81 return User.get_by_api_key(api_key, cache)
81 return User.get_by_api_key(api_key, cache)
82
82
83 def create(self, form_data, cur_user=None):
83 def create(self, form_data, cur_user=None):
84 if not cur_user:
84 if not cur_user:
85 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
85 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
86
87 from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
88 _fd = form_data
89 form_data = {
90 'username': _fd['username'], 'password': _fd['password'],
91 'email': _fd['email'], 'firstname': _fd['firstname'], 'lastname': _fd['lastname'],
92 'active': _fd['active'], 'admin': False
93 }
94 # raises UserCreationError if it's not allowed
95 check_allowed_create_user(form_data, cur_user)
96
86 from rhodecode.lib.auth import get_crypt_password
97 from rhodecode.lib.auth import get_crypt_password
87 try:
98 try:
88 new_user = User()
99 new_user = User()
89 for k, v in form_data.items():
100 for k, v in form_data.items():
90 if k == 'password':
101 if k == 'password':
91 v = get_crypt_password(v)
102 v = get_crypt_password(v)
92 if k == 'firstname':
103 if k == 'firstname':
93 k = 'name'
104 k = 'name'
94 setattr(new_user, k, v)
105 setattr(new_user, k, v)
95
106
96 new_user.api_key = generate_api_key(form_data['username'])
107 new_user.api_key = generate_api_key(form_data['username'])
97 self.sa.add(new_user)
108 self.sa.add(new_user)
98
109
99 from rhodecode.lib.hooks import log_create_user
100 log_create_user(new_user.get_dict(), cur_user)
110 log_create_user(new_user.get_dict(), cur_user)
101 return new_user
111 return new_user
102 except Exception:
112 except Exception:
103 log.error(traceback.format_exc())
113 log.error(traceback.format_exc())
104 raise
114 raise
105
115
106 def create_or_update(self, username, password, email, firstname='',
116 def create_or_update(self, username, password, email, firstname='',
107 lastname='', active=True, admin=False, ldap_dn=None,
117 lastname='', active=True, admin=False, ldap_dn=None,
108 cur_user=None):
118 cur_user=None):
109 """
119 """
110 Creates a new instance if not found, or updates current one
120 Creates a new instance if not found, or updates current one
111
121
112 :param username:
122 :param username:
113 :param password:
123 :param password:
114 :param email:
124 :param email:
115 :param active:
125 :param active:
116 :param firstname:
126 :param firstname:
117 :param lastname:
127 :param lastname:
118 :param active:
128 :param active:
119 :param admin:
129 :param admin:
120 :param ldap_dn:
130 :param ldap_dn:
121 :param cur_user:
131 :param cur_user:
122 """
132 """
123 if not cur_user:
133 if not cur_user:
124 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
134 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
125
135
126 from rhodecode.lib.auth import get_crypt_password
136 from rhodecode.lib.auth import get_crypt_password
137 from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
138 form_data = {
139 'username': username, 'password': password,
140 'email': email, 'firstname': firstname, 'lastname': lastname,
141 'active': active, 'admin': admin
142 }
143 # raises UserCreationError if it's not allowed
144 check_allowed_create_user(form_data, cur_user)
127
145
128 log.debug('Checking for %s account in RhodeCode database' % username)
146 log.debug('Checking for %s account in RhodeCode database' % username)
129 user = User.get_by_username(username, case_insensitive=True)
147 user = User.get_by_username(username, case_insensitive=True)
130 if user is None:
148 if user is None:
131 log.debug('creating new user %s' % username)
149 log.debug('creating new user %s' % username)
132 new_user = User()
150 new_user = User()
133 edit = False
151 edit = False
134 else:
152 else:
135 log.debug('updating user %s' % username)
153 log.debug('updating user %s' % username)
136 new_user = user
154 new_user = user
137 edit = True
155 edit = True
138
156
139 try:
157 try:
140 new_user.username = username
158 new_user.username = username
141 new_user.admin = admin
159 new_user.admin = admin
142 # set password only if creating an user or password is changed
160 # set password only if creating an user or password is changed
143 if not edit or user.password != password:
161 if not edit or user.password != password:
144 new_user.password = get_crypt_password(password) if password else None
162 new_user.password = get_crypt_password(password) if password else None
145 new_user.api_key = generate_api_key(username)
163 new_user.api_key = generate_api_key(username)
146 new_user.email = email
164 new_user.email = email
147 new_user.active = active
165 new_user.active = active
148 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
166 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
149 new_user.name = firstname
167 new_user.name = firstname
150 new_user.lastname = lastname
168 new_user.lastname = lastname
151 self.sa.add(new_user)
169 self.sa.add(new_user)
152
170
153 if not edit:
171 if not edit:
154 from rhodecode.lib.hooks import log_create_user
155 log_create_user(new_user.get_dict(), cur_user)
172 log_create_user(new_user.get_dict(), cur_user)
156 return new_user
173 return new_user
157 except (DatabaseError,):
174 except (DatabaseError,):
158 log.error(traceback.format_exc())
175 log.error(traceback.format_exc())
159 raise
176 raise
160
177
161 def create_for_container_auth(self, username, attrs, cur_user=None):
178 def create_for_container_auth(self, username, attrs, cur_user=None):
162 """
179 """
163 Creates the given user if it's not already in the database
180 Creates the given user if it's not already in the database
164
181
165 :param username:
182 :param username:
166 :param attrs:
183 :param attrs:
167 :param cur_user:
184 :param cur_user:
168 """
185 """
169 if not cur_user:
186 if not cur_user:
170 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
187 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
171 if self.get_by_username(username, case_insensitive=True) is None:
188 if self.get_by_username(username, case_insensitive=True) is None:
172
173 # autogenerate email for container account without one
189 # autogenerate email for container account without one
174 generate_email = lambda usr: '%s@container_auth.account' % usr
190 generate_email = lambda usr: '%s@container_auth.account' % usr
191 firstname = attrs['name']
192 lastname = attrs['lastname']
193 active = attrs.get('active', True)
194 email = attrs['email'] or generate_email(username)
195
196 from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
197 form_data = {
198 'username': username, 'password': None,
199 'email': email, 'firstname': firstname, 'lastname': lastname,
200 'active': attrs.get('active', True), 'admin': False
201 }
202 # raises UserCreationError if it's not allowed
203 check_allowed_create_user(form_data, cur_user)
175
204
176 try:
205 try:
177 new_user = User()
206 new_user = User()
178 new_user.username = username
207 new_user.username = username
179 new_user.password = None
208 new_user.password = None
180 new_user.api_key = generate_api_key(username)
209 new_user.api_key = generate_api_key(username)
181 new_user.email = attrs['email']
210 new_user.email = email
182 new_user.active = attrs.get('active', True)
211 new_user.active = active
183 new_user.name = attrs['name'] or generate_email(username)
212 new_user.name = firstname
184 new_user.lastname = attrs['lastname']
213 new_user.lastname = lastname
185
214
186 self.sa.add(new_user)
215 self.sa.add(new_user)
187
188 from rhodecode.lib.hooks import log_create_user
189 log_create_user(new_user.get_dict(), cur_user)
216 log_create_user(new_user.get_dict(), cur_user)
190 return new_user
217 return new_user
191 except (DatabaseError,):
218 except (DatabaseError,):
192 log.error(traceback.format_exc())
219 log.error(traceback.format_exc())
193 self.sa.rollback()
220 self.sa.rollback()
194 raise
221 raise
195 log.debug('User %s already exists. Skipping creation of account'
222 log.debug('User %s already exists. Skipping creation of account'
196 ' for container auth.', username)
223 ' for container auth.', username)
197 return None
224 return None
198
225
199 def create_ldap(self, username, password, user_dn, attrs, cur_user=None):
226 def create_ldap(self, username, password, user_dn, attrs, cur_user=None):
200 """
227 """
201 Checks if user is in database, if not creates this user marked
228 Checks if user is in database, if not creates this user marked
202 as ldap user
229 as ldap user
203
230
204 :param username:
231 :param username:
205 :param password:
232 :param password:
206 :param user_dn:
233 :param user_dn:
207 :param attrs:
234 :param attrs:
208 :param cur_user:
235 :param cur_user:
209 """
236 """
210 if not cur_user:
237 if not cur_user:
211 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
238 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
212 from rhodecode.lib.auth import get_crypt_password
239 from rhodecode.lib.auth import get_crypt_password
213 log.debug('Checking for such ldap account in RhodeCode database')
240 log.debug('Checking for such ldap account in RhodeCode database')
214 if self.get_by_username(username, case_insensitive=True) is None:
241 if self.get_by_username(username, case_insensitive=True) is None:
242 # autogenerate email for container account without one
243 generate_email = lambda usr: '%s@ldap.account' % usr
244 password = get_crypt_password(password)
245 firstname = attrs['name']
246 lastname = attrs['lastname']
247 active = attrs.get('active', True)
248 email = attrs['email'] or generate_email(username)
215
249
216 # autogenerate email for ldap account without one
250 from rhodecode.lib.hooks import log_create_user, check_allowed_create_user
217 generate_email = lambda usr: '%s@ldap.account' % usr
251 form_data = {
252 'username': username, 'password': password,
253 'email': email, 'firstname': firstname, 'lastname': lastname,
254 'active': attrs.get('active', True), 'admin': False
255 }
256 # raises UserCreationError if it's not allowed
257 check_allowed_create_user(form_data, cur_user)
218
258
219 try:
259 try:
220 new_user = User()
260 new_user = User()
221 username = username.lower()
261 username = username.lower()
222 # add ldap account always lowercase
262 # add ldap account always lowercase
223 new_user.username = username
263 new_user.username = username
224 new_user.password = get_crypt_password(password)
264 new_user.password = password
225 new_user.api_key = generate_api_key(username)
265 new_user.api_key = generate_api_key(username)
226 new_user.email = attrs['email'] or generate_email(username)
266 new_user.email = email
227 new_user.active = attrs.get('active', True)
267 new_user.active = active
228 new_user.ldap_dn = safe_unicode(user_dn)
268 new_user.ldap_dn = safe_unicode(user_dn)
229 new_user.name = attrs['name']
269 new_user.name = firstname
230 new_user.lastname = attrs['lastname']
270 new_user.lastname = lastname
231
232 self.sa.add(new_user)
271 self.sa.add(new_user)
233
272
234 from rhodecode.lib.hooks import log_create_user
235 log_create_user(new_user.get_dict(), cur_user)
273 log_create_user(new_user.get_dict(), cur_user)
236 return new_user
274 return new_user
237 except (DatabaseError,):
275 except (DatabaseError,):
238 log.error(traceback.format_exc())
276 log.error(traceback.format_exc())
239 self.sa.rollback()
277 self.sa.rollback()
240 raise
278 raise
241 log.debug('this %s user exists skipping creation of ldap account',
279 log.debug('this %s user exists skipping creation of ldap account',
242 username)
280 username)
243 return None
281 return None
244
282
245 def create_registration(self, form_data):
283 def create_registration(self, form_data):
246 from rhodecode.model.notification import NotificationModel
284 from rhodecode.model.notification import NotificationModel
247
285
248 try:
286 try:
249 form_data['admin'] = False
287 form_data['admin'] = False
250 new_user = self.create(form_data)
288 new_user = self.create(form_data)
251
289
252 self.sa.add(new_user)
290 self.sa.add(new_user)
253 self.sa.flush()
291 self.sa.flush()
254
292
255 # notification to admins
293 # notification to admins
256 subject = _('New user registration')
294 subject = _('New user registration')
257 body = ('New user registration\n'
295 body = ('New user registration\n'
258 '---------------------\n'
296 '---------------------\n'
259 '- Username: %s\n'
297 '- Username: %s\n'
260 '- Full Name: %s\n'
298 '- Full Name: %s\n'
261 '- Email: %s\n')
299 '- Email: %s\n')
262 body = body % (new_user.username, new_user.full_name, new_user.email)
300 body = body % (new_user.username, new_user.full_name, new_user.email)
263 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
301 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
264 kw = {'registered_user_url': edit_url}
302 kw = {'registered_user_url': edit_url}
265 NotificationModel().create(created_by=new_user, subject=subject,
303 NotificationModel().create(created_by=new_user, subject=subject,
266 body=body, recipients=None,
304 body=body, recipients=None,
267 type_=Notification.TYPE_REGISTRATION,
305 type_=Notification.TYPE_REGISTRATION,
268 email_kwargs=kw)
306 email_kwargs=kw)
269
307
270 except Exception:
308 except Exception:
271 log.error(traceback.format_exc())
309 log.error(traceback.format_exc())
272 raise
310 raise
273
311
274 def update(self, user_id, form_data, skip_attrs=[]):
312 def update(self, user_id, form_data, skip_attrs=[]):
275 from rhodecode.lib.auth import get_crypt_password
313 from rhodecode.lib.auth import get_crypt_password
276 try:
314 try:
277 user = self.get(user_id, cache=False)
315 user = self.get(user_id, cache=False)
278 if user.username == 'default':
316 if user.username == 'default':
279 raise DefaultUserException(
317 raise DefaultUserException(
280 _("You can't Edit this user since it's"
318 _("You can't Edit this user since it's"
281 " crucial for entire application"))
319 " crucial for entire application"))
282
320
283 for k, v in form_data.items():
321 for k, v in form_data.items():
284 if k in skip_attrs:
322 if k in skip_attrs:
285 continue
323 continue
286 if k == 'new_password' and v:
324 if k == 'new_password' and v:
287 user.password = get_crypt_password(v)
325 user.password = get_crypt_password(v)
288 user.api_key = generate_api_key(user.username)
326 user.api_key = generate_api_key(user.username)
289 else:
327 else:
290 if k == 'firstname':
328 if k == 'firstname':
291 k = 'name'
329 k = 'name'
292 setattr(user, k, v)
330 setattr(user, k, v)
293 self.sa.add(user)
331 self.sa.add(user)
294 except Exception:
332 except Exception:
295 log.error(traceback.format_exc())
333 log.error(traceback.format_exc())
296 raise
334 raise
297
335
298 def update_user(self, user, **kwargs):
336 def update_user(self, user, **kwargs):
299 from rhodecode.lib.auth import get_crypt_password
337 from rhodecode.lib.auth import get_crypt_password
300 try:
338 try:
301 user = self._get_user(user)
339 user = self._get_user(user)
302 if user.username == 'default':
340 if user.username == 'default':
303 raise DefaultUserException(
341 raise DefaultUserException(
304 _("You can't Edit this user since it's"
342 _("You can't Edit this user since it's"
305 " crucial for entire application")
343 " crucial for entire application")
306 )
344 )
307
345
308 for k, v in kwargs.items():
346 for k, v in kwargs.items():
309 if k == 'password' and v:
347 if k == 'password' and v:
310 v = get_crypt_password(v)
348 v = get_crypt_password(v)
311 user.api_key = generate_api_key(user.username)
349 user.api_key = generate_api_key(user.username)
312
350
313 setattr(user, k, v)
351 setattr(user, k, v)
314 self.sa.add(user)
352 self.sa.add(user)
315 return user
353 return user
316 except Exception:
354 except Exception:
317 log.error(traceback.format_exc())
355 log.error(traceback.format_exc())
318 raise
356 raise
319
357
320 def delete(self, user, cur_user=None):
358 def delete(self, user, cur_user=None):
321 if not cur_user:
359 if not cur_user:
322 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
360 cur_user = getattr(get_current_rhodecode_user(), 'username', None)
323 user = self._get_user(user)
361 user = self._get_user(user)
324
362
325 try:
363 try:
326 if user.username == 'default':
364 if user.username == 'default':
327 raise DefaultUserException(
365 raise DefaultUserException(
328 _(u"You can't remove this user since it's"
366 _(u"You can't remove this user since it's"
329 " crucial for entire application")
367 " crucial for entire application")
330 )
368 )
331 if user.repositories:
369 if user.repositories:
332 repos = [x.repo_name for x in user.repositories]
370 repos = [x.repo_name for x in user.repositories]
333 raise UserOwnsReposException(
371 raise UserOwnsReposException(
334 _(u'user "%s" still owns %s repositories and cannot be '
372 _(u'user "%s" still owns %s repositories and cannot be '
335 'removed. Switch owners or remove those repositories. %s')
373 'removed. Switch owners or remove those repositories. %s')
336 % (user.username, len(repos), ', '.join(repos))
374 % (user.username, len(repos), ', '.join(repos))
337 )
375 )
338 self.sa.delete(user)
376 self.sa.delete(user)
339
377
340 from rhodecode.lib.hooks import log_delete_user
378 from rhodecode.lib.hooks import log_delete_user
341 log_delete_user(user.get_dict(), cur_user)
379 log_delete_user(user.get_dict(), cur_user)
342 except Exception:
380 except Exception:
343 log.error(traceback.format_exc())
381 log.error(traceback.format_exc())
344 raise
382 raise
345
383
346 def reset_password_link(self, data):
384 def reset_password_link(self, data):
347 from rhodecode.lib.celerylib import tasks, run_task
385 from rhodecode.lib.celerylib import tasks, run_task
348 from rhodecode.model.notification import EmailNotificationModel
386 from rhodecode.model.notification import EmailNotificationModel
349 user_email = data['email']
387 user_email = data['email']
350 try:
388 try:
351 user = User.get_by_email(user_email)
389 user = User.get_by_email(user_email)
352 if user:
390 if user:
353 log.debug('password reset user found %s' % user)
391 log.debug('password reset user found %s' % user)
354 link = url('reset_password_confirmation', key=user.api_key,
392 link = url('reset_password_confirmation', key=user.api_key,
355 qualified=True)
393 qualified=True)
356 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
394 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
357 body = EmailNotificationModel().get_email_tmpl(reg_type,
395 body = EmailNotificationModel().get_email_tmpl(reg_type,
358 **{'user': user.short_contact,
396 **{'user': user.short_contact,
359 'reset_url': link})
397 'reset_url': link})
360 log.debug('sending email')
398 log.debug('sending email')
361 run_task(tasks.send_email, user_email,
399 run_task(tasks.send_email, user_email,
362 _("Password reset link"), body, body)
400 _("Password reset link"), body, body)
363 log.info('send new password mail to %s' % user_email)
401 log.info('send new password mail to %s' % user_email)
364 else:
402 else:
365 log.debug("password reset email %s not found" % user_email)
403 log.debug("password reset email %s not found" % user_email)
366 except Exception:
404 except Exception:
367 log.error(traceback.format_exc())
405 log.error(traceback.format_exc())
368 return False
406 return False
369
407
370 return True
408 return True
371
409
372 def reset_password(self, data):
410 def reset_password(self, data):
373 from rhodecode.lib.celerylib import tasks, run_task
411 from rhodecode.lib.celerylib import tasks, run_task
374 from rhodecode.lib import auth
412 from rhodecode.lib import auth
375 user_email = data['email']
413 user_email = data['email']
376 try:
414 try:
377 try:
415 try:
378 user = User.get_by_email(user_email)
416 user = User.get_by_email(user_email)
379 new_passwd = auth.PasswordGenerator().gen_password(8,
417 new_passwd = auth.PasswordGenerator().gen_password(8,
380 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
418 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
381 if user:
419 if user:
382 user.password = auth.get_crypt_password(new_passwd)
420 user.password = auth.get_crypt_password(new_passwd)
383 user.api_key = auth.generate_api_key(user.username)
421 user.api_key = auth.generate_api_key(user.username)
384 Session().add(user)
422 Session().add(user)
385 Session().commit()
423 Session().commit()
386 log.info('change password for %s' % user_email)
424 log.info('change password for %s' % user_email)
387 if new_passwd is None:
425 if new_passwd is None:
388 raise Exception('unable to generate new password')
426 raise Exception('unable to generate new password')
389 except Exception:
427 except Exception:
390 log.error(traceback.format_exc())
428 log.error(traceback.format_exc())
391 Session().rollback()
429 Session().rollback()
392
430
393 run_task(tasks.send_email, user_email,
431 run_task(tasks.send_email, user_email,
394 _('Your new password'),
432 _('Your new password'),
395 _('Your new RhodeCode password:%s') % (new_passwd,))
433 _('Your new RhodeCode password:%s') % (new_passwd,))
396 log.info('send new password mail to %s' % user_email)
434 log.info('send new password mail to %s' % user_email)
397
435
398 except Exception:
436 except Exception:
399 log.error('Failed to update user password')
437 log.error('Failed to update user password')
400 log.error(traceback.format_exc())
438 log.error(traceback.format_exc())
401
439
402 return True
440 return True
403
441
404 def fill_data(self, auth_user, user_id=None, api_key=None):
442 def fill_data(self, auth_user, user_id=None, api_key=None):
405 """
443 """
406 Fetches auth_user by user_id,or api_key if present.
444 Fetches auth_user by user_id,or api_key if present.
407 Fills auth_user attributes with those taken from database.
445 Fills auth_user attributes with those taken from database.
408 Additionally set's is_authenitated if lookup fails
446 Additionally set's is_authenitated if lookup fails
409 present in database
447 present in database
410
448
411 :param auth_user: instance of user to set attributes
449 :param auth_user: instance of user to set attributes
412 :param user_id: user id to fetch by
450 :param user_id: user id to fetch by
413 :param api_key: api key to fetch by
451 :param api_key: api key to fetch by
414 """
452 """
415 if user_id is None and api_key is None:
453 if user_id is None and api_key is None:
416 raise Exception('You need to pass user_id or api_key')
454 raise Exception('You need to pass user_id or api_key')
417
455
418 try:
456 try:
419 if api_key:
457 if api_key:
420 dbuser = self.get_by_api_key(api_key)
458 dbuser = self.get_by_api_key(api_key)
421 else:
459 else:
422 dbuser = self.get(user_id)
460 dbuser = self.get(user_id)
423
461
424 if dbuser is not None and dbuser.active:
462 if dbuser is not None and dbuser.active:
425 log.debug('filling %s data' % dbuser)
463 log.debug('filling %s data' % dbuser)
426 for k, v in dbuser.get_dict().items():
464 for k, v in dbuser.get_dict().items():
427 setattr(auth_user, k, v)
465 setattr(auth_user, k, v)
428 else:
466 else:
429 return False
467 return False
430
468
431 except Exception:
469 except Exception:
432 log.error(traceback.format_exc())
470 log.error(traceback.format_exc())
433 auth_user.is_authenticated = False
471 auth_user.is_authenticated = False
434 return False
472 return False
435
473
436 return True
474 return True
437
475
438 def fill_perms(self, user, explicit=True, algo='higherwin'):
476 def fill_perms(self, user, explicit=True, algo='higherwin'):
439 """
477 """
440 Fills user permission attribute with permissions taken from database
478 Fills user permission attribute with permissions taken from database
441 works for permissions given for repositories, and for permissions that
479 works for permissions given for repositories, and for permissions that
442 are granted to groups
480 are granted to groups
443
481
444 :param user: user instance to fill his perms
482 :param user: user instance to fill his perms
445 :param explicit: In case there are permissions both for user and a group
483 :param explicit: In case there are permissions both for user and a group
446 that user is part of, explicit flag will defiine if user will
484 that user is part of, explicit flag will defiine if user will
447 explicitly override permissions from group, if it's False it will
485 explicitly override permissions from group, if it's False it will
448 make decision based on the algo
486 make decision based on the algo
449 :param algo: algorithm to decide what permission should be choose if
487 :param algo: algorithm to decide what permission should be choose if
450 it's multiple defined, eg user in two different groups. It also
488 it's multiple defined, eg user in two different groups. It also
451 decides if explicit flag is turned off how to specify the permission
489 decides if explicit flag is turned off how to specify the permission
452 for case when user is in a group + have defined separate permission
490 for case when user is in a group + have defined separate permission
453 """
491 """
454 RK = 'repositories'
492 RK = 'repositories'
455 GK = 'repositories_groups'
493 GK = 'repositories_groups'
456 UK = 'user_groups'
494 UK = 'user_groups'
457 GLOBAL = 'global'
495 GLOBAL = 'global'
458 user.permissions[RK] = {}
496 user.permissions[RK] = {}
459 user.permissions[GK] = {}
497 user.permissions[GK] = {}
460 user.permissions[UK] = {}
498 user.permissions[UK] = {}
461 user.permissions[GLOBAL] = set()
499 user.permissions[GLOBAL] = set()
462
500
463 def _choose_perm(new_perm, cur_perm):
501 def _choose_perm(new_perm, cur_perm):
464 new_perm_val = PERM_WEIGHTS[new_perm]
502 new_perm_val = PERM_WEIGHTS[new_perm]
465 cur_perm_val = PERM_WEIGHTS[cur_perm]
503 cur_perm_val = PERM_WEIGHTS[cur_perm]
466 if algo == 'higherwin':
504 if algo == 'higherwin':
467 if new_perm_val > cur_perm_val:
505 if new_perm_val > cur_perm_val:
468 return new_perm
506 return new_perm
469 return cur_perm
507 return cur_perm
470 elif algo == 'lowerwin':
508 elif algo == 'lowerwin':
471 if new_perm_val < cur_perm_val:
509 if new_perm_val < cur_perm_val:
472 return new_perm
510 return new_perm
473 return cur_perm
511 return cur_perm
474
512
475 #======================================================================
513 #======================================================================
476 # fetch default permissions
514 # fetch default permissions
477 #======================================================================
515 #======================================================================
478 default_user = User.get_by_username('default', cache=True)
516 default_user = User.get_by_username('default', cache=True)
479 default_user_id = default_user.user_id
517 default_user_id = default_user.user_id
480
518
481 default_repo_perms = Permission.get_default_perms(default_user_id)
519 default_repo_perms = Permission.get_default_perms(default_user_id)
482 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
520 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
483 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
521 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
484
522
485 if user.is_admin:
523 if user.is_admin:
486 #==================================================================
524 #==================================================================
487 # admin user have all default rights for repositories
525 # admin user have all default rights for repositories
488 # and groups set to admin
526 # and groups set to admin
489 #==================================================================
527 #==================================================================
490 user.permissions[GLOBAL].add('hg.admin')
528 user.permissions[GLOBAL].add('hg.admin')
491
529
492 # repositories
530 # repositories
493 for perm in default_repo_perms:
531 for perm in default_repo_perms:
494 r_k = perm.UserRepoToPerm.repository.repo_name
532 r_k = perm.UserRepoToPerm.repository.repo_name
495 p = 'repository.admin'
533 p = 'repository.admin'
496 user.permissions[RK][r_k] = p
534 user.permissions[RK][r_k] = p
497
535
498 # repository groups
536 # repository groups
499 for perm in default_repo_groups_perms:
537 for perm in default_repo_groups_perms:
500 rg_k = perm.UserRepoGroupToPerm.group.group_name
538 rg_k = perm.UserRepoGroupToPerm.group.group_name
501 p = 'group.admin'
539 p = 'group.admin'
502 user.permissions[GK][rg_k] = p
540 user.permissions[GK][rg_k] = p
503
541
504 # user groups
542 # user groups
505 for perm in default_user_group_perms:
543 for perm in default_user_group_perms:
506 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
544 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
507 p = 'usergroup.admin'
545 p = 'usergroup.admin'
508 user.permissions[UK][u_k] = p
546 user.permissions[UK][u_k] = p
509 return user
547 return user
510
548
511 #==================================================================
549 #==================================================================
512 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
550 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
513 #==================================================================
551 #==================================================================
514 uid = user.user_id
552 uid = user.user_id
515
553
516 # default global permissions taken fron the default user
554 # default global permissions taken fron the default user
517 default_global_perms = self.sa.query(UserToPerm)\
555 default_global_perms = self.sa.query(UserToPerm)\
518 .filter(UserToPerm.user_id == default_user_id)
556 .filter(UserToPerm.user_id == default_user_id)
519
557
520 for perm in default_global_perms:
558 for perm in default_global_perms:
521 user.permissions[GLOBAL].add(perm.permission.permission_name)
559 user.permissions[GLOBAL].add(perm.permission.permission_name)
522
560
523 # defaults for repositories, taken from default user
561 # defaults for repositories, taken from default user
524 for perm in default_repo_perms:
562 for perm in default_repo_perms:
525 r_k = perm.UserRepoToPerm.repository.repo_name
563 r_k = perm.UserRepoToPerm.repository.repo_name
526 if perm.Repository.private and not (perm.Repository.user_id == uid):
564 if perm.Repository.private and not (perm.Repository.user_id == uid):
527 # disable defaults for private repos,
565 # disable defaults for private repos,
528 p = 'repository.none'
566 p = 'repository.none'
529 elif perm.Repository.user_id == uid:
567 elif perm.Repository.user_id == uid:
530 # set admin if owner
568 # set admin if owner
531 p = 'repository.admin'
569 p = 'repository.admin'
532 else:
570 else:
533 p = perm.Permission.permission_name
571 p = perm.Permission.permission_name
534
572
535 user.permissions[RK][r_k] = p
573 user.permissions[RK][r_k] = p
536
574
537 # defaults for repository groups taken from default user permission
575 # defaults for repository groups taken from default user permission
538 # on given group
576 # on given group
539 for perm in default_repo_groups_perms:
577 for perm in default_repo_groups_perms:
540 rg_k = perm.UserRepoGroupToPerm.group.group_name
578 rg_k = perm.UserRepoGroupToPerm.group.group_name
541 p = perm.Permission.permission_name
579 p = perm.Permission.permission_name
542 user.permissions[GK][rg_k] = p
580 user.permissions[GK][rg_k] = p
543
581
544 # defaults for user groups taken from default user permission
582 # defaults for user groups taken from default user permission
545 # on given user group
583 # on given user group
546 for perm in default_user_group_perms:
584 for perm in default_user_group_perms:
547 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
585 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
548 p = perm.Permission.permission_name
586 p = perm.Permission.permission_name
549 user.permissions[UK][u_k] = p
587 user.permissions[UK][u_k] = p
550
588
551 #======================================================================
589 #======================================================================
552 # !! OVERRIDE GLOBALS !! with user permissions if any found
590 # !! OVERRIDE GLOBALS !! with user permissions if any found
553 #======================================================================
591 #======================================================================
554 # those can be configured from groups or users explicitly
592 # those can be configured from groups or users explicitly
555 _configurable = set([
593 _configurable = set([
556 'hg.fork.none', 'hg.fork.repository',
594 'hg.fork.none', 'hg.fork.repository',
557 'hg.create.none', 'hg.create.repository',
595 'hg.create.none', 'hg.create.repository',
558 'hg.usergroup.create.false', 'hg.usergroup.create.true'
596 'hg.usergroup.create.false', 'hg.usergroup.create.true'
559 ])
597 ])
560
598
561 # USER GROUPS comes first
599 # USER GROUPS comes first
562 # user group global permissions
600 # user group global permissions
563 user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
601 user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
564 .options(joinedload(UserGroupToPerm.permission))\
602 .options(joinedload(UserGroupToPerm.permission))\
565 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
603 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
566 UserGroupMember.users_group_id))\
604 UserGroupMember.users_group_id))\
567 .filter(UserGroupMember.user_id == uid)\
605 .filter(UserGroupMember.user_id == uid)\
568 .order_by(UserGroupToPerm.users_group_id)\
606 .order_by(UserGroupToPerm.users_group_id)\
569 .all()
607 .all()
570 #need to group here by groups since user can be in more than one group
608 #need to group here by groups since user can be in more than one group
571 _grouped = [[x, list(y)] for x, y in
609 _grouped = [[x, list(y)] for x, y in
572 itertools.groupby(user_perms_from_users_groups,
610 itertools.groupby(user_perms_from_users_groups,
573 lambda x:x.users_group)]
611 lambda x:x.users_group)]
574 for gr, perms in _grouped:
612 for gr, perms in _grouped:
575 # since user can be in multiple groups iterate over them and
613 # since user can be in multiple groups iterate over them and
576 # select the lowest permissions first (more explicit)
614 # select the lowest permissions first (more explicit)
577 ##TODO: do this^^
615 ##TODO: do this^^
578 if not gr.inherit_default_permissions:
616 if not gr.inherit_default_permissions:
579 # NEED TO IGNORE all configurable permissions and
617 # NEED TO IGNORE all configurable permissions and
580 # replace them with explicitly set
618 # replace them with explicitly set
581 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
619 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
582 .difference(_configurable)
620 .difference(_configurable)
583 for perm in perms:
621 for perm in perms:
584 user.permissions[GLOBAL].add(perm.permission.permission_name)
622 user.permissions[GLOBAL].add(perm.permission.permission_name)
585
623
586 # user specific global permissions
624 # user specific global permissions
587 user_perms = self.sa.query(UserToPerm)\
625 user_perms = self.sa.query(UserToPerm)\
588 .options(joinedload(UserToPerm.permission))\
626 .options(joinedload(UserToPerm.permission))\
589 .filter(UserToPerm.user_id == uid).all()
627 .filter(UserToPerm.user_id == uid).all()
590
628
591 if not user.inherit_default_permissions:
629 if not user.inherit_default_permissions:
592 # NEED TO IGNORE all configurable permissions and
630 # NEED TO IGNORE all configurable permissions and
593 # replace them with explicitly set
631 # replace them with explicitly set
594 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
632 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
595 .difference(_configurable)
633 .difference(_configurable)
596
634
597 for perm in user_perms:
635 for perm in user_perms:
598 user.permissions[GLOBAL].add(perm.permission.permission_name)
636 user.permissions[GLOBAL].add(perm.permission.permission_name)
599 ## END GLOBAL PERMISSIONS
637 ## END GLOBAL PERMISSIONS
600
638
601 #======================================================================
639 #======================================================================
602 # !! PERMISSIONS FOR REPOSITORIES !!
640 # !! PERMISSIONS FOR REPOSITORIES !!
603 #======================================================================
641 #======================================================================
604 #======================================================================
642 #======================================================================
605 # check if user is part of user groups for this repository and
643 # check if user is part of user groups for this repository and
606 # fill in his permission from it. _choose_perm decides of which
644 # fill in his permission from it. _choose_perm decides of which
607 # permission should be selected based on selected method
645 # permission should be selected based on selected method
608 #======================================================================
646 #======================================================================
609
647
610 # user group for repositories permissions
648 # user group for repositories permissions
611 user_repo_perms_from_users_groups = \
649 user_repo_perms_from_users_groups = \
612 self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
650 self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
613 .join((Repository, UserGroupRepoToPerm.repository_id ==
651 .join((Repository, UserGroupRepoToPerm.repository_id ==
614 Repository.repo_id))\
652 Repository.repo_id))\
615 .join((Permission, UserGroupRepoToPerm.permission_id ==
653 .join((Permission, UserGroupRepoToPerm.permission_id ==
616 Permission.permission_id))\
654 Permission.permission_id))\
617 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
655 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
618 UserGroupMember.users_group_id))\
656 UserGroupMember.users_group_id))\
619 .filter(UserGroupMember.user_id == uid)\
657 .filter(UserGroupMember.user_id == uid)\
620 .all()
658 .all()
621
659
622 multiple_counter = collections.defaultdict(int)
660 multiple_counter = collections.defaultdict(int)
623 for perm in user_repo_perms_from_users_groups:
661 for perm in user_repo_perms_from_users_groups:
624 r_k = perm.UserGroupRepoToPerm.repository.repo_name
662 r_k = perm.UserGroupRepoToPerm.repository.repo_name
625 multiple_counter[r_k] += 1
663 multiple_counter[r_k] += 1
626 p = perm.Permission.permission_name
664 p = perm.Permission.permission_name
627 cur_perm = user.permissions[RK][r_k]
665 cur_perm = user.permissions[RK][r_k]
628
666
629 if perm.Repository.user_id == uid:
667 if perm.Repository.user_id == uid:
630 # set admin if owner
668 # set admin if owner
631 p = 'repository.admin'
669 p = 'repository.admin'
632 else:
670 else:
633 if multiple_counter[r_k] > 1:
671 if multiple_counter[r_k] > 1:
634 p = _choose_perm(p, cur_perm)
672 p = _choose_perm(p, cur_perm)
635 user.permissions[RK][r_k] = p
673 user.permissions[RK][r_k] = p
636
674
637 # user explicit permissions for repositories, overrides any specified
675 # user explicit permissions for repositories, overrides any specified
638 # by the group permission
676 # by the group permission
639 user_repo_perms = Permission.get_default_perms(uid)
677 user_repo_perms = Permission.get_default_perms(uid)
640 for perm in user_repo_perms:
678 for perm in user_repo_perms:
641 r_k = perm.UserRepoToPerm.repository.repo_name
679 r_k = perm.UserRepoToPerm.repository.repo_name
642 cur_perm = user.permissions[RK][r_k]
680 cur_perm = user.permissions[RK][r_k]
643 # set admin if owner
681 # set admin if owner
644 if perm.Repository.user_id == uid:
682 if perm.Repository.user_id == uid:
645 p = 'repository.admin'
683 p = 'repository.admin'
646 else:
684 else:
647 p = perm.Permission.permission_name
685 p = perm.Permission.permission_name
648 if not explicit:
686 if not explicit:
649 p = _choose_perm(p, cur_perm)
687 p = _choose_perm(p, cur_perm)
650 user.permissions[RK][r_k] = p
688 user.permissions[RK][r_k] = p
651
689
652 #======================================================================
690 #======================================================================
653 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
691 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
654 #======================================================================
692 #======================================================================
655 #======================================================================
693 #======================================================================
656 # check if user is part of user groups for this repository groups and
694 # check if user is part of user groups for this repository groups and
657 # fill in his permission from it. _choose_perm decides of which
695 # fill in his permission from it. _choose_perm decides of which
658 # permission should be selected based on selected method
696 # permission should be selected based on selected method
659 #======================================================================
697 #======================================================================
660 # user group for repo groups permissions
698 # user group for repo groups permissions
661 user_repo_group_perms_from_users_groups = \
699 user_repo_group_perms_from_users_groups = \
662 self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
700 self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
663 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
701 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
664 .join((Permission, UserGroupRepoGroupToPerm.permission_id
702 .join((Permission, UserGroupRepoGroupToPerm.permission_id
665 == Permission.permission_id))\
703 == Permission.permission_id))\
666 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
704 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
667 == UserGroupMember.users_group_id))\
705 == UserGroupMember.users_group_id))\
668 .filter(UserGroupMember.user_id == uid)\
706 .filter(UserGroupMember.user_id == uid)\
669 .all()
707 .all()
670
708
671 multiple_counter = collections.defaultdict(int)
709 multiple_counter = collections.defaultdict(int)
672 for perm in user_repo_group_perms_from_users_groups:
710 for perm in user_repo_group_perms_from_users_groups:
673 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
711 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
674 multiple_counter[g_k] += 1
712 multiple_counter[g_k] += 1
675 p = perm.Permission.permission_name
713 p = perm.Permission.permission_name
676 cur_perm = user.permissions[GK][g_k]
714 cur_perm = user.permissions[GK][g_k]
677 if multiple_counter[g_k] > 1:
715 if multiple_counter[g_k] > 1:
678 p = _choose_perm(p, cur_perm)
716 p = _choose_perm(p, cur_perm)
679 user.permissions[GK][g_k] = p
717 user.permissions[GK][g_k] = p
680
718
681 # user explicit permissions for repository groups
719 # user explicit permissions for repository groups
682 user_repo_groups_perms = Permission.get_default_group_perms(uid)
720 user_repo_groups_perms = Permission.get_default_group_perms(uid)
683 for perm in user_repo_groups_perms:
721 for perm in user_repo_groups_perms:
684 rg_k = perm.UserRepoGroupToPerm.group.group_name
722 rg_k = perm.UserRepoGroupToPerm.group.group_name
685 p = perm.Permission.permission_name
723 p = perm.Permission.permission_name
686 cur_perm = user.permissions[GK][rg_k]
724 cur_perm = user.permissions[GK][rg_k]
687 if not explicit:
725 if not explicit:
688 p = _choose_perm(p, cur_perm)
726 p = _choose_perm(p, cur_perm)
689 user.permissions[GK][rg_k] = p
727 user.permissions[GK][rg_k] = p
690
728
691 #======================================================================
729 #======================================================================
692 # !! PERMISSIONS FOR USER GROUPS !!
730 # !! PERMISSIONS FOR USER GROUPS !!
693 #======================================================================
731 #======================================================================
694 # user group for user group permissions
732 # user group for user group permissions
695 user_group_user_groups_perms = \
733 user_group_user_groups_perms = \
696 self.sa.query(UserGroupUserGroupToPerm, Permission, UserGroup)\
734 self.sa.query(UserGroupUserGroupToPerm, Permission, UserGroup)\
697 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
735 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
698 == UserGroup.users_group_id))\
736 == UserGroup.users_group_id))\
699 .join((Permission, UserGroupUserGroupToPerm.permission_id
737 .join((Permission, UserGroupUserGroupToPerm.permission_id
700 == Permission.permission_id))\
738 == Permission.permission_id))\
701 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
739 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
702 == UserGroupMember.users_group_id))\
740 == UserGroupMember.users_group_id))\
703 .filter(UserGroupMember.user_id == uid)\
741 .filter(UserGroupMember.user_id == uid)\
704 .all()
742 .all()
705
743
706 multiple_counter = collections.defaultdict(int)
744 multiple_counter = collections.defaultdict(int)
707 for perm in user_group_user_groups_perms:
745 for perm in user_group_user_groups_perms:
708 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
746 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
709 multiple_counter[g_k] += 1
747 multiple_counter[g_k] += 1
710 p = perm.Permission.permission_name
748 p = perm.Permission.permission_name
711 cur_perm = user.permissions[UK][g_k]
749 cur_perm = user.permissions[UK][g_k]
712 if multiple_counter[g_k] > 1:
750 if multiple_counter[g_k] > 1:
713 p = _choose_perm(p, cur_perm)
751 p = _choose_perm(p, cur_perm)
714 user.permissions[UK][g_k] = p
752 user.permissions[UK][g_k] = p
715
753
716 #user explicit permission for user groups
754 #user explicit permission for user groups
717 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
755 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
718 for perm in user_user_groups_perms:
756 for perm in user_user_groups_perms:
719 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
757 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
720 p = perm.Permission.permission_name
758 p = perm.Permission.permission_name
721 cur_perm = user.permissions[UK][u_k]
759 cur_perm = user.permissions[UK][u_k]
722 if not explicit:
760 if not explicit:
723 p = _choose_perm(p, cur_perm)
761 p = _choose_perm(p, cur_perm)
724 user.permissions[UK][u_k] = p
762 user.permissions[UK][u_k] = p
725
763
726 return user
764 return user
727
765
728 def has_perm(self, user, perm):
766 def has_perm(self, user, perm):
729 perm = self._get_perm(perm)
767 perm = self._get_perm(perm)
730 user = self._get_user(user)
768 user = self._get_user(user)
731
769
732 return UserToPerm.query().filter(UserToPerm.user == user)\
770 return UserToPerm.query().filter(UserToPerm.user == user)\
733 .filter(UserToPerm.permission == perm).scalar() is not None
771 .filter(UserToPerm.permission == perm).scalar() is not None
734
772
735 def grant_perm(self, user, perm):
773 def grant_perm(self, user, perm):
736 """
774 """
737 Grant user global permissions
775 Grant user global permissions
738
776
739 :param user:
777 :param user:
740 :param perm:
778 :param perm:
741 """
779 """
742 user = self._get_user(user)
780 user = self._get_user(user)
743 perm = self._get_perm(perm)
781 perm = self._get_perm(perm)
744 # if this permission is already granted skip it
782 # if this permission is already granted skip it
745 _perm = UserToPerm.query()\
783 _perm = UserToPerm.query()\
746 .filter(UserToPerm.user == user)\
784 .filter(UserToPerm.user == user)\
747 .filter(UserToPerm.permission == perm)\
785 .filter(UserToPerm.permission == perm)\
748 .scalar()
786 .scalar()
749 if _perm:
787 if _perm:
750 return
788 return
751 new = UserToPerm()
789 new = UserToPerm()
752 new.user = user
790 new.user = user
753 new.permission = perm
791 new.permission = perm
754 self.sa.add(new)
792 self.sa.add(new)
755
793
756 def revoke_perm(self, user, perm):
794 def revoke_perm(self, user, perm):
757 """
795 """
758 Revoke users global permissions
796 Revoke users global permissions
759
797
760 :param user:
798 :param user:
761 :param perm:
799 :param perm:
762 """
800 """
763 user = self._get_user(user)
801 user = self._get_user(user)
764 perm = self._get_perm(perm)
802 perm = self._get_perm(perm)
765
803
766 obj = UserToPerm.query()\
804 obj = UserToPerm.query()\
767 .filter(UserToPerm.user == user)\
805 .filter(UserToPerm.user == user)\
768 .filter(UserToPerm.permission == perm)\
806 .filter(UserToPerm.permission == perm)\
769 .scalar()
807 .scalar()
770 if obj:
808 if obj:
771 self.sa.delete(obj)
809 self.sa.delete(obj)
772
810
773 def add_extra_email(self, user, email):
811 def add_extra_email(self, user, email):
774 """
812 """
775 Adds email address to UserEmailMap
813 Adds email address to UserEmailMap
776
814
777 :param user:
815 :param user:
778 :param email:
816 :param email:
779 """
817 """
780 from rhodecode.model import forms
818 from rhodecode.model import forms
781 form = forms.UserExtraEmailForm()()
819 form = forms.UserExtraEmailForm()()
782 data = form.to_python(dict(email=email))
820 data = form.to_python(dict(email=email))
783 user = self._get_user(user)
821 user = self._get_user(user)
784
822
785 obj = UserEmailMap()
823 obj = UserEmailMap()
786 obj.user = user
824 obj.user = user
787 obj.email = data['email']
825 obj.email = data['email']
788 self.sa.add(obj)
826 self.sa.add(obj)
789 return obj
827 return obj
790
828
791 def delete_extra_email(self, user, email_id):
829 def delete_extra_email(self, user, email_id):
792 """
830 """
793 Removes email address from UserEmailMap
831 Removes email address from UserEmailMap
794
832
795 :param user:
833 :param user:
796 :param email_id:
834 :param email_id:
797 """
835 """
798 user = self._get_user(user)
836 user = self._get_user(user)
799 obj = UserEmailMap.query().get(email_id)
837 obj = UserEmailMap.query().get(email_id)
800 if obj:
838 if obj:
801 self.sa.delete(obj)
839 self.sa.delete(obj)
802
840
803 def add_extra_ip(self, user, ip):
841 def add_extra_ip(self, user, ip):
804 """
842 """
805 Adds ip address to UserIpMap
843 Adds ip address to UserIpMap
806
844
807 :param user:
845 :param user:
808 :param ip:
846 :param ip:
809 """
847 """
810 from rhodecode.model import forms
848 from rhodecode.model import forms
811 form = forms.UserExtraIpForm()()
849 form = forms.UserExtraIpForm()()
812 data = form.to_python(dict(ip=ip))
850 data = form.to_python(dict(ip=ip))
813 user = self._get_user(user)
851 user = self._get_user(user)
814
852
815 obj = UserIpMap()
853 obj = UserIpMap()
816 obj.user = user
854 obj.user = user
817 obj.ip_addr = data['ip']
855 obj.ip_addr = data['ip']
818 self.sa.add(obj)
856 self.sa.add(obj)
819 return obj
857 return obj
820
858
821 def delete_extra_ip(self, user, ip_id):
859 def delete_extra_ip(self, user, ip_id):
822 """
860 """
823 Removes ip address from UserIpMap
861 Removes ip address from UserIpMap
824
862
825 :param user:
863 :param user:
826 :param ip_id:
864 :param ip_id:
827 """
865 """
828 user = self._get_user(user)
866 user = self._get_user(user)
829 obj = UserIpMap.query().get(ip_id)
867 obj = UserIpMap.query().get(ip_id)
830 if obj:
868 if obj:
831 self.sa.delete(obj)
869 self.sa.delete(obj)
@@ -1,91 +1,100 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="base/root.html"/>
2 <%inherit file="base/root.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Sign Up')} &middot; ${c.rhodecode_name}
5 ${_('Sign Up')} &middot; ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <div id="register">
8 <div id="register">
9
9 <div class="flash_msg">
10 <% messages = h.flash.pop_messages() %>
11 % if messages:
12 <ul id="flash-messages">
13 % for message in messages:
14 <li class="${message.category}_msg">${message}</li>
15 % endfor
16 </ul>
17 % endif
18 </div>
10 <div class="title top-left-rounded-corner top-right-rounded-corner">
19 <div class="title top-left-rounded-corner top-right-rounded-corner">
11 <h5>${_('Sign Up to')} ${c.rhodecode_name}</h5>
20 <h5>${_('Sign Up to')} ${c.rhodecode_name}</h5>
12 </div>
21 </div>
13 <div class="inner">
22 <div class="inner">
14 ${h.form(url('register'))}
23 ${h.form(url('register'))}
15 <div class="form">
24 <div class="form">
16 <!-- fields -->
25 <!-- fields -->
17 <div class="fields">
26 <div class="fields">
18 <div class="field">
27 <div class="field">
19 <div class="label">
28 <div class="label">
20 <label for="username">${_('Username')}:</label>
29 <label for="username">${_('Username')}:</label>
21 </div>
30 </div>
22 <div class="input">
31 <div class="input">
23 ${h.text('username',class_="medium")}
32 ${h.text('username',class_="medium")}
24 </div>
33 </div>
25 </div>
34 </div>
26
35
27 <div class="field">
36 <div class="field">
28 <div class="label">
37 <div class="label">
29 <label for="password">${_('Password')}:</label>
38 <label for="password">${_('Password')}:</label>
30 </div>
39 </div>
31 <div class="input">
40 <div class="input">
32 ${h.password('password',class_="medium")}
41 ${h.password('password',class_="medium")}
33 </div>
42 </div>
34 </div>
43 </div>
35
44
36 <div class="field">
45 <div class="field">
37 <div class="label">
46 <div class="label">
38 <label for="password">${_('Re-enter password')}:</label>
47 <label for="password">${_('Re-enter password')}:</label>
39 </div>
48 </div>
40 <div class="input">
49 <div class="input">
41 ${h.password('password_confirmation',class_="medium")}
50 ${h.password('password_confirmation',class_="medium")}
42 </div>
51 </div>
43 </div>
52 </div>
44
53
45 <div class="field">
54 <div class="field">
46 <div class="label">
55 <div class="label">
47 <label for="firstname">${_('First Name')}:</label>
56 <label for="firstname">${_('First Name')}:</label>
48 </div>
57 </div>
49 <div class="input">
58 <div class="input">
50 ${h.text('firstname',class_="medium")}
59 ${h.text('firstname',class_="medium")}
51 </div>
60 </div>
52 </div>
61 </div>
53
62
54 <div class="field">
63 <div class="field">
55 <div class="label">
64 <div class="label">
56 <label for="lastname">${_('Last Name')}:</label>
65 <label for="lastname">${_('Last Name')}:</label>
57 </div>
66 </div>
58 <div class="input">
67 <div class="input">
59 ${h.text('lastname',class_="medium")}
68 ${h.text('lastname',class_="medium")}
60 </div>
69 </div>
61 </div>
70 </div>
62
71
63 <div class="field">
72 <div class="field">
64 <div class="label">
73 <div class="label">
65 <label for="email">${_('Email')}:</label>
74 <label for="email">${_('Email')}:</label>
66 </div>
75 </div>
67 <div class="input">
76 <div class="input">
68 ${h.text('email',class_="medium")}
77 ${h.text('email',class_="medium")}
69 </div>
78 </div>
70 </div>
79 </div>
71
80
72 <div class="buttons">
81 <div class="buttons">
73 <div class="nohighlight">
82 <div class="nohighlight">
74 ${h.submit('sign_up',_('Sign Up'),class_="ui-btn large")}
83 ${h.submit('sign_up',_('Sign Up'),class_="ui-btn large")}
75 %if c.auto_active:
84 %if c.auto_active:
76 <div class="activation_msg">${_('Your account will be activated right after registration')}</div>
85 <div class="activation_msg">${_('Your account will be activated right after registration')}</div>
77 %else:
86 %else:
78 <div class="activation_msg">${_('Your account must wait for activation by administrator')}</div>
87 <div class="activation_msg">${_('Your account must wait for activation by administrator')}</div>
79 %endif
88 %endif
80 </div>
89 </div>
81 </div>
90 </div>
82 </div>
91 </div>
83 </div>
92 </div>
84 ${h.end_form()}
93 ${h.end_form()}
85 <script type="text/javascript">
94 <script type="text/javascript">
86 YUE.onDOMReady(function(){
95 YUE.onDOMReady(function(){
87 YUD.get('username').focus();
96 YUD.get('username').focus();
88 })
97 })
89 </script>
98 </script>
90 </div>
99 </div>
91 </div>
100 </div>
General Comments 0
You need to be logged in to leave comments. Login now