##// END OF EJS Templates
authentication: allow super-admins to change bound authentication for users....
dan -
r3988:f04c4065 default
parent child Browse files
Show More
@@ -1,1288 +1,1317 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import formencode
23 import formencode
24 import formencode.htmlfill
24 import formencode.htmlfill
25
25
26 from pyramid.httpexceptions import HTTPFound
26 from pyramid.httpexceptions import HTTPFound
27 from pyramid.view import view_config
27 from pyramid.view import view_config
28 from pyramid.renderers import render
28 from pyramid.renderers import render
29 from pyramid.response import Response
29 from pyramid.response import Response
30
30
31 from rhodecode import events
31 from rhodecode import events
32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
34 from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin
34 from rhodecode.authentication.plugins import auth_rhodecode
35 from rhodecode.authentication.plugins import auth_rhodecode
35 from rhodecode.events import trigger
36 from rhodecode.events import trigger
36 from rhodecode.model.db import true
37 from rhodecode.model.db import true
37
38
38 from rhodecode.lib import audit_logger, rc_cache
39 from rhodecode.lib import audit_logger, rc_cache
39 from rhodecode.lib.exceptions import (
40 from rhodecode.lib.exceptions import (
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
41 UserOwnsUserGroupsException, DefaultUserException)
42 UserOwnsUserGroupsException, DefaultUserException)
42 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.ext_json import json
43 from rhodecode.lib.auth import (
44 from rhodecode.lib.auth import (
44 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
45 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
45 from rhodecode.lib import helpers as h
46 from rhodecode.lib import helpers as h
46 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
47 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
47 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.auth_token import AuthTokenModel
48 from rhodecode.model.forms import (
49 from rhodecode.model.forms import (
49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
50 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
50 UserExtraEmailForm, UserExtraIpForm)
51 UserExtraEmailForm, UserExtraIpForm)
51 from rhodecode.model.permission import PermissionModel
52 from rhodecode.model.permission import PermissionModel
52 from rhodecode.model.repo_group import RepoGroupModel
53 from rhodecode.model.repo_group import RepoGroupModel
53 from rhodecode.model.ssh_key import SshKeyModel
54 from rhodecode.model.ssh_key import SshKeyModel
54 from rhodecode.model.user import UserModel
55 from rhodecode.model.user import UserModel
55 from rhodecode.model.user_group import UserGroupModel
56 from rhodecode.model.user_group import UserGroupModel
56 from rhodecode.model.db import (
57 from rhodecode.model.db import (
57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
58 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
58 UserApiKeys, UserSshKeys, RepoGroup)
59 UserApiKeys, UserSshKeys, RepoGroup)
59 from rhodecode.model.meta import Session
60 from rhodecode.model.meta import Session
60
61
61 log = logging.getLogger(__name__)
62 log = logging.getLogger(__name__)
62
63
63
64
64 class AdminUsersView(BaseAppView, DataGridAppView):
65 class AdminUsersView(BaseAppView, DataGridAppView):
65
66
66 def load_default_context(self):
67 def load_default_context(self):
67 c = self._get_local_tmpl_context()
68 c = self._get_local_tmpl_context()
68 return c
69 return c
69
70
70 @LoginRequired()
71 @LoginRequired()
71 @HasPermissionAllDecorator('hg.admin')
72 @HasPermissionAllDecorator('hg.admin')
72 @view_config(
73 @view_config(
73 route_name='users', request_method='GET',
74 route_name='users', request_method='GET',
74 renderer='rhodecode:templates/admin/users/users.mako')
75 renderer='rhodecode:templates/admin/users/users.mako')
75 def users_list(self):
76 def users_list(self):
76 c = self.load_default_context()
77 c = self.load_default_context()
77 return self._get_template_context(c)
78 return self._get_template_context(c)
78
79
79 @LoginRequired()
80 @LoginRequired()
80 @HasPermissionAllDecorator('hg.admin')
81 @HasPermissionAllDecorator('hg.admin')
81 @view_config(
82 @view_config(
82 # renderer defined below
83 # renderer defined below
83 route_name='users_data', request_method='GET',
84 route_name='users_data', request_method='GET',
84 renderer='json_ext', xhr=True)
85 renderer='json_ext', xhr=True)
85 def users_list_data(self):
86 def users_list_data(self):
86 self.load_default_context()
87 self.load_default_context()
87 column_map = {
88 column_map = {
88 'first_name': 'name',
89 'first_name': 'name',
89 'last_name': 'lastname',
90 'last_name': 'lastname',
90 }
91 }
91 draw, start, limit = self._extract_chunk(self.request)
92 draw, start, limit = self._extract_chunk(self.request)
92 search_q, order_by, order_dir = self._extract_ordering(
93 search_q, order_by, order_dir = self._extract_ordering(
93 self.request, column_map=column_map)
94 self.request, column_map=column_map)
94 _render = self.request.get_partial_renderer(
95 _render = self.request.get_partial_renderer(
95 'rhodecode:templates/data_table/_dt_elements.mako')
96 'rhodecode:templates/data_table/_dt_elements.mako')
96
97
97 def user_actions(user_id, username):
98 def user_actions(user_id, username):
98 return _render("user_actions", user_id, username)
99 return _render("user_actions", user_id, username)
99
100
100 users_data_total_count = User.query()\
101 users_data_total_count = User.query()\
101 .filter(User.username != User.DEFAULT_USER) \
102 .filter(User.username != User.DEFAULT_USER) \
102 .count()
103 .count()
103
104
104 users_data_total_inactive_count = User.query()\
105 users_data_total_inactive_count = User.query()\
105 .filter(User.username != User.DEFAULT_USER) \
106 .filter(User.username != User.DEFAULT_USER) \
106 .filter(User.active != true())\
107 .filter(User.active != true())\
107 .count()
108 .count()
108
109
109 # json generate
110 # json generate
110 base_q = User.query().filter(User.username != User.DEFAULT_USER)
111 base_q = User.query().filter(User.username != User.DEFAULT_USER)
111 base_inactive_q = base_q.filter(User.active != true())
112 base_inactive_q = base_q.filter(User.active != true())
112
113
113 if search_q:
114 if search_q:
114 like_expression = u'%{}%'.format(safe_unicode(search_q))
115 like_expression = u'%{}%'.format(safe_unicode(search_q))
115 base_q = base_q.filter(or_(
116 base_q = base_q.filter(or_(
116 User.username.ilike(like_expression),
117 User.username.ilike(like_expression),
117 User._email.ilike(like_expression),
118 User._email.ilike(like_expression),
118 User.name.ilike(like_expression),
119 User.name.ilike(like_expression),
119 User.lastname.ilike(like_expression),
120 User.lastname.ilike(like_expression),
120 ))
121 ))
121 base_inactive_q = base_q.filter(User.active != true())
122 base_inactive_q = base_q.filter(User.active != true())
122
123
123 users_data_total_filtered_count = base_q.count()
124 users_data_total_filtered_count = base_q.count()
124 users_data_total_filtered_inactive_count = base_inactive_q.count()
125 users_data_total_filtered_inactive_count = base_inactive_q.count()
125
126
126 sort_col = getattr(User, order_by, None)
127 sort_col = getattr(User, order_by, None)
127 if sort_col:
128 if sort_col:
128 if order_dir == 'asc':
129 if order_dir == 'asc':
129 # handle null values properly to order by NULL last
130 # handle null values properly to order by NULL last
130 if order_by in ['last_activity']:
131 if order_by in ['last_activity']:
131 sort_col = coalesce(sort_col, datetime.date.max)
132 sort_col = coalesce(sort_col, datetime.date.max)
132 sort_col = sort_col.asc()
133 sort_col = sort_col.asc()
133 else:
134 else:
134 # handle null values properly to order by NULL last
135 # handle null values properly to order by NULL last
135 if order_by in ['last_activity']:
136 if order_by in ['last_activity']:
136 sort_col = coalesce(sort_col, datetime.date.min)
137 sort_col = coalesce(sort_col, datetime.date.min)
137 sort_col = sort_col.desc()
138 sort_col = sort_col.desc()
138
139
139 base_q = base_q.order_by(sort_col)
140 base_q = base_q.order_by(sort_col)
140 base_q = base_q.offset(start).limit(limit)
141 base_q = base_q.offset(start).limit(limit)
141
142
142 users_list = base_q.all()
143 users_list = base_q.all()
143
144
144 users_data = []
145 users_data = []
145 for user in users_list:
146 for user in users_list:
146 users_data.append({
147 users_data.append({
147 "username": h.gravatar_with_user(self.request, user.username),
148 "username": h.gravatar_with_user(self.request, user.username),
148 "email": user.email,
149 "email": user.email,
149 "first_name": user.first_name,
150 "first_name": user.first_name,
150 "last_name": user.last_name,
151 "last_name": user.last_name,
151 "last_login": h.format_date(user.last_login),
152 "last_login": h.format_date(user.last_login),
152 "last_activity": h.format_date(user.last_activity),
153 "last_activity": h.format_date(user.last_activity),
153 "active": h.bool2icon(user.active),
154 "active": h.bool2icon(user.active),
154 "active_raw": user.active,
155 "active_raw": user.active,
155 "admin": h.bool2icon(user.admin),
156 "admin": h.bool2icon(user.admin),
156 "extern_type": user.extern_type,
157 "extern_type": user.extern_type,
157 "extern_name": user.extern_name,
158 "extern_name": user.extern_name,
158 "action": user_actions(user.user_id, user.username),
159 "action": user_actions(user.user_id, user.username),
159 })
160 })
160 data = ({
161 data = ({
161 'draw': draw,
162 'draw': draw,
162 'data': users_data,
163 'data': users_data,
163 'recordsTotal': users_data_total_count,
164 'recordsTotal': users_data_total_count,
164 'recordsFiltered': users_data_total_filtered_count,
165 'recordsFiltered': users_data_total_filtered_count,
165 'recordsTotalInactive': users_data_total_inactive_count,
166 'recordsTotalInactive': users_data_total_inactive_count,
166 'recordsFilteredInactive': users_data_total_filtered_inactive_count
167 'recordsFilteredInactive': users_data_total_filtered_inactive_count
167 })
168 })
168
169
169 return data
170 return data
170
171
171 def _set_personal_repo_group_template_vars(self, c_obj):
172 def _set_personal_repo_group_template_vars(self, c_obj):
172 DummyUser = AttributeDict({
173 DummyUser = AttributeDict({
173 'username': '${username}',
174 'username': '${username}',
174 'user_id': '${user_id}',
175 'user_id': '${user_id}',
175 })
176 })
176 c_obj.default_create_repo_group = RepoGroupModel() \
177 c_obj.default_create_repo_group = RepoGroupModel() \
177 .get_default_create_personal_repo_group()
178 .get_default_create_personal_repo_group()
178 c_obj.personal_repo_group_name = RepoGroupModel() \
179 c_obj.personal_repo_group_name = RepoGroupModel() \
179 .get_personal_group_name(DummyUser)
180 .get_personal_group_name(DummyUser)
180
181
181 @LoginRequired()
182 @LoginRequired()
182 @HasPermissionAllDecorator('hg.admin')
183 @HasPermissionAllDecorator('hg.admin')
183 @view_config(
184 @view_config(
184 route_name='users_new', request_method='GET',
185 route_name='users_new', request_method='GET',
185 renderer='rhodecode:templates/admin/users/user_add.mako')
186 renderer='rhodecode:templates/admin/users/user_add.mako')
186 def users_new(self):
187 def users_new(self):
187 _ = self.request.translate
188 _ = self.request.translate
188 c = self.load_default_context()
189 c = self.load_default_context()
189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
190 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
190 self._set_personal_repo_group_template_vars(c)
191 self._set_personal_repo_group_template_vars(c)
191 return self._get_template_context(c)
192 return self._get_template_context(c)
192
193
193 @LoginRequired()
194 @LoginRequired()
194 @HasPermissionAllDecorator('hg.admin')
195 @HasPermissionAllDecorator('hg.admin')
195 @CSRFRequired()
196 @CSRFRequired()
196 @view_config(
197 @view_config(
197 route_name='users_create', request_method='POST',
198 route_name='users_create', request_method='POST',
198 renderer='rhodecode:templates/admin/users/user_add.mako')
199 renderer='rhodecode:templates/admin/users/user_add.mako')
199 def users_create(self):
200 def users_create(self):
200 _ = self.request.translate
201 _ = self.request.translate
201 c = self.load_default_context()
202 c = self.load_default_context()
202 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
203 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
203 user_model = UserModel()
204 user_model = UserModel()
204 user_form = UserForm(self.request.translate)()
205 user_form = UserForm(self.request.translate)()
205 try:
206 try:
206 form_result = user_form.to_python(dict(self.request.POST))
207 form_result = user_form.to_python(dict(self.request.POST))
207 user = user_model.create(form_result)
208 user = user_model.create(form_result)
208 Session().flush()
209 Session().flush()
209 creation_data = user.get_api_data()
210 creation_data = user.get_api_data()
210 username = form_result['username']
211 username = form_result['username']
211
212
212 audit_logger.store_web(
213 audit_logger.store_web(
213 'user.create', action_data={'data': creation_data},
214 'user.create', action_data={'data': creation_data},
214 user=c.rhodecode_user)
215 user=c.rhodecode_user)
215
216
216 user_link = h.link_to(
217 user_link = h.link_to(
217 h.escape(username),
218 h.escape(username),
218 h.route_path('user_edit', user_id=user.user_id))
219 h.route_path('user_edit', user_id=user.user_id))
219 h.flash(h.literal(_('Created user %(user_link)s')
220 h.flash(h.literal(_('Created user %(user_link)s')
220 % {'user_link': user_link}), category='success')
221 % {'user_link': user_link}), category='success')
221 Session().commit()
222 Session().commit()
222 except formencode.Invalid as errors:
223 except formencode.Invalid as errors:
223 self._set_personal_repo_group_template_vars(c)
224 self._set_personal_repo_group_template_vars(c)
224 data = render(
225 data = render(
225 'rhodecode:templates/admin/users/user_add.mako',
226 'rhodecode:templates/admin/users/user_add.mako',
226 self._get_template_context(c), self.request)
227 self._get_template_context(c), self.request)
227 html = formencode.htmlfill.render(
228 html = formencode.htmlfill.render(
228 data,
229 data,
229 defaults=errors.value,
230 defaults=errors.value,
230 errors=errors.error_dict or {},
231 errors=errors.error_dict or {},
231 prefix_error=False,
232 prefix_error=False,
232 encoding="UTF-8",
233 encoding="UTF-8",
233 force_defaults=False
234 force_defaults=False
234 )
235 )
235 return Response(html)
236 return Response(html)
236 except UserCreationError as e:
237 except UserCreationError as e:
237 h.flash(e, 'error')
238 h.flash(e, 'error')
238 except Exception:
239 except Exception:
239 log.exception("Exception creation of user")
240 log.exception("Exception creation of user")
240 h.flash(_('Error occurred during creation of user %s')
241 h.flash(_('Error occurred during creation of user %s')
241 % self.request.POST.get('username'), category='error')
242 % self.request.POST.get('username'), category='error')
242 raise HTTPFound(h.route_path('users'))
243 raise HTTPFound(h.route_path('users'))
243
244
244
245
245 class UsersView(UserAppView):
246 class UsersView(UserAppView):
246 ALLOW_SCOPED_TOKENS = False
247 ALLOW_SCOPED_TOKENS = False
247 """
248 """
248 This view has alternative version inside EE, if modified please take a look
249 This view has alternative version inside EE, if modified please take a look
249 in there as well.
250 in there as well.
250 """
251 """
251
252
253 def get_auth_plugins(self):
254 valid_plugins = []
255 authn_registry = get_authn_registry(self.request.registry)
256 for plugin in authn_registry.get_plugins_for_authentication():
257 if isinstance(plugin, RhodeCodeExternalAuthPlugin):
258 valid_plugins.append(plugin)
259 elif plugin.name == 'rhodecode':
260 valid_plugins.append(plugin)
261
262 # extend our choices if user has set a bound plugin which isn't enabled at the
263 # moment
264 extern_type = self.db_user.extern_type
265 if extern_type not in [x.uid for x in valid_plugins]:
266 try:
267 plugin = authn_registry.get_plugin_by_uid(extern_type)
268 if plugin:
269 valid_plugins.append(plugin)
270
271 except Exception:
272 log.exception(
273 'Could not extend user plugins with `{}`'.format(extern_type))
274 return valid_plugins
275
252 def load_default_context(self):
276 def load_default_context(self):
277 req = self.request
278
253 c = self._get_local_tmpl_context()
279 c = self._get_local_tmpl_context()
254 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
280 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
255 c.allowed_languages = [
281 c.allowed_languages = [
256 ('en', 'English (en)'),
282 ('en', 'English (en)'),
257 ('de', 'German (de)'),
283 ('de', 'German (de)'),
258 ('fr', 'French (fr)'),
284 ('fr', 'French (fr)'),
259 ('it', 'Italian (it)'),
285 ('it', 'Italian (it)'),
260 ('ja', 'Japanese (ja)'),
286 ('ja', 'Japanese (ja)'),
261 ('pl', 'Polish (pl)'),
287 ('pl', 'Polish (pl)'),
262 ('pt', 'Portuguese (pt)'),
288 ('pt', 'Portuguese (pt)'),
263 ('ru', 'Russian (ru)'),
289 ('ru', 'Russian (ru)'),
264 ('zh', 'Chinese (zh)'),
290 ('zh', 'Chinese (zh)'),
265 ]
291 ]
266 req = self.request
292
293 c.allowed_extern_types = [
294 (x.uid, x.get_display_name()) for x in self.get_auth_plugins()
295 ]
267
296
268 c.available_permissions = req.registry.settings['available_permissions']
297 c.available_permissions = req.registry.settings['available_permissions']
269 PermissionModel().set_global_permission_choices(
298 PermissionModel().set_global_permission_choices(
270 c, gettext_translator=req.translate)
299 c, gettext_translator=req.translate)
271
300
272 return c
301 return c
273
302
274 @LoginRequired()
303 @LoginRequired()
275 @HasPermissionAllDecorator('hg.admin')
304 @HasPermissionAllDecorator('hg.admin')
276 @CSRFRequired()
305 @CSRFRequired()
277 @view_config(
306 @view_config(
278 route_name='user_update', request_method='POST',
307 route_name='user_update', request_method='POST',
279 renderer='rhodecode:templates/admin/users/user_edit.mako')
308 renderer='rhodecode:templates/admin/users/user_edit.mako')
280 def user_update(self):
309 def user_update(self):
281 _ = self.request.translate
310 _ = self.request.translate
282 c = self.load_default_context()
311 c = self.load_default_context()
283
312
284 user_id = self.db_user_id
313 user_id = self.db_user_id
285 c.user = self.db_user
314 c.user = self.db_user
286
315
287 c.active = 'profile'
316 c.active = 'profile'
288 c.extern_type = c.user.extern_type
317 c.extern_type = c.user.extern_type
289 c.extern_name = c.user.extern_name
318 c.extern_name = c.user.extern_name
290 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
319 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
291 available_languages = [x[0] for x in c.allowed_languages]
320 available_languages = [x[0] for x in c.allowed_languages]
292 _form = UserForm(self.request.translate, edit=True,
321 _form = UserForm(self.request.translate, edit=True,
293 available_languages=available_languages,
322 available_languages=available_languages,
294 old_data={'user_id': user_id,
323 old_data={'user_id': user_id,
295 'email': c.user.email})()
324 'email': c.user.email})()
296 form_result = {}
325 form_result = {}
297 old_values = c.user.get_api_data()
326 old_values = c.user.get_api_data()
298 try:
327 try:
299 form_result = _form.to_python(dict(self.request.POST))
328 form_result = _form.to_python(dict(self.request.POST))
300 skip_attrs = ['extern_type', 'extern_name']
329 skip_attrs = ['extern_name']
301 # TODO: plugin should define if username can be updated
330 # TODO: plugin should define if username can be updated
302 if c.extern_type != "rhodecode":
331 if c.extern_type != "rhodecode":
303 # forbid updating username for external accounts
332 # forbid updating username for external accounts
304 skip_attrs.append('username')
333 skip_attrs.append('username')
305
334
306 UserModel().update_user(
335 UserModel().update_user(
307 user_id, skip_attrs=skip_attrs, **form_result)
336 user_id, skip_attrs=skip_attrs, **form_result)
308
337
309 audit_logger.store_web(
338 audit_logger.store_web(
310 'user.edit', action_data={'old_data': old_values},
339 'user.edit', action_data={'old_data': old_values},
311 user=c.rhodecode_user)
340 user=c.rhodecode_user)
312
341
313 Session().commit()
342 Session().commit()
314 h.flash(_('User updated successfully'), category='success')
343 h.flash(_('User updated successfully'), category='success')
315 except formencode.Invalid as errors:
344 except formencode.Invalid as errors:
316 data = render(
345 data = render(
317 'rhodecode:templates/admin/users/user_edit.mako',
346 'rhodecode:templates/admin/users/user_edit.mako',
318 self._get_template_context(c), self.request)
347 self._get_template_context(c), self.request)
319 html = formencode.htmlfill.render(
348 html = formencode.htmlfill.render(
320 data,
349 data,
321 defaults=errors.value,
350 defaults=errors.value,
322 errors=errors.error_dict or {},
351 errors=errors.error_dict or {},
323 prefix_error=False,
352 prefix_error=False,
324 encoding="UTF-8",
353 encoding="UTF-8",
325 force_defaults=False
354 force_defaults=False
326 )
355 )
327 return Response(html)
356 return Response(html)
328 except UserCreationError as e:
357 except UserCreationError as e:
329 h.flash(e, 'error')
358 h.flash(e, 'error')
330 except Exception:
359 except Exception:
331 log.exception("Exception updating user")
360 log.exception("Exception updating user")
332 h.flash(_('Error occurred during update of user %s')
361 h.flash(_('Error occurred during update of user %s')
333 % form_result.get('username'), category='error')
362 % form_result.get('username'), category='error')
334 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
363 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
335
364
336 @LoginRequired()
365 @LoginRequired()
337 @HasPermissionAllDecorator('hg.admin')
366 @HasPermissionAllDecorator('hg.admin')
338 @CSRFRequired()
367 @CSRFRequired()
339 @view_config(
368 @view_config(
340 route_name='user_delete', request_method='POST',
369 route_name='user_delete', request_method='POST',
341 renderer='rhodecode:templates/admin/users/user_edit.mako')
370 renderer='rhodecode:templates/admin/users/user_edit.mako')
342 def user_delete(self):
371 def user_delete(self):
343 _ = self.request.translate
372 _ = self.request.translate
344 c = self.load_default_context()
373 c = self.load_default_context()
345 c.user = self.db_user
374 c.user = self.db_user
346
375
347 _repos = c.user.repositories
376 _repos = c.user.repositories
348 _repo_groups = c.user.repository_groups
377 _repo_groups = c.user.repository_groups
349 _user_groups = c.user.user_groups
378 _user_groups = c.user.user_groups
350
379
351 handle_repos = None
380 handle_repos = None
352 handle_repo_groups = None
381 handle_repo_groups = None
353 handle_user_groups = None
382 handle_user_groups = None
354 # dummy call for flash of handle
383 # dummy call for flash of handle
355 set_handle_flash_repos = lambda: None
384 set_handle_flash_repos = lambda: None
356 set_handle_flash_repo_groups = lambda: None
385 set_handle_flash_repo_groups = lambda: None
357 set_handle_flash_user_groups = lambda: None
386 set_handle_flash_user_groups = lambda: None
358
387
359 if _repos and self.request.POST.get('user_repos'):
388 if _repos and self.request.POST.get('user_repos'):
360 do = self.request.POST['user_repos']
389 do = self.request.POST['user_repos']
361 if do == 'detach':
390 if do == 'detach':
362 handle_repos = 'detach'
391 handle_repos = 'detach'
363 set_handle_flash_repos = lambda: h.flash(
392 set_handle_flash_repos = lambda: h.flash(
364 _('Detached %s repositories') % len(_repos),
393 _('Detached %s repositories') % len(_repos),
365 category='success')
394 category='success')
366 elif do == 'delete':
395 elif do == 'delete':
367 handle_repos = 'delete'
396 handle_repos = 'delete'
368 set_handle_flash_repos = lambda: h.flash(
397 set_handle_flash_repos = lambda: h.flash(
369 _('Deleted %s repositories') % len(_repos),
398 _('Deleted %s repositories') % len(_repos),
370 category='success')
399 category='success')
371
400
372 if _repo_groups and self.request.POST.get('user_repo_groups'):
401 if _repo_groups and self.request.POST.get('user_repo_groups'):
373 do = self.request.POST['user_repo_groups']
402 do = self.request.POST['user_repo_groups']
374 if do == 'detach':
403 if do == 'detach':
375 handle_repo_groups = 'detach'
404 handle_repo_groups = 'detach'
376 set_handle_flash_repo_groups = lambda: h.flash(
405 set_handle_flash_repo_groups = lambda: h.flash(
377 _('Detached %s repository groups') % len(_repo_groups),
406 _('Detached %s repository groups') % len(_repo_groups),
378 category='success')
407 category='success')
379 elif do == 'delete':
408 elif do == 'delete':
380 handle_repo_groups = 'delete'
409 handle_repo_groups = 'delete'
381 set_handle_flash_repo_groups = lambda: h.flash(
410 set_handle_flash_repo_groups = lambda: h.flash(
382 _('Deleted %s repository groups') % len(_repo_groups),
411 _('Deleted %s repository groups') % len(_repo_groups),
383 category='success')
412 category='success')
384
413
385 if _user_groups and self.request.POST.get('user_user_groups'):
414 if _user_groups and self.request.POST.get('user_user_groups'):
386 do = self.request.POST['user_user_groups']
415 do = self.request.POST['user_user_groups']
387 if do == 'detach':
416 if do == 'detach':
388 handle_user_groups = 'detach'
417 handle_user_groups = 'detach'
389 set_handle_flash_user_groups = lambda: h.flash(
418 set_handle_flash_user_groups = lambda: h.flash(
390 _('Detached %s user groups') % len(_user_groups),
419 _('Detached %s user groups') % len(_user_groups),
391 category='success')
420 category='success')
392 elif do == 'delete':
421 elif do == 'delete':
393 handle_user_groups = 'delete'
422 handle_user_groups = 'delete'
394 set_handle_flash_user_groups = lambda: h.flash(
423 set_handle_flash_user_groups = lambda: h.flash(
395 _('Deleted %s user groups') % len(_user_groups),
424 _('Deleted %s user groups') % len(_user_groups),
396 category='success')
425 category='success')
397
426
398 old_values = c.user.get_api_data()
427 old_values = c.user.get_api_data()
399 try:
428 try:
400 UserModel().delete(c.user, handle_repos=handle_repos,
429 UserModel().delete(c.user, handle_repos=handle_repos,
401 handle_repo_groups=handle_repo_groups,
430 handle_repo_groups=handle_repo_groups,
402 handle_user_groups=handle_user_groups)
431 handle_user_groups=handle_user_groups)
403
432
404 audit_logger.store_web(
433 audit_logger.store_web(
405 'user.delete', action_data={'old_data': old_values},
434 'user.delete', action_data={'old_data': old_values},
406 user=c.rhodecode_user)
435 user=c.rhodecode_user)
407
436
408 Session().commit()
437 Session().commit()
409 set_handle_flash_repos()
438 set_handle_flash_repos()
410 set_handle_flash_repo_groups()
439 set_handle_flash_repo_groups()
411 set_handle_flash_user_groups()
440 set_handle_flash_user_groups()
412 h.flash(_('Successfully deleted user'), category='success')
441 h.flash(_('Successfully deleted user'), category='success')
413 except (UserOwnsReposException, UserOwnsRepoGroupsException,
442 except (UserOwnsReposException, UserOwnsRepoGroupsException,
414 UserOwnsUserGroupsException, DefaultUserException) as e:
443 UserOwnsUserGroupsException, DefaultUserException) as e:
415 h.flash(e, category='warning')
444 h.flash(e, category='warning')
416 except Exception:
445 except Exception:
417 log.exception("Exception during deletion of user")
446 log.exception("Exception during deletion of user")
418 h.flash(_('An error occurred during deletion of user'),
447 h.flash(_('An error occurred during deletion of user'),
419 category='error')
448 category='error')
420 raise HTTPFound(h.route_path('users'))
449 raise HTTPFound(h.route_path('users'))
421
450
422 @LoginRequired()
451 @LoginRequired()
423 @HasPermissionAllDecorator('hg.admin')
452 @HasPermissionAllDecorator('hg.admin')
424 @view_config(
453 @view_config(
425 route_name='user_edit', request_method='GET',
454 route_name='user_edit', request_method='GET',
426 renderer='rhodecode:templates/admin/users/user_edit.mako')
455 renderer='rhodecode:templates/admin/users/user_edit.mako')
427 def user_edit(self):
456 def user_edit(self):
428 _ = self.request.translate
457 _ = self.request.translate
429 c = self.load_default_context()
458 c = self.load_default_context()
430 c.user = self.db_user
459 c.user = self.db_user
431
460
432 c.active = 'profile'
461 c.active = 'profile'
433 c.extern_type = c.user.extern_type
462 c.extern_type = c.user.extern_type
434 c.extern_name = c.user.extern_name
463 c.extern_name = c.user.extern_name
435 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
464 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
436
465
437 defaults = c.user.get_dict()
466 defaults = c.user.get_dict()
438 defaults.update({'language': c.user.user_data.get('language')})
467 defaults.update({'language': c.user.user_data.get('language')})
439
468
440 data = render(
469 data = render(
441 'rhodecode:templates/admin/users/user_edit.mako',
470 'rhodecode:templates/admin/users/user_edit.mako',
442 self._get_template_context(c), self.request)
471 self._get_template_context(c), self.request)
443 html = formencode.htmlfill.render(
472 html = formencode.htmlfill.render(
444 data,
473 data,
445 defaults=defaults,
474 defaults=defaults,
446 encoding="UTF-8",
475 encoding="UTF-8",
447 force_defaults=False
476 force_defaults=False
448 )
477 )
449 return Response(html)
478 return Response(html)
450
479
451 @LoginRequired()
480 @LoginRequired()
452 @HasPermissionAllDecorator('hg.admin')
481 @HasPermissionAllDecorator('hg.admin')
453 @view_config(
482 @view_config(
454 route_name='user_edit_advanced', request_method='GET',
483 route_name='user_edit_advanced', request_method='GET',
455 renderer='rhodecode:templates/admin/users/user_edit.mako')
484 renderer='rhodecode:templates/admin/users/user_edit.mako')
456 def user_edit_advanced(self):
485 def user_edit_advanced(self):
457 _ = self.request.translate
486 _ = self.request.translate
458 c = self.load_default_context()
487 c = self.load_default_context()
459
488
460 user_id = self.db_user_id
489 user_id = self.db_user_id
461 c.user = self.db_user
490 c.user = self.db_user
462
491
463 c.active = 'advanced'
492 c.active = 'advanced'
464 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
493 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
465 c.personal_repo_group_name = RepoGroupModel()\
494 c.personal_repo_group_name = RepoGroupModel()\
466 .get_personal_group_name(c.user)
495 .get_personal_group_name(c.user)
467
496
468 c.user_to_review_rules = sorted(
497 c.user_to_review_rules = sorted(
469 (x.user for x in c.user.user_review_rules),
498 (x.user for x in c.user.user_review_rules),
470 key=lambda u: u.username.lower())
499 key=lambda u: u.username.lower())
471
500
472 c.first_admin = User.get_first_super_admin()
501 c.first_admin = User.get_first_super_admin()
473 defaults = c.user.get_dict()
502 defaults = c.user.get_dict()
474
503
475 # Interim workaround if the user participated on any pull requests as a
504 # Interim workaround if the user participated on any pull requests as a
476 # reviewer.
505 # reviewer.
477 has_review = len(c.user.reviewer_pull_requests)
506 has_review = len(c.user.reviewer_pull_requests)
478 c.can_delete_user = not has_review
507 c.can_delete_user = not has_review
479 c.can_delete_user_message = ''
508 c.can_delete_user_message = ''
480 inactive_link = h.link_to(
509 inactive_link = h.link_to(
481 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
510 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
482 if has_review == 1:
511 if has_review == 1:
483 c.can_delete_user_message = h.literal(_(
512 c.can_delete_user_message = h.literal(_(
484 'The user participates as reviewer in {} pull request and '
513 'The user participates as reviewer in {} pull request and '
485 'cannot be deleted. \nYou can set the user to '
514 'cannot be deleted. \nYou can set the user to '
486 '"{}" instead of deleting it.').format(
515 '"{}" instead of deleting it.').format(
487 has_review, inactive_link))
516 has_review, inactive_link))
488 elif has_review:
517 elif has_review:
489 c.can_delete_user_message = h.literal(_(
518 c.can_delete_user_message = h.literal(_(
490 'The user participates as reviewer in {} pull requests and '
519 'The user participates as reviewer in {} pull requests and '
491 'cannot be deleted. \nYou can set the user to '
520 'cannot be deleted. \nYou can set the user to '
492 '"{}" instead of deleting it.').format(
521 '"{}" instead of deleting it.').format(
493 has_review, inactive_link))
522 has_review, inactive_link))
494
523
495 data = render(
524 data = render(
496 'rhodecode:templates/admin/users/user_edit.mako',
525 'rhodecode:templates/admin/users/user_edit.mako',
497 self._get_template_context(c), self.request)
526 self._get_template_context(c), self.request)
498 html = formencode.htmlfill.render(
527 html = formencode.htmlfill.render(
499 data,
528 data,
500 defaults=defaults,
529 defaults=defaults,
501 encoding="UTF-8",
530 encoding="UTF-8",
502 force_defaults=False
531 force_defaults=False
503 )
532 )
504 return Response(html)
533 return Response(html)
505
534
506 @LoginRequired()
535 @LoginRequired()
507 @HasPermissionAllDecorator('hg.admin')
536 @HasPermissionAllDecorator('hg.admin')
508 @view_config(
537 @view_config(
509 route_name='user_edit_global_perms', request_method='GET',
538 route_name='user_edit_global_perms', request_method='GET',
510 renderer='rhodecode:templates/admin/users/user_edit.mako')
539 renderer='rhodecode:templates/admin/users/user_edit.mako')
511 def user_edit_global_perms(self):
540 def user_edit_global_perms(self):
512 _ = self.request.translate
541 _ = self.request.translate
513 c = self.load_default_context()
542 c = self.load_default_context()
514 c.user = self.db_user
543 c.user = self.db_user
515
544
516 c.active = 'global_perms'
545 c.active = 'global_perms'
517
546
518 c.default_user = User.get_default_user()
547 c.default_user = User.get_default_user()
519 defaults = c.user.get_dict()
548 defaults = c.user.get_dict()
520 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
549 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
521 defaults.update(c.default_user.get_default_perms())
550 defaults.update(c.default_user.get_default_perms())
522 defaults.update(c.user.get_default_perms())
551 defaults.update(c.user.get_default_perms())
523
552
524 data = render(
553 data = render(
525 'rhodecode:templates/admin/users/user_edit.mako',
554 'rhodecode:templates/admin/users/user_edit.mako',
526 self._get_template_context(c), self.request)
555 self._get_template_context(c), self.request)
527 html = formencode.htmlfill.render(
556 html = formencode.htmlfill.render(
528 data,
557 data,
529 defaults=defaults,
558 defaults=defaults,
530 encoding="UTF-8",
559 encoding="UTF-8",
531 force_defaults=False
560 force_defaults=False
532 )
561 )
533 return Response(html)
562 return Response(html)
534
563
535 @LoginRequired()
564 @LoginRequired()
536 @HasPermissionAllDecorator('hg.admin')
565 @HasPermissionAllDecorator('hg.admin')
537 @CSRFRequired()
566 @CSRFRequired()
538 @view_config(
567 @view_config(
539 route_name='user_edit_global_perms_update', request_method='POST',
568 route_name='user_edit_global_perms_update', request_method='POST',
540 renderer='rhodecode:templates/admin/users/user_edit.mako')
569 renderer='rhodecode:templates/admin/users/user_edit.mako')
541 def user_edit_global_perms_update(self):
570 def user_edit_global_perms_update(self):
542 _ = self.request.translate
571 _ = self.request.translate
543 c = self.load_default_context()
572 c = self.load_default_context()
544
573
545 user_id = self.db_user_id
574 user_id = self.db_user_id
546 c.user = self.db_user
575 c.user = self.db_user
547
576
548 c.active = 'global_perms'
577 c.active = 'global_perms'
549 try:
578 try:
550 # first stage that verifies the checkbox
579 # first stage that verifies the checkbox
551 _form = UserIndividualPermissionsForm(self.request.translate)
580 _form = UserIndividualPermissionsForm(self.request.translate)
552 form_result = _form.to_python(dict(self.request.POST))
581 form_result = _form.to_python(dict(self.request.POST))
553 inherit_perms = form_result['inherit_default_permissions']
582 inherit_perms = form_result['inherit_default_permissions']
554 c.user.inherit_default_permissions = inherit_perms
583 c.user.inherit_default_permissions = inherit_perms
555 Session().add(c.user)
584 Session().add(c.user)
556
585
557 if not inherit_perms:
586 if not inherit_perms:
558 # only update the individual ones if we un check the flag
587 # only update the individual ones if we un check the flag
559 _form = UserPermissionsForm(
588 _form = UserPermissionsForm(
560 self.request.translate,
589 self.request.translate,
561 [x[0] for x in c.repo_create_choices],
590 [x[0] for x in c.repo_create_choices],
562 [x[0] for x in c.repo_create_on_write_choices],
591 [x[0] for x in c.repo_create_on_write_choices],
563 [x[0] for x in c.repo_group_create_choices],
592 [x[0] for x in c.repo_group_create_choices],
564 [x[0] for x in c.user_group_create_choices],
593 [x[0] for x in c.user_group_create_choices],
565 [x[0] for x in c.fork_choices],
594 [x[0] for x in c.fork_choices],
566 [x[0] for x in c.inherit_default_permission_choices])()
595 [x[0] for x in c.inherit_default_permission_choices])()
567
596
568 form_result = _form.to_python(dict(self.request.POST))
597 form_result = _form.to_python(dict(self.request.POST))
569 form_result.update({'perm_user_id': c.user.user_id})
598 form_result.update({'perm_user_id': c.user.user_id})
570
599
571 PermissionModel().update_user_permissions(form_result)
600 PermissionModel().update_user_permissions(form_result)
572
601
573 # TODO(marcink): implement global permissions
602 # TODO(marcink): implement global permissions
574 # audit_log.store_web('user.edit.permissions')
603 # audit_log.store_web('user.edit.permissions')
575
604
576 Session().commit()
605 Session().commit()
577
606
578 h.flash(_('User global permissions updated successfully'),
607 h.flash(_('User global permissions updated successfully'),
579 category='success')
608 category='success')
580
609
581 except formencode.Invalid as errors:
610 except formencode.Invalid as errors:
582 data = render(
611 data = render(
583 'rhodecode:templates/admin/users/user_edit.mako',
612 'rhodecode:templates/admin/users/user_edit.mako',
584 self._get_template_context(c), self.request)
613 self._get_template_context(c), self.request)
585 html = formencode.htmlfill.render(
614 html = formencode.htmlfill.render(
586 data,
615 data,
587 defaults=errors.value,
616 defaults=errors.value,
588 errors=errors.error_dict or {},
617 errors=errors.error_dict or {},
589 prefix_error=False,
618 prefix_error=False,
590 encoding="UTF-8",
619 encoding="UTF-8",
591 force_defaults=False
620 force_defaults=False
592 )
621 )
593 return Response(html)
622 return Response(html)
594 except Exception:
623 except Exception:
595 log.exception("Exception during permissions saving")
624 log.exception("Exception during permissions saving")
596 h.flash(_('An error occurred during permissions saving'),
625 h.flash(_('An error occurred during permissions saving'),
597 category='error')
626 category='error')
598
627
599 affected_user_ids = [user_id]
628 affected_user_ids = [user_id]
600 PermissionModel().trigger_permission_flush(affected_user_ids)
629 PermissionModel().trigger_permission_flush(affected_user_ids)
601 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
630 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
602
631
603 @LoginRequired()
632 @LoginRequired()
604 @HasPermissionAllDecorator('hg.admin')
633 @HasPermissionAllDecorator('hg.admin')
605 @CSRFRequired()
634 @CSRFRequired()
606 @view_config(
635 @view_config(
607 route_name='user_enable_force_password_reset', request_method='POST',
636 route_name='user_enable_force_password_reset', request_method='POST',
608 renderer='rhodecode:templates/admin/users/user_edit.mako')
637 renderer='rhodecode:templates/admin/users/user_edit.mako')
609 def user_enable_force_password_reset(self):
638 def user_enable_force_password_reset(self):
610 _ = self.request.translate
639 _ = self.request.translate
611 c = self.load_default_context()
640 c = self.load_default_context()
612
641
613 user_id = self.db_user_id
642 user_id = self.db_user_id
614 c.user = self.db_user
643 c.user = self.db_user
615
644
616 try:
645 try:
617 c.user.update_userdata(force_password_change=True)
646 c.user.update_userdata(force_password_change=True)
618
647
619 msg = _('Force password change enabled for user')
648 msg = _('Force password change enabled for user')
620 audit_logger.store_web('user.edit.password_reset.enabled',
649 audit_logger.store_web('user.edit.password_reset.enabled',
621 user=c.rhodecode_user)
650 user=c.rhodecode_user)
622
651
623 Session().commit()
652 Session().commit()
624 h.flash(msg, category='success')
653 h.flash(msg, category='success')
625 except Exception:
654 except Exception:
626 log.exception("Exception during password reset for user")
655 log.exception("Exception during password reset for user")
627 h.flash(_('An error occurred during password reset for user'),
656 h.flash(_('An error occurred during password reset for user'),
628 category='error')
657 category='error')
629
658
630 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
659 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
631
660
632 @LoginRequired()
661 @LoginRequired()
633 @HasPermissionAllDecorator('hg.admin')
662 @HasPermissionAllDecorator('hg.admin')
634 @CSRFRequired()
663 @CSRFRequired()
635 @view_config(
664 @view_config(
636 route_name='user_disable_force_password_reset', request_method='POST',
665 route_name='user_disable_force_password_reset', request_method='POST',
637 renderer='rhodecode:templates/admin/users/user_edit.mako')
666 renderer='rhodecode:templates/admin/users/user_edit.mako')
638 def user_disable_force_password_reset(self):
667 def user_disable_force_password_reset(self):
639 _ = self.request.translate
668 _ = self.request.translate
640 c = self.load_default_context()
669 c = self.load_default_context()
641
670
642 user_id = self.db_user_id
671 user_id = self.db_user_id
643 c.user = self.db_user
672 c.user = self.db_user
644
673
645 try:
674 try:
646 c.user.update_userdata(force_password_change=False)
675 c.user.update_userdata(force_password_change=False)
647
676
648 msg = _('Force password change disabled for user')
677 msg = _('Force password change disabled for user')
649 audit_logger.store_web(
678 audit_logger.store_web(
650 'user.edit.password_reset.disabled',
679 'user.edit.password_reset.disabled',
651 user=c.rhodecode_user)
680 user=c.rhodecode_user)
652
681
653 Session().commit()
682 Session().commit()
654 h.flash(msg, category='success')
683 h.flash(msg, category='success')
655 except Exception:
684 except Exception:
656 log.exception("Exception during password reset for user")
685 log.exception("Exception during password reset for user")
657 h.flash(_('An error occurred during password reset for user'),
686 h.flash(_('An error occurred during password reset for user'),
658 category='error')
687 category='error')
659
688
660 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
689 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
661
690
662 @LoginRequired()
691 @LoginRequired()
663 @HasPermissionAllDecorator('hg.admin')
692 @HasPermissionAllDecorator('hg.admin')
664 @CSRFRequired()
693 @CSRFRequired()
665 @view_config(
694 @view_config(
666 route_name='user_create_personal_repo_group', request_method='POST',
695 route_name='user_create_personal_repo_group', request_method='POST',
667 renderer='rhodecode:templates/admin/users/user_edit.mako')
696 renderer='rhodecode:templates/admin/users/user_edit.mako')
668 def user_create_personal_repo_group(self):
697 def user_create_personal_repo_group(self):
669 """
698 """
670 Create personal repository group for this user
699 Create personal repository group for this user
671 """
700 """
672 from rhodecode.model.repo_group import RepoGroupModel
701 from rhodecode.model.repo_group import RepoGroupModel
673
702
674 _ = self.request.translate
703 _ = self.request.translate
675 c = self.load_default_context()
704 c = self.load_default_context()
676
705
677 user_id = self.db_user_id
706 user_id = self.db_user_id
678 c.user = self.db_user
707 c.user = self.db_user
679
708
680 personal_repo_group = RepoGroup.get_user_personal_repo_group(
709 personal_repo_group = RepoGroup.get_user_personal_repo_group(
681 c.user.user_id)
710 c.user.user_id)
682 if personal_repo_group:
711 if personal_repo_group:
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
712 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
684
713
685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
714 personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user)
686 named_personal_group = RepoGroup.get_by_group_name(
715 named_personal_group = RepoGroup.get_by_group_name(
687 personal_repo_group_name)
716 personal_repo_group_name)
688 try:
717 try:
689
718
690 if named_personal_group and named_personal_group.user_id == c.user.user_id:
719 if named_personal_group and named_personal_group.user_id == c.user.user_id:
691 # migrate the same named group, and mark it as personal
720 # migrate the same named group, and mark it as personal
692 named_personal_group.personal = True
721 named_personal_group.personal = True
693 Session().add(named_personal_group)
722 Session().add(named_personal_group)
694 Session().commit()
723 Session().commit()
695 msg = _('Linked repository group `%s` as personal' % (
724 msg = _('Linked repository group `%s` as personal' % (
696 personal_repo_group_name,))
725 personal_repo_group_name,))
697 h.flash(msg, category='success')
726 h.flash(msg, category='success')
698 elif not named_personal_group:
727 elif not named_personal_group:
699 RepoGroupModel().create_personal_repo_group(c.user)
728 RepoGroupModel().create_personal_repo_group(c.user)
700
729
701 msg = _('Created repository group `%s`' % (
730 msg = _('Created repository group `%s`' % (
702 personal_repo_group_name,))
731 personal_repo_group_name,))
703 h.flash(msg, category='success')
732 h.flash(msg, category='success')
704 else:
733 else:
705 msg = _('Repository group `%s` is already taken' % (
734 msg = _('Repository group `%s` is already taken' % (
706 personal_repo_group_name,))
735 personal_repo_group_name,))
707 h.flash(msg, category='warning')
736 h.flash(msg, category='warning')
708 except Exception:
737 except Exception:
709 log.exception("Exception during repository group creation")
738 log.exception("Exception during repository group creation")
710 msg = _(
739 msg = _(
711 'An error occurred during repository group creation for user')
740 'An error occurred during repository group creation for user')
712 h.flash(msg, category='error')
741 h.flash(msg, category='error')
713 Session().rollback()
742 Session().rollback()
714
743
715 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
744 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
716
745
717 @LoginRequired()
746 @LoginRequired()
718 @HasPermissionAllDecorator('hg.admin')
747 @HasPermissionAllDecorator('hg.admin')
719 @view_config(
748 @view_config(
720 route_name='edit_user_auth_tokens', request_method='GET',
749 route_name='edit_user_auth_tokens', request_method='GET',
721 renderer='rhodecode:templates/admin/users/user_edit.mako')
750 renderer='rhodecode:templates/admin/users/user_edit.mako')
722 def auth_tokens(self):
751 def auth_tokens(self):
723 _ = self.request.translate
752 _ = self.request.translate
724 c = self.load_default_context()
753 c = self.load_default_context()
725 c.user = self.db_user
754 c.user = self.db_user
726
755
727 c.active = 'auth_tokens'
756 c.active = 'auth_tokens'
728
757
729 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
758 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
730 c.role_values = [
759 c.role_values = [
731 (x, AuthTokenModel.cls._get_role_name(x))
760 (x, AuthTokenModel.cls._get_role_name(x))
732 for x in AuthTokenModel.cls.ROLES]
761 for x in AuthTokenModel.cls.ROLES]
733 c.role_options = [(c.role_values, _("Role"))]
762 c.role_options = [(c.role_values, _("Role"))]
734 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
763 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
735 c.user.user_id, show_expired=True)
764 c.user.user_id, show_expired=True)
736 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
765 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
737 return self._get_template_context(c)
766 return self._get_template_context(c)
738
767
739 def maybe_attach_token_scope(self, token):
768 def maybe_attach_token_scope(self, token):
740 # implemented in EE edition
769 # implemented in EE edition
741 pass
770 pass
742
771
743 @LoginRequired()
772 @LoginRequired()
744 @HasPermissionAllDecorator('hg.admin')
773 @HasPermissionAllDecorator('hg.admin')
745 @CSRFRequired()
774 @CSRFRequired()
746 @view_config(
775 @view_config(
747 route_name='edit_user_auth_tokens_add', request_method='POST')
776 route_name='edit_user_auth_tokens_add', request_method='POST')
748 def auth_tokens_add(self):
777 def auth_tokens_add(self):
749 _ = self.request.translate
778 _ = self.request.translate
750 c = self.load_default_context()
779 c = self.load_default_context()
751
780
752 user_id = self.db_user_id
781 user_id = self.db_user_id
753 c.user = self.db_user
782 c.user = self.db_user
754
783
755 user_data = c.user.get_api_data()
784 user_data = c.user.get_api_data()
756 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
785 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
757 description = self.request.POST.get('description')
786 description = self.request.POST.get('description')
758 role = self.request.POST.get('role')
787 role = self.request.POST.get('role')
759
788
760 token = UserModel().add_auth_token(
789 token = UserModel().add_auth_token(
761 user=c.user.user_id,
790 user=c.user.user_id,
762 lifetime_minutes=lifetime, role=role, description=description,
791 lifetime_minutes=lifetime, role=role, description=description,
763 scope_callback=self.maybe_attach_token_scope)
792 scope_callback=self.maybe_attach_token_scope)
764 token_data = token.get_api_data()
793 token_data = token.get_api_data()
765
794
766 audit_logger.store_web(
795 audit_logger.store_web(
767 'user.edit.token.add', action_data={
796 'user.edit.token.add', action_data={
768 'data': {'token': token_data, 'user': user_data}},
797 'data': {'token': token_data, 'user': user_data}},
769 user=self._rhodecode_user, )
798 user=self._rhodecode_user, )
770 Session().commit()
799 Session().commit()
771
800
772 h.flash(_("Auth token successfully created"), category='success')
801 h.flash(_("Auth token successfully created"), category='success')
773 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
802 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
774
803
775 @LoginRequired()
804 @LoginRequired()
776 @HasPermissionAllDecorator('hg.admin')
805 @HasPermissionAllDecorator('hg.admin')
777 @CSRFRequired()
806 @CSRFRequired()
778 @view_config(
807 @view_config(
779 route_name='edit_user_auth_tokens_delete', request_method='POST')
808 route_name='edit_user_auth_tokens_delete', request_method='POST')
780 def auth_tokens_delete(self):
809 def auth_tokens_delete(self):
781 _ = self.request.translate
810 _ = self.request.translate
782 c = self.load_default_context()
811 c = self.load_default_context()
783
812
784 user_id = self.db_user_id
813 user_id = self.db_user_id
785 c.user = self.db_user
814 c.user = self.db_user
786
815
787 user_data = c.user.get_api_data()
816 user_data = c.user.get_api_data()
788
817
789 del_auth_token = self.request.POST.get('del_auth_token')
818 del_auth_token = self.request.POST.get('del_auth_token')
790
819
791 if del_auth_token:
820 if del_auth_token:
792 token = UserApiKeys.get_or_404(del_auth_token)
821 token = UserApiKeys.get_or_404(del_auth_token)
793 token_data = token.get_api_data()
822 token_data = token.get_api_data()
794
823
795 AuthTokenModel().delete(del_auth_token, c.user.user_id)
824 AuthTokenModel().delete(del_auth_token, c.user.user_id)
796 audit_logger.store_web(
825 audit_logger.store_web(
797 'user.edit.token.delete', action_data={
826 'user.edit.token.delete', action_data={
798 'data': {'token': token_data, 'user': user_data}},
827 'data': {'token': token_data, 'user': user_data}},
799 user=self._rhodecode_user,)
828 user=self._rhodecode_user,)
800 Session().commit()
829 Session().commit()
801 h.flash(_("Auth token successfully deleted"), category='success')
830 h.flash(_("Auth token successfully deleted"), category='success')
802
831
803 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
832 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
804
833
805 @LoginRequired()
834 @LoginRequired()
806 @HasPermissionAllDecorator('hg.admin')
835 @HasPermissionAllDecorator('hg.admin')
807 @view_config(
836 @view_config(
808 route_name='edit_user_ssh_keys', request_method='GET',
837 route_name='edit_user_ssh_keys', request_method='GET',
809 renderer='rhodecode:templates/admin/users/user_edit.mako')
838 renderer='rhodecode:templates/admin/users/user_edit.mako')
810 def ssh_keys(self):
839 def ssh_keys(self):
811 _ = self.request.translate
840 _ = self.request.translate
812 c = self.load_default_context()
841 c = self.load_default_context()
813 c.user = self.db_user
842 c.user = self.db_user
814
843
815 c.active = 'ssh_keys'
844 c.active = 'ssh_keys'
816 c.default_key = self.request.GET.get('default_key')
845 c.default_key = self.request.GET.get('default_key')
817 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
846 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
818 return self._get_template_context(c)
847 return self._get_template_context(c)
819
848
820 @LoginRequired()
849 @LoginRequired()
821 @HasPermissionAllDecorator('hg.admin')
850 @HasPermissionAllDecorator('hg.admin')
822 @view_config(
851 @view_config(
823 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
852 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
824 renderer='rhodecode:templates/admin/users/user_edit.mako')
853 renderer='rhodecode:templates/admin/users/user_edit.mako')
825 def ssh_keys_generate_keypair(self):
854 def ssh_keys_generate_keypair(self):
826 _ = self.request.translate
855 _ = self.request.translate
827 c = self.load_default_context()
856 c = self.load_default_context()
828
857
829 c.user = self.db_user
858 c.user = self.db_user
830
859
831 c.active = 'ssh_keys_generate'
860 c.active = 'ssh_keys_generate'
832 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
861 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
833 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
862 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
834
863
835 return self._get_template_context(c)
864 return self._get_template_context(c)
836
865
837 @LoginRequired()
866 @LoginRequired()
838 @HasPermissionAllDecorator('hg.admin')
867 @HasPermissionAllDecorator('hg.admin')
839 @CSRFRequired()
868 @CSRFRequired()
840 @view_config(
869 @view_config(
841 route_name='edit_user_ssh_keys_add', request_method='POST')
870 route_name='edit_user_ssh_keys_add', request_method='POST')
842 def ssh_keys_add(self):
871 def ssh_keys_add(self):
843 _ = self.request.translate
872 _ = self.request.translate
844 c = self.load_default_context()
873 c = self.load_default_context()
845
874
846 user_id = self.db_user_id
875 user_id = self.db_user_id
847 c.user = self.db_user
876 c.user = self.db_user
848
877
849 user_data = c.user.get_api_data()
878 user_data = c.user.get_api_data()
850 key_data = self.request.POST.get('key_data')
879 key_data = self.request.POST.get('key_data')
851 description = self.request.POST.get('description')
880 description = self.request.POST.get('description')
852
881
853 fingerprint = 'unknown'
882 fingerprint = 'unknown'
854 try:
883 try:
855 if not key_data:
884 if not key_data:
856 raise ValueError('Please add a valid public key')
885 raise ValueError('Please add a valid public key')
857
886
858 key = SshKeyModel().parse_key(key_data.strip())
887 key = SshKeyModel().parse_key(key_data.strip())
859 fingerprint = key.hash_md5()
888 fingerprint = key.hash_md5()
860
889
861 ssh_key = SshKeyModel().create(
890 ssh_key = SshKeyModel().create(
862 c.user.user_id, fingerprint, key.keydata, description)
891 c.user.user_id, fingerprint, key.keydata, description)
863 ssh_key_data = ssh_key.get_api_data()
892 ssh_key_data = ssh_key.get_api_data()
864
893
865 audit_logger.store_web(
894 audit_logger.store_web(
866 'user.edit.ssh_key.add', action_data={
895 'user.edit.ssh_key.add', action_data={
867 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
896 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
868 user=self._rhodecode_user, )
897 user=self._rhodecode_user, )
869 Session().commit()
898 Session().commit()
870
899
871 # Trigger an event on change of keys.
900 # Trigger an event on change of keys.
872 trigger(SshKeyFileChangeEvent(), self.request.registry)
901 trigger(SshKeyFileChangeEvent(), self.request.registry)
873
902
874 h.flash(_("Ssh Key successfully created"), category='success')
903 h.flash(_("Ssh Key successfully created"), category='success')
875
904
876 except IntegrityError:
905 except IntegrityError:
877 log.exception("Exception during ssh key saving")
906 log.exception("Exception during ssh key saving")
878 err = 'Such key with fingerprint `{}` already exists, ' \
907 err = 'Such key with fingerprint `{}` already exists, ' \
879 'please use a different one'.format(fingerprint)
908 'please use a different one'.format(fingerprint)
880 h.flash(_('An error occurred during ssh key saving: {}').format(err),
909 h.flash(_('An error occurred during ssh key saving: {}').format(err),
881 category='error')
910 category='error')
882 except Exception as e:
911 except Exception as e:
883 log.exception("Exception during ssh key saving")
912 log.exception("Exception during ssh key saving")
884 h.flash(_('An error occurred during ssh key saving: {}').format(e),
913 h.flash(_('An error occurred during ssh key saving: {}').format(e),
885 category='error')
914 category='error')
886
915
887 return HTTPFound(
916 return HTTPFound(
888 h.route_path('edit_user_ssh_keys', user_id=user_id))
917 h.route_path('edit_user_ssh_keys', user_id=user_id))
889
918
890 @LoginRequired()
919 @LoginRequired()
891 @HasPermissionAllDecorator('hg.admin')
920 @HasPermissionAllDecorator('hg.admin')
892 @CSRFRequired()
921 @CSRFRequired()
893 @view_config(
922 @view_config(
894 route_name='edit_user_ssh_keys_delete', request_method='POST')
923 route_name='edit_user_ssh_keys_delete', request_method='POST')
895 def ssh_keys_delete(self):
924 def ssh_keys_delete(self):
896 _ = self.request.translate
925 _ = self.request.translate
897 c = self.load_default_context()
926 c = self.load_default_context()
898
927
899 user_id = self.db_user_id
928 user_id = self.db_user_id
900 c.user = self.db_user
929 c.user = self.db_user
901
930
902 user_data = c.user.get_api_data()
931 user_data = c.user.get_api_data()
903
932
904 del_ssh_key = self.request.POST.get('del_ssh_key')
933 del_ssh_key = self.request.POST.get('del_ssh_key')
905
934
906 if del_ssh_key:
935 if del_ssh_key:
907 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
936 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
908 ssh_key_data = ssh_key.get_api_data()
937 ssh_key_data = ssh_key.get_api_data()
909
938
910 SshKeyModel().delete(del_ssh_key, c.user.user_id)
939 SshKeyModel().delete(del_ssh_key, c.user.user_id)
911 audit_logger.store_web(
940 audit_logger.store_web(
912 'user.edit.ssh_key.delete', action_data={
941 'user.edit.ssh_key.delete', action_data={
913 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
942 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
914 user=self._rhodecode_user,)
943 user=self._rhodecode_user,)
915 Session().commit()
944 Session().commit()
916 # Trigger an event on change of keys.
945 # Trigger an event on change of keys.
917 trigger(SshKeyFileChangeEvent(), self.request.registry)
946 trigger(SshKeyFileChangeEvent(), self.request.registry)
918 h.flash(_("Ssh key successfully deleted"), category='success')
947 h.flash(_("Ssh key successfully deleted"), category='success')
919
948
920 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
949 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
921
950
922 @LoginRequired()
951 @LoginRequired()
923 @HasPermissionAllDecorator('hg.admin')
952 @HasPermissionAllDecorator('hg.admin')
924 @view_config(
953 @view_config(
925 route_name='edit_user_emails', request_method='GET',
954 route_name='edit_user_emails', request_method='GET',
926 renderer='rhodecode:templates/admin/users/user_edit.mako')
955 renderer='rhodecode:templates/admin/users/user_edit.mako')
927 def emails(self):
956 def emails(self):
928 _ = self.request.translate
957 _ = self.request.translate
929 c = self.load_default_context()
958 c = self.load_default_context()
930 c.user = self.db_user
959 c.user = self.db_user
931
960
932 c.active = 'emails'
961 c.active = 'emails'
933 c.user_email_map = UserEmailMap.query() \
962 c.user_email_map = UserEmailMap.query() \
934 .filter(UserEmailMap.user == c.user).all()
963 .filter(UserEmailMap.user == c.user).all()
935
964
936 return self._get_template_context(c)
965 return self._get_template_context(c)
937
966
938 @LoginRequired()
967 @LoginRequired()
939 @HasPermissionAllDecorator('hg.admin')
968 @HasPermissionAllDecorator('hg.admin')
940 @CSRFRequired()
969 @CSRFRequired()
941 @view_config(
970 @view_config(
942 route_name='edit_user_emails_add', request_method='POST')
971 route_name='edit_user_emails_add', request_method='POST')
943 def emails_add(self):
972 def emails_add(self):
944 _ = self.request.translate
973 _ = self.request.translate
945 c = self.load_default_context()
974 c = self.load_default_context()
946
975
947 user_id = self.db_user_id
976 user_id = self.db_user_id
948 c.user = self.db_user
977 c.user = self.db_user
949
978
950 email = self.request.POST.get('new_email')
979 email = self.request.POST.get('new_email')
951 user_data = c.user.get_api_data()
980 user_data = c.user.get_api_data()
952 try:
981 try:
953
982
954 form = UserExtraEmailForm(self.request.translate)()
983 form = UserExtraEmailForm(self.request.translate)()
955 data = form.to_python({'email': email})
984 data = form.to_python({'email': email})
956 email = data['email']
985 email = data['email']
957
986
958 UserModel().add_extra_email(c.user.user_id, email)
987 UserModel().add_extra_email(c.user.user_id, email)
959 audit_logger.store_web(
988 audit_logger.store_web(
960 'user.edit.email.add',
989 'user.edit.email.add',
961 action_data={'email': email, 'user': user_data},
990 action_data={'email': email, 'user': user_data},
962 user=self._rhodecode_user)
991 user=self._rhodecode_user)
963 Session().commit()
992 Session().commit()
964 h.flash(_("Added new email address `%s` for user account") % email,
993 h.flash(_("Added new email address `%s` for user account") % email,
965 category='success')
994 category='success')
966 except formencode.Invalid as error:
995 except formencode.Invalid as error:
967 h.flash(h.escape(error.error_dict['email']), category='error')
996 h.flash(h.escape(error.error_dict['email']), category='error')
968 except IntegrityError:
997 except IntegrityError:
969 log.warning("Email %s already exists", email)
998 log.warning("Email %s already exists", email)
970 h.flash(_('Email `{}` is already registered for another user.').format(email),
999 h.flash(_('Email `{}` is already registered for another user.').format(email),
971 category='error')
1000 category='error')
972 except Exception:
1001 except Exception:
973 log.exception("Exception during email saving")
1002 log.exception("Exception during email saving")
974 h.flash(_('An error occurred during email saving'),
1003 h.flash(_('An error occurred during email saving'),
975 category='error')
1004 category='error')
976 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1005 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
977
1006
978 @LoginRequired()
1007 @LoginRequired()
979 @HasPermissionAllDecorator('hg.admin')
1008 @HasPermissionAllDecorator('hg.admin')
980 @CSRFRequired()
1009 @CSRFRequired()
981 @view_config(
1010 @view_config(
982 route_name='edit_user_emails_delete', request_method='POST')
1011 route_name='edit_user_emails_delete', request_method='POST')
983 def emails_delete(self):
1012 def emails_delete(self):
984 _ = self.request.translate
1013 _ = self.request.translate
985 c = self.load_default_context()
1014 c = self.load_default_context()
986
1015
987 user_id = self.db_user_id
1016 user_id = self.db_user_id
988 c.user = self.db_user
1017 c.user = self.db_user
989
1018
990 email_id = self.request.POST.get('del_email_id')
1019 email_id = self.request.POST.get('del_email_id')
991 user_model = UserModel()
1020 user_model = UserModel()
992
1021
993 email = UserEmailMap.query().get(email_id).email
1022 email = UserEmailMap.query().get(email_id).email
994 user_data = c.user.get_api_data()
1023 user_data = c.user.get_api_data()
995 user_model.delete_extra_email(c.user.user_id, email_id)
1024 user_model.delete_extra_email(c.user.user_id, email_id)
996 audit_logger.store_web(
1025 audit_logger.store_web(
997 'user.edit.email.delete',
1026 'user.edit.email.delete',
998 action_data={'email': email, 'user': user_data},
1027 action_data={'email': email, 'user': user_data},
999 user=self._rhodecode_user)
1028 user=self._rhodecode_user)
1000 Session().commit()
1029 Session().commit()
1001 h.flash(_("Removed email address from user account"),
1030 h.flash(_("Removed email address from user account"),
1002 category='success')
1031 category='success')
1003 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1032 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1004
1033
1005 @LoginRequired()
1034 @LoginRequired()
1006 @HasPermissionAllDecorator('hg.admin')
1035 @HasPermissionAllDecorator('hg.admin')
1007 @view_config(
1036 @view_config(
1008 route_name='edit_user_ips', request_method='GET',
1037 route_name='edit_user_ips', request_method='GET',
1009 renderer='rhodecode:templates/admin/users/user_edit.mako')
1038 renderer='rhodecode:templates/admin/users/user_edit.mako')
1010 def ips(self):
1039 def ips(self):
1011 _ = self.request.translate
1040 _ = self.request.translate
1012 c = self.load_default_context()
1041 c = self.load_default_context()
1013 c.user = self.db_user
1042 c.user = self.db_user
1014
1043
1015 c.active = 'ips'
1044 c.active = 'ips'
1016 c.user_ip_map = UserIpMap.query() \
1045 c.user_ip_map = UserIpMap.query() \
1017 .filter(UserIpMap.user == c.user).all()
1046 .filter(UserIpMap.user == c.user).all()
1018
1047
1019 c.inherit_default_ips = c.user.inherit_default_permissions
1048 c.inherit_default_ips = c.user.inherit_default_permissions
1020 c.default_user_ip_map = UserIpMap.query() \
1049 c.default_user_ip_map = UserIpMap.query() \
1021 .filter(UserIpMap.user == User.get_default_user()).all()
1050 .filter(UserIpMap.user == User.get_default_user()).all()
1022
1051
1023 return self._get_template_context(c)
1052 return self._get_template_context(c)
1024
1053
1025 @LoginRequired()
1054 @LoginRequired()
1026 @HasPermissionAllDecorator('hg.admin')
1055 @HasPermissionAllDecorator('hg.admin')
1027 @CSRFRequired()
1056 @CSRFRequired()
1028 @view_config(
1057 @view_config(
1029 route_name='edit_user_ips_add', request_method='POST')
1058 route_name='edit_user_ips_add', request_method='POST')
1030 # NOTE(marcink): this view is allowed for default users, as we can
1059 # NOTE(marcink): this view is allowed for default users, as we can
1031 # edit their IP white list
1060 # edit their IP white list
1032 def ips_add(self):
1061 def ips_add(self):
1033 _ = self.request.translate
1062 _ = self.request.translate
1034 c = self.load_default_context()
1063 c = self.load_default_context()
1035
1064
1036 user_id = self.db_user_id
1065 user_id = self.db_user_id
1037 c.user = self.db_user
1066 c.user = self.db_user
1038
1067
1039 user_model = UserModel()
1068 user_model = UserModel()
1040 desc = self.request.POST.get('description')
1069 desc = self.request.POST.get('description')
1041 try:
1070 try:
1042 ip_list = user_model.parse_ip_range(
1071 ip_list = user_model.parse_ip_range(
1043 self.request.POST.get('new_ip'))
1072 self.request.POST.get('new_ip'))
1044 except Exception as e:
1073 except Exception as e:
1045 ip_list = []
1074 ip_list = []
1046 log.exception("Exception during ip saving")
1075 log.exception("Exception during ip saving")
1047 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1076 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1048 category='error')
1077 category='error')
1049 added = []
1078 added = []
1050 user_data = c.user.get_api_data()
1079 user_data = c.user.get_api_data()
1051 for ip in ip_list:
1080 for ip in ip_list:
1052 try:
1081 try:
1053 form = UserExtraIpForm(self.request.translate)()
1082 form = UserExtraIpForm(self.request.translate)()
1054 data = form.to_python({'ip': ip})
1083 data = form.to_python({'ip': ip})
1055 ip = data['ip']
1084 ip = data['ip']
1056
1085
1057 user_model.add_extra_ip(c.user.user_id, ip, desc)
1086 user_model.add_extra_ip(c.user.user_id, ip, desc)
1058 audit_logger.store_web(
1087 audit_logger.store_web(
1059 'user.edit.ip.add',
1088 'user.edit.ip.add',
1060 action_data={'ip': ip, 'user': user_data},
1089 action_data={'ip': ip, 'user': user_data},
1061 user=self._rhodecode_user)
1090 user=self._rhodecode_user)
1062 Session().commit()
1091 Session().commit()
1063 added.append(ip)
1092 added.append(ip)
1064 except formencode.Invalid as error:
1093 except formencode.Invalid as error:
1065 msg = error.error_dict['ip']
1094 msg = error.error_dict['ip']
1066 h.flash(msg, category='error')
1095 h.flash(msg, category='error')
1067 except Exception:
1096 except Exception:
1068 log.exception("Exception during ip saving")
1097 log.exception("Exception during ip saving")
1069 h.flash(_('An error occurred during ip saving'),
1098 h.flash(_('An error occurred during ip saving'),
1070 category='error')
1099 category='error')
1071 if added:
1100 if added:
1072 h.flash(
1101 h.flash(
1073 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1102 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1074 category='success')
1103 category='success')
1075 if 'default_user' in self.request.POST:
1104 if 'default_user' in self.request.POST:
1076 # case for editing global IP list we do it for 'DEFAULT' user
1105 # case for editing global IP list we do it for 'DEFAULT' user
1077 raise HTTPFound(h.route_path('admin_permissions_ips'))
1106 raise HTTPFound(h.route_path('admin_permissions_ips'))
1078 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1107 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1079
1108
1080 @LoginRequired()
1109 @LoginRequired()
1081 @HasPermissionAllDecorator('hg.admin')
1110 @HasPermissionAllDecorator('hg.admin')
1082 @CSRFRequired()
1111 @CSRFRequired()
1083 @view_config(
1112 @view_config(
1084 route_name='edit_user_ips_delete', request_method='POST')
1113 route_name='edit_user_ips_delete', request_method='POST')
1085 # NOTE(marcink): this view is allowed for default users, as we can
1114 # NOTE(marcink): this view is allowed for default users, as we can
1086 # edit their IP white list
1115 # edit their IP white list
1087 def ips_delete(self):
1116 def ips_delete(self):
1088 _ = self.request.translate
1117 _ = self.request.translate
1089 c = self.load_default_context()
1118 c = self.load_default_context()
1090
1119
1091 user_id = self.db_user_id
1120 user_id = self.db_user_id
1092 c.user = self.db_user
1121 c.user = self.db_user
1093
1122
1094 ip_id = self.request.POST.get('del_ip_id')
1123 ip_id = self.request.POST.get('del_ip_id')
1095 user_model = UserModel()
1124 user_model = UserModel()
1096 user_data = c.user.get_api_data()
1125 user_data = c.user.get_api_data()
1097 ip = UserIpMap.query().get(ip_id).ip_addr
1126 ip = UserIpMap.query().get(ip_id).ip_addr
1098 user_model.delete_extra_ip(c.user.user_id, ip_id)
1127 user_model.delete_extra_ip(c.user.user_id, ip_id)
1099 audit_logger.store_web(
1128 audit_logger.store_web(
1100 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1129 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1101 user=self._rhodecode_user)
1130 user=self._rhodecode_user)
1102 Session().commit()
1131 Session().commit()
1103 h.flash(_("Removed ip address from user whitelist"), category='success')
1132 h.flash(_("Removed ip address from user whitelist"), category='success')
1104
1133
1105 if 'default_user' in self.request.POST:
1134 if 'default_user' in self.request.POST:
1106 # case for editing global IP list we do it for 'DEFAULT' user
1135 # case for editing global IP list we do it for 'DEFAULT' user
1107 raise HTTPFound(h.route_path('admin_permissions_ips'))
1136 raise HTTPFound(h.route_path('admin_permissions_ips'))
1108 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1137 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1109
1138
1110 @LoginRequired()
1139 @LoginRequired()
1111 @HasPermissionAllDecorator('hg.admin')
1140 @HasPermissionAllDecorator('hg.admin')
1112 @view_config(
1141 @view_config(
1113 route_name='edit_user_groups_management', request_method='GET',
1142 route_name='edit_user_groups_management', request_method='GET',
1114 renderer='rhodecode:templates/admin/users/user_edit.mako')
1143 renderer='rhodecode:templates/admin/users/user_edit.mako')
1115 def groups_management(self):
1144 def groups_management(self):
1116 c = self.load_default_context()
1145 c = self.load_default_context()
1117 c.user = self.db_user
1146 c.user = self.db_user
1118 c.data = c.user.group_member
1147 c.data = c.user.group_member
1119
1148
1120 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1149 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1121 for group in c.user.group_member]
1150 for group in c.user.group_member]
1122 c.groups = json.dumps(groups)
1151 c.groups = json.dumps(groups)
1123 c.active = 'groups'
1152 c.active = 'groups'
1124
1153
1125 return self._get_template_context(c)
1154 return self._get_template_context(c)
1126
1155
1127 @LoginRequired()
1156 @LoginRequired()
1128 @HasPermissionAllDecorator('hg.admin')
1157 @HasPermissionAllDecorator('hg.admin')
1129 @CSRFRequired()
1158 @CSRFRequired()
1130 @view_config(
1159 @view_config(
1131 route_name='edit_user_groups_management_updates', request_method='POST')
1160 route_name='edit_user_groups_management_updates', request_method='POST')
1132 def groups_management_updates(self):
1161 def groups_management_updates(self):
1133 _ = self.request.translate
1162 _ = self.request.translate
1134 c = self.load_default_context()
1163 c = self.load_default_context()
1135
1164
1136 user_id = self.db_user_id
1165 user_id = self.db_user_id
1137 c.user = self.db_user
1166 c.user = self.db_user
1138
1167
1139 user_groups = set(self.request.POST.getall('users_group_id'))
1168 user_groups = set(self.request.POST.getall('users_group_id'))
1140 user_groups_objects = []
1169 user_groups_objects = []
1141
1170
1142 for ugid in user_groups:
1171 for ugid in user_groups:
1143 user_groups_objects.append(
1172 user_groups_objects.append(
1144 UserGroupModel().get_group(safe_int(ugid)))
1173 UserGroupModel().get_group(safe_int(ugid)))
1145 user_group_model = UserGroupModel()
1174 user_group_model = UserGroupModel()
1146 added_to_groups, removed_from_groups = \
1175 added_to_groups, removed_from_groups = \
1147 user_group_model.change_groups(c.user, user_groups_objects)
1176 user_group_model.change_groups(c.user, user_groups_objects)
1148
1177
1149 user_data = c.user.get_api_data()
1178 user_data = c.user.get_api_data()
1150 for user_group_id in added_to_groups:
1179 for user_group_id in added_to_groups:
1151 user_group = UserGroup.get(user_group_id)
1180 user_group = UserGroup.get(user_group_id)
1152 old_values = user_group.get_api_data()
1181 old_values = user_group.get_api_data()
1153 audit_logger.store_web(
1182 audit_logger.store_web(
1154 'user_group.edit.member.add',
1183 'user_group.edit.member.add',
1155 action_data={'user': user_data, 'old_data': old_values},
1184 action_data={'user': user_data, 'old_data': old_values},
1156 user=self._rhodecode_user)
1185 user=self._rhodecode_user)
1157
1186
1158 for user_group_id in removed_from_groups:
1187 for user_group_id in removed_from_groups:
1159 user_group = UserGroup.get(user_group_id)
1188 user_group = UserGroup.get(user_group_id)
1160 old_values = user_group.get_api_data()
1189 old_values = user_group.get_api_data()
1161 audit_logger.store_web(
1190 audit_logger.store_web(
1162 'user_group.edit.member.delete',
1191 'user_group.edit.member.delete',
1163 action_data={'user': user_data, 'old_data': old_values},
1192 action_data={'user': user_data, 'old_data': old_values},
1164 user=self._rhodecode_user)
1193 user=self._rhodecode_user)
1165
1194
1166 Session().commit()
1195 Session().commit()
1167 c.active = 'user_groups_management'
1196 c.active = 'user_groups_management'
1168 h.flash(_("Groups successfully changed"), category='success')
1197 h.flash(_("Groups successfully changed"), category='success')
1169
1198
1170 return HTTPFound(h.route_path(
1199 return HTTPFound(h.route_path(
1171 'edit_user_groups_management', user_id=user_id))
1200 'edit_user_groups_management', user_id=user_id))
1172
1201
1173 @LoginRequired()
1202 @LoginRequired()
1174 @HasPermissionAllDecorator('hg.admin')
1203 @HasPermissionAllDecorator('hg.admin')
1175 @view_config(
1204 @view_config(
1176 route_name='edit_user_audit_logs', request_method='GET',
1205 route_name='edit_user_audit_logs', request_method='GET',
1177 renderer='rhodecode:templates/admin/users/user_edit.mako')
1206 renderer='rhodecode:templates/admin/users/user_edit.mako')
1178 def user_audit_logs(self):
1207 def user_audit_logs(self):
1179 _ = self.request.translate
1208 _ = self.request.translate
1180 c = self.load_default_context()
1209 c = self.load_default_context()
1181 c.user = self.db_user
1210 c.user = self.db_user
1182
1211
1183 c.active = 'audit'
1212 c.active = 'audit'
1184
1213
1185 p = safe_int(self.request.GET.get('page', 1), 1)
1214 p = safe_int(self.request.GET.get('page', 1), 1)
1186
1215
1187 filter_term = self.request.GET.get('filter')
1216 filter_term = self.request.GET.get('filter')
1188 user_log = UserModel().get_user_log(c.user, filter_term)
1217 user_log = UserModel().get_user_log(c.user, filter_term)
1189
1218
1190 def url_generator(**kw):
1219 def url_generator(**kw):
1191 if filter_term:
1220 if filter_term:
1192 kw['filter'] = filter_term
1221 kw['filter'] = filter_term
1193 return self.request.current_route_path(_query=kw)
1222 return self.request.current_route_path(_query=kw)
1194
1223
1195 c.audit_logs = h.Page(
1224 c.audit_logs = h.Page(
1196 user_log, page=p, items_per_page=10, url=url_generator)
1225 user_log, page=p, items_per_page=10, url=url_generator)
1197 c.filter_term = filter_term
1226 c.filter_term = filter_term
1198 return self._get_template_context(c)
1227 return self._get_template_context(c)
1199
1228
1200 @LoginRequired()
1229 @LoginRequired()
1201 @HasPermissionAllDecorator('hg.admin')
1230 @HasPermissionAllDecorator('hg.admin')
1202 @view_config(
1231 @view_config(
1203 route_name='edit_user_audit_logs_download', request_method='GET',
1232 route_name='edit_user_audit_logs_download', request_method='GET',
1204 renderer='string')
1233 renderer='string')
1205 def user_audit_logs_download(self):
1234 def user_audit_logs_download(self):
1206 _ = self.request.translate
1235 _ = self.request.translate
1207 c = self.load_default_context()
1236 c = self.load_default_context()
1208 c.user = self.db_user
1237 c.user = self.db_user
1209
1238
1210 user_log = UserModel().get_user_log(c.user, filter_term=None)
1239 user_log = UserModel().get_user_log(c.user, filter_term=None)
1211
1240
1212 audit_log_data = {}
1241 audit_log_data = {}
1213 for entry in user_log:
1242 for entry in user_log:
1214 audit_log_data[entry.user_log_id] = entry.get_dict()
1243 audit_log_data[entry.user_log_id] = entry.get_dict()
1215
1244
1216 response = Response(json.dumps(audit_log_data, indent=4))
1245 response = Response(json.dumps(audit_log_data, indent=4))
1217 response.content_disposition = str(
1246 response.content_disposition = str(
1218 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id))
1247 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id))
1219 response.content_type = 'application/json'
1248 response.content_type = 'application/json'
1220
1249
1221 return response
1250 return response
1222
1251
1223 @LoginRequired()
1252 @LoginRequired()
1224 @HasPermissionAllDecorator('hg.admin')
1253 @HasPermissionAllDecorator('hg.admin')
1225 @view_config(
1254 @view_config(
1226 route_name='edit_user_perms_summary', request_method='GET',
1255 route_name='edit_user_perms_summary', request_method='GET',
1227 renderer='rhodecode:templates/admin/users/user_edit.mako')
1256 renderer='rhodecode:templates/admin/users/user_edit.mako')
1228 def user_perms_summary(self):
1257 def user_perms_summary(self):
1229 _ = self.request.translate
1258 _ = self.request.translate
1230 c = self.load_default_context()
1259 c = self.load_default_context()
1231 c.user = self.db_user
1260 c.user = self.db_user
1232
1261
1233 c.active = 'perms_summary'
1262 c.active = 'perms_summary'
1234 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1263 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1235
1264
1236 return self._get_template_context(c)
1265 return self._get_template_context(c)
1237
1266
1238 @LoginRequired()
1267 @LoginRequired()
1239 @HasPermissionAllDecorator('hg.admin')
1268 @HasPermissionAllDecorator('hg.admin')
1240 @view_config(
1269 @view_config(
1241 route_name='edit_user_perms_summary_json', request_method='GET',
1270 route_name='edit_user_perms_summary_json', request_method='GET',
1242 renderer='json_ext')
1271 renderer='json_ext')
1243 def user_perms_summary_json(self):
1272 def user_perms_summary_json(self):
1244 self.load_default_context()
1273 self.load_default_context()
1245 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1274 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1246
1275
1247 return perm_user.permissions
1276 return perm_user.permissions
1248
1277
1249 @LoginRequired()
1278 @LoginRequired()
1250 @HasPermissionAllDecorator('hg.admin')
1279 @HasPermissionAllDecorator('hg.admin')
1251 @view_config(
1280 @view_config(
1252 route_name='edit_user_caches', request_method='GET',
1281 route_name='edit_user_caches', request_method='GET',
1253 renderer='rhodecode:templates/admin/users/user_edit.mako')
1282 renderer='rhodecode:templates/admin/users/user_edit.mako')
1254 def user_caches(self):
1283 def user_caches(self):
1255 _ = self.request.translate
1284 _ = self.request.translate
1256 c = self.load_default_context()
1285 c = self.load_default_context()
1257 c.user = self.db_user
1286 c.user = self.db_user
1258
1287
1259 c.active = 'caches'
1288 c.active = 'caches'
1260 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1289 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1261
1290
1262 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1291 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1263 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1292 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1264 c.backend = c.region.backend
1293 c.backend = c.region.backend
1265 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1294 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1266
1295
1267 return self._get_template_context(c)
1296 return self._get_template_context(c)
1268
1297
1269 @LoginRequired()
1298 @LoginRequired()
1270 @HasPermissionAllDecorator('hg.admin')
1299 @HasPermissionAllDecorator('hg.admin')
1271 @CSRFRequired()
1300 @CSRFRequired()
1272 @view_config(
1301 @view_config(
1273 route_name='edit_user_caches_update', request_method='POST')
1302 route_name='edit_user_caches_update', request_method='POST')
1274 def user_caches_update(self):
1303 def user_caches_update(self):
1275 _ = self.request.translate
1304 _ = self.request.translate
1276 c = self.load_default_context()
1305 c = self.load_default_context()
1277 c.user = self.db_user
1306 c.user = self.db_user
1278
1307
1279 c.active = 'caches'
1308 c.active = 'caches'
1280 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1309 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1281
1310
1282 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1311 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1283 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1312 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1284
1313
1285 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1314 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1286
1315
1287 return HTTPFound(h.route_path(
1316 return HTTPFound(h.route_path(
1288 'edit_user_caches', user_id=c.user.user_id))
1317 'edit_user_caches', user_id=c.user.user_id))
@@ -1,230 +1,231 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
21 import colander
22 import logging
22 import logging
23
23
24 from rhodecode.translation import _
24 from rhodecode.translation import _
25 from rhodecode.authentication.base import (
25 from rhodecode.authentication.base import (
26 RhodeCodeExternalAuthPlugin, hybrid_property)
26 RhodeCodeExternalAuthPlugin, hybrid_property)
27 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
27 from rhodecode.authentication.schema import AuthnPluginSettingsSchemaBase
28 from rhodecode.authentication.routes import AuthnPluginResourceBase
28 from rhodecode.authentication.routes import AuthnPluginResourceBase
29 from rhodecode.lib.colander_utils import strip_whitespace
29 from rhodecode.lib.colander_utils import strip_whitespace
30 from rhodecode.lib.utils2 import str2bool, safe_unicode
30 from rhodecode.lib.utils2 import str2bool, safe_unicode
31 from rhodecode.model.db import User
31 from rhodecode.model.db import User
32
32
33
33
34 log = logging.getLogger(__name__)
34 log = logging.getLogger(__name__)
35
35
36
36
37 def plugin_factory(plugin_id, *args, **kwargs):
37 def plugin_factory(plugin_id, *args, **kwargs):
38 """
38 """
39 Factory function that is called during plugin discovery.
39 Factory function that is called during plugin discovery.
40 It returns the plugin instance.
40 It returns the plugin instance.
41 """
41 """
42 plugin = RhodeCodeAuthPlugin(plugin_id)
42 plugin = RhodeCodeAuthPlugin(plugin_id)
43 return plugin
43 return plugin
44
44
45
45
46 class HeadersAuthnResource(AuthnPluginResourceBase):
46 class HeadersAuthnResource(AuthnPluginResourceBase):
47 pass
47 pass
48
48
49
49
50 class HeadersSettingsSchema(AuthnPluginSettingsSchemaBase):
50 class HeadersSettingsSchema(AuthnPluginSettingsSchemaBase):
51 header = colander.SchemaNode(
51 header = colander.SchemaNode(
52 colander.String(),
52 colander.String(),
53 default='REMOTE_USER',
53 default='REMOTE_USER',
54 description=_('Header to extract the user from'),
54 description=_('Header to extract the user from'),
55 preparer=strip_whitespace,
55 preparer=strip_whitespace,
56 title=_('Header'),
56 title=_('Header'),
57 widget='string')
57 widget='string')
58 fallback_header = colander.SchemaNode(
58 fallback_header = colander.SchemaNode(
59 colander.String(),
59 colander.String(),
60 default='HTTP_X_FORWARDED_USER',
60 default='HTTP_X_FORWARDED_USER',
61 description=_('Header to extract the user from when main one fails'),
61 description=_('Header to extract the user from when main one fails'),
62 preparer=strip_whitespace,
62 preparer=strip_whitespace,
63 title=_('Fallback header'),
63 title=_('Fallback header'),
64 widget='string')
64 widget='string')
65 clean_username = colander.SchemaNode(
65 clean_username = colander.SchemaNode(
66 colander.Boolean(),
66 colander.Boolean(),
67 default=True,
67 default=True,
68 description=_('Perform cleaning of user, if passed user has @ in '
68 description=_('Perform cleaning of user, if passed user has @ in '
69 'username then first part before @ is taken. '
69 'username then first part before @ is taken. '
70 'If there\'s \\ in the username only the part after '
70 'If there\'s \\ in the username only the part after '
71 ' \\ is taken'),
71 ' \\ is taken'),
72 missing=False,
72 missing=False,
73 title=_('Clean username'),
73 title=_('Clean username'),
74 widget='bool')
74 widget='bool')
75
75
76
76
77 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
77 class RhodeCodeAuthPlugin(RhodeCodeExternalAuthPlugin):
78 uid = 'headers'
78 uid = 'headers'
79
79 def includeme(self, config):
80 def includeme(self, config):
80 config.add_authn_plugin(self)
81 config.add_authn_plugin(self)
81 config.add_authn_resource(self.get_id(), HeadersAuthnResource(self))
82 config.add_authn_resource(self.get_id(), HeadersAuthnResource(self))
82 config.add_view(
83 config.add_view(
83 'rhodecode.authentication.views.AuthnPluginViewBase',
84 'rhodecode.authentication.views.AuthnPluginViewBase',
84 attr='settings_get',
85 attr='settings_get',
85 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
86 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
86 request_method='GET',
87 request_method='GET',
87 route_name='auth_home',
88 route_name='auth_home',
88 context=HeadersAuthnResource)
89 context=HeadersAuthnResource)
89 config.add_view(
90 config.add_view(
90 'rhodecode.authentication.views.AuthnPluginViewBase',
91 'rhodecode.authentication.views.AuthnPluginViewBase',
91 attr='settings_post',
92 attr='settings_post',
92 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
93 renderer='rhodecode:templates/admin/auth/plugin_settings.mako',
93 request_method='POST',
94 request_method='POST',
94 route_name='auth_home',
95 route_name='auth_home',
95 context=HeadersAuthnResource)
96 context=HeadersAuthnResource)
96
97
97 def get_display_name(self):
98 def get_display_name(self):
98 return _('Headers')
99 return _('Headers')
99
100
100 def get_settings_schema(self):
101 def get_settings_schema(self):
101 return HeadersSettingsSchema()
102 return HeadersSettingsSchema()
102
103
103 @hybrid_property
104 @hybrid_property
104 def name(self):
105 def name(self):
105 return u"headers"
106 return u"headers"
106
107
107 @property
108 @property
108 def is_headers_auth(self):
109 def is_headers_auth(self):
109 return True
110 return True
110
111
111 def use_fake_password(self):
112 def use_fake_password(self):
112 return True
113 return True
113
114
114 def user_activation_state(self):
115 def user_activation_state(self):
115 def_user_perms = User.get_default_user().AuthUser().permissions['global']
116 def_user_perms = User.get_default_user().AuthUser().permissions['global']
116 return 'hg.extern_activate.auto' in def_user_perms
117 return 'hg.extern_activate.auto' in def_user_perms
117
118
118 def _clean_username(self, username):
119 def _clean_username(self, username):
119 # Removing realm and domain from username
120 # Removing realm and domain from username
120 username = username.split('@')[0]
121 username = username.split('@')[0]
121 username = username.rsplit('\\')[-1]
122 username = username.rsplit('\\')[-1]
122 return username
123 return username
123
124
124 def _get_username(self, environ, settings):
125 def _get_username(self, environ, settings):
125 username = None
126 username = None
126 environ = environ or {}
127 environ = environ or {}
127 if not environ:
128 if not environ:
128 log.debug('got empty environ: %s', environ)
129 log.debug('got empty environ: %s', environ)
129
130
130 settings = settings or {}
131 settings = settings or {}
131 if settings.get('header'):
132 if settings.get('header'):
132 header = settings.get('header')
133 header = settings.get('header')
133 username = environ.get(header)
134 username = environ.get(header)
134 log.debug('extracted %s:%s', header, username)
135 log.debug('extracted %s:%s', header, username)
135
136
136 # fallback mode
137 # fallback mode
137 if not username and settings.get('fallback_header'):
138 if not username and settings.get('fallback_header'):
138 header = settings.get('fallback_header')
139 header = settings.get('fallback_header')
139 username = environ.get(header)
140 username = environ.get(header)
140 log.debug('extracted %s:%s', header, username)
141 log.debug('extracted %s:%s', header, username)
141
142
142 if username and str2bool(settings.get('clean_username')):
143 if username and str2bool(settings.get('clean_username')):
143 log.debug('Received username `%s` from headers', username)
144 log.debug('Received username `%s` from headers', username)
144 username = self._clean_username(username)
145 username = self._clean_username(username)
145 log.debug('New cleanup user is:%s', username)
146 log.debug('New cleanup user is:%s', username)
146 return username
147 return username
147
148
148 def get_user(self, username=None, **kwargs):
149 def get_user(self, username=None, **kwargs):
149 """
150 """
150 Helper method for user fetching in plugins, by default it's using
151 Helper method for user fetching in plugins, by default it's using
151 simple fetch by username, but this method can be custimized in plugins
152 simple fetch by username, but this method can be custimized in plugins
152 eg. headers auth plugin to fetch user by environ params
153 eg. headers auth plugin to fetch user by environ params
153 :param username: username if given to fetch
154 :param username: username if given to fetch
154 :param kwargs: extra arguments needed for user fetching.
155 :param kwargs: extra arguments needed for user fetching.
155 """
156 """
156 environ = kwargs.get('environ') or {}
157 environ = kwargs.get('environ') or {}
157 settings = kwargs.get('settings') or {}
158 settings = kwargs.get('settings') or {}
158 username = self._get_username(environ, settings)
159 username = self._get_username(environ, settings)
159 # we got the username, so use default method now
160 # we got the username, so use default method now
160 return super(RhodeCodeAuthPlugin, self).get_user(username)
161 return super(RhodeCodeAuthPlugin, self).get_user(username)
161
162
162 def auth(self, userobj, username, password, settings, **kwargs):
163 def auth(self, userobj, username, password, settings, **kwargs):
163 """
164 """
164 Get's the headers_auth username (or email). It tries to get username
165 Get's the headers_auth username (or email). It tries to get username
165 from REMOTE_USER if this plugin is enabled, if that fails
166 from REMOTE_USER if this plugin is enabled, if that fails
166 it tries to get username from HTTP_X_FORWARDED_USER if fallback header
167 it tries to get username from HTTP_X_FORWARDED_USER if fallback header
167 is set. clean_username extracts the username from this data if it's
168 is set. clean_username extracts the username from this data if it's
168 having @ in it.
169 having @ in it.
169 Return None on failure. On success, return a dictionary of the form:
170 Return None on failure. On success, return a dictionary of the form:
170
171
171 see: RhodeCodeAuthPluginBase.auth_func_attrs
172 see: RhodeCodeAuthPluginBase.auth_func_attrs
172
173
173 :param userobj:
174 :param userobj:
174 :param username:
175 :param username:
175 :param password:
176 :param password:
176 :param settings:
177 :param settings:
177 :param kwargs:
178 :param kwargs:
178 """
179 """
179 environ = kwargs.get('environ')
180 environ = kwargs.get('environ')
180 if not environ:
181 if not environ:
181 log.debug('Empty environ data skipping...')
182 log.debug('Empty environ data skipping...')
182 return None
183 return None
183
184
184 if not userobj:
185 if not userobj:
185 userobj = self.get_user('', environ=environ, settings=settings)
186 userobj = self.get_user('', environ=environ, settings=settings)
186
187
187 # we don't care passed username/password for headers auth plugins.
188 # we don't care passed username/password for headers auth plugins.
188 # only way to log in is using environ
189 # only way to log in is using environ
189 username = None
190 username = None
190 if userobj:
191 if userobj:
191 username = getattr(userobj, 'username')
192 username = getattr(userobj, 'username')
192
193
193 if not username:
194 if not username:
194 # we don't have any objects in DB user doesn't exist extract
195 # we don't have any objects in DB user doesn't exist extract
195 # username from environ based on the settings
196 # username from environ based on the settings
196 username = self._get_username(environ, settings)
197 username = self._get_username(environ, settings)
197
198
198 # if cannot fetch username, it's a no-go for this plugin to proceed
199 # if cannot fetch username, it's a no-go for this plugin to proceed
199 if not username:
200 if not username:
200 return None
201 return None
201
202
202 # old attrs fetched from RhodeCode database
203 # old attrs fetched from RhodeCode database
203 admin = getattr(userobj, 'admin', False)
204 admin = getattr(userobj, 'admin', False)
204 active = getattr(userobj, 'active', True)
205 active = getattr(userobj, 'active', True)
205 email = getattr(userobj, 'email', '')
206 email = getattr(userobj, 'email', '')
206 firstname = getattr(userobj, 'firstname', '')
207 firstname = getattr(userobj, 'firstname', '')
207 lastname = getattr(userobj, 'lastname', '')
208 lastname = getattr(userobj, 'lastname', '')
208 extern_type = getattr(userobj, 'extern_type', '')
209 extern_type = getattr(userobj, 'extern_type', '')
209
210
210 user_attrs = {
211 user_attrs = {
211 'username': username,
212 'username': username,
212 'firstname': safe_unicode(firstname or username),
213 'firstname': safe_unicode(firstname or username),
213 'lastname': safe_unicode(lastname or ''),
214 'lastname': safe_unicode(lastname or ''),
214 'groups': [],
215 'groups': [],
215 'user_group_sync': False,
216 'user_group_sync': False,
216 'email': email or '',
217 'email': email or '',
217 'admin': admin or False,
218 'admin': admin or False,
218 'active': active,
219 'active': active,
219 'active_from_extern': True,
220 'active_from_extern': True,
220 'extern_name': username,
221 'extern_name': username,
221 'extern_type': extern_type,
222 'extern_type': extern_type,
222 }
223 }
223
224
224 log.info('user `%s` authenticated correctly', user_attrs['username'])
225 log.info('user `%s` authenticated correctly', user_attrs['username'])
225 return user_attrs
226 return user_attrs
226
227
227
228
228 def includeme(config):
229 def includeme(config):
229 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
230 plugin_id = 'egg:rhodecode-enterprise-ce#{}'.format(RhodeCodeAuthPlugin.uid)
230 plugin_factory(plugin_id).includeme(config)
231 plugin_factory(plugin_id).includeme(config)
@@ -1,89 +1,94 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2019 RhodeCode GmbH
3 # Copyright (C) 2012-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.exceptions import ConfigurationError
23 from pyramid.exceptions import ConfigurationError
24 from zope.interface import implementer
24 from zope.interface import implementer
25
25
26 from rhodecode.authentication.interface import IAuthnPluginRegistry
26 from rhodecode.authentication.interface import IAuthnPluginRegistry
27 from rhodecode.lib.utils2 import safe_str
27 from rhodecode.lib.utils2 import safe_str
28 from rhodecode.model.settings import SettingsModel
28 from rhodecode.model.settings import SettingsModel
29
29
30 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
31
31
32
32
33 @implementer(IAuthnPluginRegistry)
33 @implementer(IAuthnPluginRegistry)
34 class AuthenticationPluginRegistry(object):
34 class AuthenticationPluginRegistry(object):
35
35
36 # INI settings key to set a fallback authentication plugin.
36 # INI settings key to set a fallback authentication plugin.
37 fallback_plugin_key = 'rhodecode.auth_plugin_fallback'
37 fallback_plugin_key = 'rhodecode.auth_plugin_fallback'
38
38
39 def __init__(self, settings):
39 def __init__(self, settings):
40 self._plugins = {}
40 self._plugins = {}
41 self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
41 self._fallback_plugin = settings.get(self.fallback_plugin_key, None)
42
42
43 def add_authn_plugin(self, config, plugin):
43 def add_authn_plugin(self, config, plugin):
44 plugin_id = plugin.get_id()
44 plugin_id = plugin.get_id()
45 if plugin_id in self._plugins.keys():
45 if plugin_id in self._plugins.keys():
46 raise ConfigurationError(
46 raise ConfigurationError(
47 'Cannot register authentication plugin twice: "%s"', plugin_id)
47 'Cannot register authentication plugin twice: "%s"', plugin_id)
48 else:
48 else:
49 log.debug('Register authentication plugin: "%s"', plugin_id)
49 log.debug('Register authentication plugin: "%s"', plugin_id)
50 self._plugins[plugin_id] = plugin
50 self._plugins[plugin_id] = plugin
51
51
52 def get_plugins(self):
52 def get_plugins(self):
53 def sort_key(plugin):
53 def sort_key(plugin):
54 return str.lower(safe_str(plugin.get_display_name()))
54 return str.lower(safe_str(plugin.get_display_name()))
55
55
56 return sorted(self._plugins.values(), key=sort_key)
56 return sorted(self._plugins.values(), key=sort_key)
57
57
58 def get_plugin(self, plugin_id):
58 def get_plugin(self, plugin_id):
59 return self._plugins.get(plugin_id, None)
59 return self._plugins.get(plugin_id, None)
60
60
61 def get_plugin_by_uid(self, plugin_uid):
62 for plugin in self._plugins.values():
63 if plugin.uid == plugin_uid:
64 return plugin
65
61 def get_plugins_for_authentication(self):
66 def get_plugins_for_authentication(self):
62 """
67 """
63 Returns a list of plugins which should be consulted when authenticating
68 Returns a list of plugins which should be consulted when authenticating
64 a user. It only returns plugins which are enabled and active.
69 a user. It only returns plugins which are enabled and active.
65 Additionally it includes the fallback plugin from the INI file, if
70 Additionally it includes the fallback plugin from the INI file, if
66 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
71 `rhodecode.auth_plugin_fallback` is set to a plugin ID.
67 """
72 """
68 plugins = []
73 plugins = []
69
74
70 # Add all enabled and active plugins to the list. We iterate over the
75 # Add all enabled and active plugins to the list. We iterate over the
71 # auth_plugins setting from DB because it also represents the ordering.
76 # auth_plugins setting from DB because it also represents the ordering.
72 enabled_plugins = SettingsModel().get_auth_plugins()
77 enabled_plugins = SettingsModel().get_auth_plugins()
73 raw_settings = SettingsModel().get_all_settings()
78 raw_settings = SettingsModel().get_all_settings()
74 for plugin_id in enabled_plugins:
79 for plugin_id in enabled_plugins:
75 plugin = self.get_plugin(plugin_id)
80 plugin = self.get_plugin(plugin_id)
76 if plugin is not None and plugin.is_active(
81 if plugin is not None and plugin.is_active(
77 plugin_cached_settings=raw_settings):
82 plugin_cached_settings=raw_settings):
78 plugins.append(plugin)
83 plugins.append(plugin)
79
84
80 # Add the fallback plugin from ini file.
85 # Add the fallback plugin from ini file.
81 if self._fallback_plugin:
86 if self._fallback_plugin:
82 log.warn(
87 log.warn(
83 'Using fallback authentication plugin from INI file: "%s"',
88 'Using fallback authentication plugin from INI file: "%s"',
84 self._fallback_plugin)
89 self._fallback_plugin)
85 plugin = self.get_plugin(self._fallback_plugin)
90 plugin = self.get_plugin(self._fallback_plugin)
86 if plugin is not None and plugin not in plugins:
91 if plugin is not None and plugin not in plugins:
87 plugins.append(plugin)
92 plugins.append(plugin)
88
93
89 return plugins
94 return plugins
@@ -1,150 +1,147 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <div class="panel panel-default user-profile">
3 <div class="panel panel-default user-profile">
4 <div class="panel-heading">
4 <div class="panel-heading">
5 <h3 class="panel-title">${_('User Profile')}</h3>
5 <h3 class="panel-title">${_('User Profile')}</h3>
6 </div>
6 </div>
7 <div class="panel-body">
7 <div class="panel-body">
8 <div class="user-profile-content">
8 <div class="user-profile-content">
9 ${h.secure_form(h.route_path('user_update', user_id=c.user.user_id), class_='form', request=request)}
9 ${h.secure_form(h.route_path('user_update', user_id=c.user.user_id), class_='form', request=request)}
10 <% readonly = None %>
10 <% readonly = None %>
11 <% disabled = "" %>
11 <% disabled = "" %>
12 %if c.extern_type != 'rhodecode':
12 %if c.extern_type != 'rhodecode':
13 <% readonly = "readonly" %>
13 <% readonly = "readonly" %>
14 <% disabled = " disabled" %>
14 <% disabled = " disabled" %>
15 <div class="infoform">
15 <div class="alert-warning" style="margin:0px 0px 20px 0px; padding: 10px">
16 <div class="fields">
16 <strong>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</strong>
17 <p>${_('This user was created from external source (%s). Editing some of the settings is limited.' % c.extern_type)}</p>
18 </div>
19 </div>
17 </div>
20 %endif
18 %endif
21 <div class="form">
19 <div class="form">
22 <div class="fields">
20 <div class="fields">
23 <div class="field">
21 <div class="field">
24 <div class="label photo">
22 <div class="label photo">
25 ${_('Photo')}:
23 ${_('Photo')}:
26 </div>
24 </div>
27 <div class="input profile">
25 <div class="input profile">
28 %if c.visual.use_gravatar:
26 %if c.visual.use_gravatar:
29 ${base.gravatar(c.user.email, 100)}
27 ${base.gravatar(c.user.email, 100)}
30 <p class="help-block">${_('Change the avatar at')} <a href="http://gravatar.com">gravatar.com</a>.</p>
28 <p class="help-block">${_('Change the avatar at')} <a href="http://gravatar.com">gravatar.com</a>.</p>
31 %else:
29 %else:
32 ${base.gravatar(c.user.email, 100)}
30 ${base.gravatar(c.user.email, 100)}
33 %endif
31 %endif
34 </div>
32 </div>
35 </div>
33 </div>
36 <div class="field">
34 <div class="field">
37 <div class="label">
35 <div class="label">
38 ${_('Username')}:
36 ${_('Username')}:
39 </div>
37 </div>
40 <div class="input">
38 <div class="input">
41 ${h.text('username', class_='%s medium' % disabled, readonly=readonly)}
39 ${h.text('username', class_='%s medium' % disabled, readonly=readonly)}
42 </div>
40 </div>
43 </div>
41 </div>
44 <div class="field">
42 <div class="field">
45 <div class="label">
43 <div class="label">
46 <label for="name">${_('First Name')}:</label>
44 <label for="name">${_('First Name')}:</label>
47 </div>
45 </div>
48 <div class="input">
46 <div class="input">
49 ${h.text('firstname', class_="medium")}
47 ${h.text('firstname', class_="medium")}
50 </div>
48 </div>
51 </div>
49 </div>
52
50
53 <div class="field">
51 <div class="field">
54 <div class="label">
52 <div class="label">
55 <label for="lastname">${_('Last Name')}:</label>
53 <label for="lastname">${_('Last Name')}:</label>
56 </div>
54 </div>
57 <div class="input">
55 <div class="input">
58 ${h.text('lastname', class_="medium")}
56 ${h.text('lastname', class_="medium")}
59 </div>
57 </div>
60 </div>
58 </div>
61
59
62 <div class="field">
60 <div class="field">
63 <div class="label">
61 <div class="label">
64 <label for="email">${_('Email')}:</label>
62 <label for="email">${_('Email')}:</label>
65 </div>
63 </div>
66 <div class="input">
64 <div class="input">
67 ## we should be able to edit email !
65 ## we should be able to edit email !
68 ${h.text('email', class_="medium")}
66 ${h.text('email', class_="medium")}
69 </div>
67 </div>
70 </div>
68 </div>
71 <div class="field">
69 <div class="field">
72 <div class="label">
70 <div class="label">
73 ${_('New Password')}:
71 ${_('New Password')}:
74 </div>
72 </div>
75 <div class="input">
73 <div class="input">
76 ${h.password('new_password',class_='%s medium' % disabled,autocomplete="off",readonly=readonly)}
74 ${h.password('new_password',class_='%s medium' % disabled,autocomplete="off",readonly=readonly)}
77 </div>
75 </div>
78 </div>
76 </div>
79 <div class="field">
77 <div class="field">
80 <div class="label">
78 <div class="label">
81 ${_('New Password Confirmation')}:
79 ${_('New Password Confirmation')}:
82 </div>
80 </div>
83 <div class="input">
81 <div class="input">
84 ${h.password('password_confirmation',class_="%s medium" % disabled,autocomplete="off",readonly=readonly)}
82 ${h.password('password_confirmation',class_="%s medium" % disabled,autocomplete="off",readonly=readonly)}
85 </div>
83 </div>
86 </div>
84 </div>
87 <div class="field">
85 <div class="field">
88 <div class="label-text">
86 <div class="label-text">
89 ${_('Active')}:
87 ${_('Active')}:
90 </div>
88 </div>
91 <div class="input user-checkbox">
89 <div class="input user-checkbox">
92 ${h.checkbox('active',value=True)}
90 ${h.checkbox('active',value=True)}
93 </div>
91 </div>
94 </div>
92 </div>
95 <div class="field">
93 <div class="field">
96 <div class="label-text">
94 <div class="label-text">
97 ${_('Super Admin')}:
95 ${_('Super Admin')}:
98 </div>
96 </div>
99 <div class="input user-checkbox">
97 <div class="input user-checkbox">
100 ${h.checkbox('admin',value=True)}
98 ${h.checkbox('admin',value=True)}
101 </div>
99 </div>
102 </div>
100 </div>
103 <div class="field">
101 <div class="field">
104 <div class="label-text">
102 <div class="label-text">
105 ${_('Authentication type')}:
103 ${_('Authentication type')}:
106 </div>
104 </div>
107 <div class="input">
105 <div class="input">
108 <p>${c.extern_type}</p>
106 ${h.select('extern_type', c.extern_type, c.allowed_extern_types)}
109 ${h.hidden('extern_type', readonly="readonly")}
107 <p class="help-block">${_('When user was created using an external source. He is bound to authentication using this method.')}</p>
110 <p class="help-block">${_('User was created using an external source. He is bound to authentication using this method.')}</p>
111 </div>
108 </div>
112 </div>
109 </div>
113 <div class="field">
110 <div class="field">
114 <div class="label-text">
111 <div class="label-text">
115 ${_('Name in Source of Record')}:
112 ${_('Name in Source of Record')}:
116 </div>
113 </div>
117 <div class="input">
114 <div class="input">
118 <p>${c.extern_name}</p>
115 <p>${c.extern_name}</p>
119 ${h.hidden('extern_name', readonly="readonly")}
116 ${h.hidden('extern_name', readonly="readonly")}
120 </div>
117 </div>
121 </div>
118 </div>
122 <div class="field">
119 <div class="field">
123 <div class="label">
120 <div class="label">
124 ${_('Language')}:
121 ${_('Language')}:
125 </div>
122 </div>
126 <div class="input">
123 <div class="input">
127 ## allowed_languages is defined in the users.py
124 ## allowed_languages is defined in the users.py
128 ## c.language comes from base.py as a default language
125 ## c.language comes from base.py as a default language
129 ${h.select('language', c.language, c.allowed_languages)}
126 ${h.select('language', c.language, c.allowed_languages)}
130 <p class="help-block">${h.literal(_('User interface language. Help translate %(rc_link)s into your language.') % {'rc_link': h.link_to('RhodeCode Enterprise', h.route_url('rhodecode_translations'))})}</p>
127 <p class="help-block">${h.literal(_('User interface language. Help translate %(rc_link)s into your language.') % {'rc_link': h.link_to('RhodeCode Enterprise', h.route_url('rhodecode_translations'))})}</p>
131 </div>
128 </div>
132 </div>
129 </div>
133 <div class="buttons">
130 <div class="buttons">
134 ${h.submit('save', _('Save'), class_="btn")}
131 ${h.submit('save', _('Save'), class_="btn")}
135 ${h.reset('reset', _('Reset'), class_="btn")}
132 ${h.reset('reset', _('Reset'), class_="btn")}
136 </div>
133 </div>
137 </div>
134 </div>
138 </div>
135 </div>
139 ${h.end_form()}
136 ${h.end_form()}
140 </div>
137 </div>
141 </div>
138 </div>
142 </div>
139 </div>
143
140
144 <script>
141 <script>
145 $('#language').select2({
142 $('#language').select2({
146 'containerCssClass': "drop-menu",
143 'containerCssClass': "drop-menu",
147 'dropdownCssClass': "drop-menu-dropdown",
144 'dropdownCssClass': "drop-menu-dropdown",
148 'dropdownAutoWidth': true
145 'dropdownAutoWidth': true
149 });
146 });
150 </script>
147 </script>
General Comments 0
You need to be logged in to leave comments. Login now