##// END OF EJS Templates
permissions: handle more cases for invalidating permission caches...
marcink -
r3383:c5723c68 default
parent child Browse files
Show More

The requested changes are too big and content was truncated. Show full diff

@@ -1,207 +1,215 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 formencode
22 import formencode
23 import formencode.htmlfill
23 import formencode.htmlfill
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32
32
33 from rhodecode.lib.ext_json import json
33 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.auth import (
34 from rhodecode.lib.auth import (
35 LoginRequired, CSRFRequired, NotAnonymous,
35 LoginRequired, CSRFRequired, NotAnonymous,
36 HasPermissionAny, HasRepoGroupPermissionAny)
36 HasPermissionAny, HasRepoGroupPermissionAny)
37 from rhodecode.lib import helpers as h, audit_logger
37 from rhodecode.lib import helpers as h, audit_logger
38 from rhodecode.lib.utils2 import safe_int, safe_unicode
38 from rhodecode.lib.utils2 import safe_int, safe_unicode
39 from rhodecode.model.forms import RepoGroupForm
39 from rhodecode.model.forms import RepoGroupForm
40 from rhodecode.model.repo_group import RepoGroupModel
40 from rhodecode.model.repo_group import RepoGroupModel
41 from rhodecode.model.scm import RepoGroupList
41 from rhodecode.model.scm import RepoGroupList
42 from rhodecode.model.db import Session, RepoGroup
42 from rhodecode.model.db import Session, RepoGroup
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
47 class AdminRepoGroupsView(BaseAppView, DataGridAppView):
48
48
49 def load_default_context(self):
49 def load_default_context(self):
50 c = self._get_local_tmpl_context()
50 c = self._get_local_tmpl_context()
51
51
52 return c
52 return c
53
53
54 def _load_form_data(self, c):
54 def _load_form_data(self, c):
55 allow_empty_group = False
55 allow_empty_group = False
56
56
57 if self._can_create_repo_group():
57 if self._can_create_repo_group():
58 # we're global admin, we're ok and we can create TOP level groups
58 # we're global admin, we're ok and we can create TOP level groups
59 allow_empty_group = True
59 allow_empty_group = True
60
60
61 # override the choices for this form, we need to filter choices
61 # override the choices for this form, we need to filter choices
62 # and display only those we have ADMIN right
62 # and display only those we have ADMIN right
63 groups_with_admin_rights = RepoGroupList(
63 groups_with_admin_rights = RepoGroupList(
64 RepoGroup.query().all(),
64 RepoGroup.query().all(),
65 perm_set=['group.admin'])
65 perm_set=['group.admin'])
66 c.repo_groups = RepoGroup.groups_choices(
66 c.repo_groups = RepoGroup.groups_choices(
67 groups=groups_with_admin_rights,
67 groups=groups_with_admin_rights,
68 show_empty_group=allow_empty_group)
68 show_empty_group=allow_empty_group)
69
69
70 def _can_create_repo_group(self, parent_group_id=None):
70 def _can_create_repo_group(self, parent_group_id=None):
71 is_admin = HasPermissionAny('hg.admin')('group create controller')
71 is_admin = HasPermissionAny('hg.admin')('group create controller')
72 create_repo_group = HasPermissionAny(
72 create_repo_group = HasPermissionAny(
73 'hg.repogroup.create.true')('group create controller')
73 'hg.repogroup.create.true')('group create controller')
74 if is_admin or (create_repo_group and not parent_group_id):
74 if is_admin or (create_repo_group and not parent_group_id):
75 # we're global admin, or we have global repo group create
75 # we're global admin, or we have global repo group create
76 # permission
76 # permission
77 # we're ok and we can create TOP level groups
77 # we're ok and we can create TOP level groups
78 return True
78 return True
79 elif parent_group_id:
79 elif parent_group_id:
80 # we check the permission if we can write to parent group
80 # we check the permission if we can write to parent group
81 group = RepoGroup.get(parent_group_id)
81 group = RepoGroup.get(parent_group_id)
82 group_name = group.group_name if group else None
82 group_name = group.group_name if group else None
83 if HasRepoGroupPermissionAny('group.admin')(
83 if HasRepoGroupPermissionAny('group.admin')(
84 group_name, 'check if user is an admin of group'):
84 group_name, 'check if user is an admin of group'):
85 # we're an admin of passed in group, we're ok.
85 # we're an admin of passed in group, we're ok.
86 return True
86 return True
87 else:
87 else:
88 return False
88 return False
89 return False
89 return False
90
90
91 @LoginRequired()
91 @LoginRequired()
92 @NotAnonymous()
92 @NotAnonymous()
93 # perms check inside
93 # perms check inside
94 @view_config(
94 @view_config(
95 route_name='repo_groups', request_method='GET',
95 route_name='repo_groups', request_method='GET',
96 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
96 renderer='rhodecode:templates/admin/repo_groups/repo_groups.mako')
97 def repo_group_list(self):
97 def repo_group_list(self):
98 c = self.load_default_context()
98 c = self.load_default_context()
99
99
100 repo_group_list = RepoGroup.get_all_repo_groups()
100 repo_group_list = RepoGroup.get_all_repo_groups()
101 repo_group_list_acl = RepoGroupList(
101 repo_group_list_acl = RepoGroupList(
102 repo_group_list, perm_set=['group.admin'])
102 repo_group_list, perm_set=['group.admin'])
103 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
103 repo_group_data = RepoGroupModel().get_repo_groups_as_dict(
104 repo_group_list=repo_group_list_acl, admin=True)
104 repo_group_list=repo_group_list_acl, admin=True)
105 c.data = json.dumps(repo_group_data)
105 c.data = json.dumps(repo_group_data)
106 return self._get_template_context(c)
106 return self._get_template_context(c)
107
107
108 @LoginRequired()
108 @LoginRequired()
109 @NotAnonymous()
109 @NotAnonymous()
110 # perm checks inside
110 # perm checks inside
111 @view_config(
111 @view_config(
112 route_name='repo_group_new', request_method='GET',
112 route_name='repo_group_new', request_method='GET',
113 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
113 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
114 def repo_group_new(self):
114 def repo_group_new(self):
115 c = self.load_default_context()
115 c = self.load_default_context()
116
116
117 # perm check for admin, create_group perm or admin of parent_group
117 # perm check for admin, create_group perm or admin of parent_group
118 parent_group_id = safe_int(self.request.GET.get('parent_group'))
118 parent_group_id = safe_int(self.request.GET.get('parent_group'))
119 if not self._can_create_repo_group(parent_group_id):
119 if not self._can_create_repo_group(parent_group_id):
120 raise HTTPForbidden()
120 raise HTTPForbidden()
121
121
122 self._load_form_data(c)
122 self._load_form_data(c)
123
123
124 defaults = {} # Future proof for default of repo group
124 defaults = {} # Future proof for default of repo group
125 data = render(
125 data = render(
126 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
126 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
127 self._get_template_context(c), self.request)
127 self._get_template_context(c), self.request)
128 html = formencode.htmlfill.render(
128 html = formencode.htmlfill.render(
129 data,
129 data,
130 defaults=defaults,
130 defaults=defaults,
131 encoding="UTF-8",
131 encoding="UTF-8",
132 force_defaults=False
132 force_defaults=False
133 )
133 )
134 return Response(html)
134 return Response(html)
135
135
136 @LoginRequired()
136 @LoginRequired()
137 @NotAnonymous()
137 @NotAnonymous()
138 @CSRFRequired()
138 @CSRFRequired()
139 # perm checks inside
139 # perm checks inside
140 @view_config(
140 @view_config(
141 route_name='repo_group_create', request_method='POST',
141 route_name='repo_group_create', request_method='POST',
142 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
142 renderer='rhodecode:templates/admin/repo_groups/repo_group_add.mako')
143 def repo_group_create(self):
143 def repo_group_create(self):
144 c = self.load_default_context()
144 c = self.load_default_context()
145 _ = self.request.translate
145 _ = self.request.translate
146
146
147 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
147 parent_group_id = safe_int(self.request.POST.get('group_parent_id'))
148 can_create = self._can_create_repo_group(parent_group_id)
148 can_create = self._can_create_repo_group(parent_group_id)
149
149
150 self._load_form_data(c)
150 self._load_form_data(c)
151 # permissions for can create group based on parent_id are checked
151 # permissions for can create group based on parent_id are checked
152 # here in the Form
152 # here in the Form
153 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
153 available_groups = map(lambda k: safe_unicode(k[0]), c.repo_groups)
154 repo_group_form = RepoGroupForm(
154 repo_group_form = RepoGroupForm(
155 self.request.translate, available_groups=available_groups,
155 self.request.translate, available_groups=available_groups,
156 can_create_in_root=can_create)()
156 can_create_in_root=can_create)()
157
157
158 repo_group_name = self.request.POST.get('group_name')
158 repo_group_name = self.request.POST.get('group_name')
159 try:
159 try:
160 owner = self._rhodecode_user
160 owner = self._rhodecode_user
161 form_result = repo_group_form.to_python(dict(self.request.POST))
161 form_result = repo_group_form.to_python(dict(self.request.POST))
162 copy_permissions = form_result.get('group_copy_permissions')
162 repo_group = RepoGroupModel().create(
163 repo_group = RepoGroupModel().create(
163 group_name=form_result['group_name_full'],
164 group_name=form_result['group_name_full'],
164 group_description=form_result['group_description'],
165 group_description=form_result['group_description'],
165 owner=owner.user_id,
166 owner=owner.user_id,
166 copy_permissions=form_result['group_copy_permissions']
167 copy_permissions=form_result['group_copy_permissions']
167 )
168 )
168 Session().flush()
169 Session().flush()
169
170
170 repo_group_data = repo_group.get_api_data()
171 repo_group_data = repo_group.get_api_data()
171 audit_logger.store_web(
172 audit_logger.store_web(
172 'repo_group.create', action_data={'data': repo_group_data},
173 'repo_group.create', action_data={'data': repo_group_data},
173 user=self._rhodecode_user)
174 user=self._rhodecode_user)
174
175
175 Session().commit()
176 Session().commit()
176
177
177 _new_group_name = form_result['group_name_full']
178 _new_group_name = form_result['group_name_full']
178
179
179 repo_group_url = h.link_to(
180 repo_group_url = h.link_to(
180 _new_group_name,
181 _new_group_name,
181 h.route_path('repo_group_home', repo_group_name=_new_group_name))
182 h.route_path('repo_group_home', repo_group_name=_new_group_name))
182 h.flash(h.literal(_('Created repository group %s')
183 h.flash(h.literal(_('Created repository group %s')
183 % repo_group_url), category='success')
184 % repo_group_url), category='success')
184
185
185 except formencode.Invalid as errors:
186 except formencode.Invalid as errors:
186 data = render(
187 data = render(
187 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
188 'rhodecode:templates/admin/repo_groups/repo_group_add.mako',
188 self._get_template_context(c), self.request)
189 self._get_template_context(c), self.request)
189 html = formencode.htmlfill.render(
190 html = formencode.htmlfill.render(
190 data,
191 data,
191 defaults=errors.value,
192 defaults=errors.value,
192 errors=errors.error_dict or {},
193 errors=errors.error_dict or {},
193 prefix_error=False,
194 prefix_error=False,
194 encoding="UTF-8",
195 encoding="UTF-8",
195 force_defaults=False
196 force_defaults=False
196 )
197 )
197 return Response(html)
198 return Response(html)
198 except Exception:
199 except Exception:
199 log.exception("Exception during creation of repository group")
200 log.exception("Exception during creation of repository group")
200 h.flash(_('Error occurred during creation of repository group %s')
201 h.flash(_('Error occurred during creation of repository group %s')
201 % repo_group_name, category='error')
202 % repo_group_name, category='error')
202 raise HTTPFound(h.route_path('home'))
203 raise HTTPFound(h.route_path('home'))
203
204
204 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
205 affected_user_ids = [self._rhodecode_user.user_id]
206 if copy_permissions:
207 user_group_perms = repo_group.permissions(expand_from_user_groups=True)
208 copy_perms = [perm['user_id'] for perm in user_group_perms]
209 # also include those newly created by copy
210 affected_user_ids.extend(copy_perms)
211 events.trigger(events.UserPermissionsChange(affected_user_ids))
212
205 raise HTTPFound(
213 raise HTTPFound(
206 h.route_path('repo_group_home',
214 h.route_path('repo_group_home',
207 repo_group_name=form_result['group_name_full']))
215 repo_group_name=form_result['group_name_full']))
@@ -1,184 +1,194 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 formencode
22 import formencode
23 import formencode.htmlfill
23 import formencode.htmlfill
24
24
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
25 from pyramid.httpexceptions import HTTPFound, HTTPForbidden
26 from pyramid.view import view_config
26 from pyramid.view import view_config
27 from pyramid.renderers import render
27 from pyramid.renderers import render
28 from pyramid.response import Response
28 from pyramid.response import Response
29
29
30 from rhodecode import events
30 from rhodecode import events
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode.lib.celerylib.utils import get_task_id
32 from rhodecode.lib.celerylib.utils import get_task_id
33
33
34 from rhodecode.lib.ext_json import json
34 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.auth import (
35 from rhodecode.lib.auth import (
36 LoginRequired, CSRFRequired, NotAnonymous,
36 LoginRequired, CSRFRequired, NotAnonymous,
37 HasPermissionAny, HasRepoGroupPermissionAny)
37 HasPermissionAny, HasRepoGroupPermissionAny)
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.utils import repo_name_slug
39 from rhodecode.lib.utils import repo_name_slug
40 from rhodecode.lib.utils2 import safe_int, safe_unicode
40 from rhodecode.lib.utils2 import safe_int, safe_unicode
41 from rhodecode.model.forms import RepoForm
41 from rhodecode.model.forms import RepoForm
42 from rhodecode.model.repo import RepoModel
42 from rhodecode.model.repo import RepoModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
43 from rhodecode.model.scm import RepoList, RepoGroupList, ScmModel
44 from rhodecode.model.settings import SettingsModel
44 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.db import Repository, RepoGroup
45 from rhodecode.model.db import Repository, RepoGroup
46
46
47 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
48
48
49
49
50 class AdminReposView(BaseAppView, DataGridAppView):
50 class AdminReposView(BaseAppView, DataGridAppView):
51
51
52 def load_default_context(self):
52 def load_default_context(self):
53 c = self._get_local_tmpl_context()
53 c = self._get_local_tmpl_context()
54
54
55 return c
55 return c
56
56
57 def _load_form_data(self, c):
57 def _load_form_data(self, c):
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
58 acl_groups = RepoGroupList(RepoGroup.query().all(),
59 perm_set=['group.write', 'group.admin'])
59 perm_set=['group.write', 'group.admin'])
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
60 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
61 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
62 c.landing_revs_choices, c.landing_revs = \
62 c.landing_revs_choices, c.landing_revs = \
63 ScmModel().get_repo_landing_revs(self.request.translate)
63 ScmModel().get_repo_landing_revs(self.request.translate)
64 c.personal_repo_group = self._rhodecode_user.personal_repo_group
64 c.personal_repo_group = self._rhodecode_user.personal_repo_group
65
65
66 @LoginRequired()
66 @LoginRequired()
67 @NotAnonymous()
67 @NotAnonymous()
68 # perms check inside
68 # perms check inside
69 @view_config(
69 @view_config(
70 route_name='repos', request_method='GET',
70 route_name='repos', request_method='GET',
71 renderer='rhodecode:templates/admin/repos/repos.mako')
71 renderer='rhodecode:templates/admin/repos/repos.mako')
72 def repository_list(self):
72 def repository_list(self):
73 c = self.load_default_context()
73 c = self.load_default_context()
74
74
75 repo_list = Repository.get_all_repos()
75 repo_list = Repository.get_all_repos()
76 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
76 c.repo_list = RepoList(repo_list, perm_set=['repository.admin'])
77 repos_data = RepoModel().get_repos_as_dict(
77 repos_data = RepoModel().get_repos_as_dict(
78 repo_list=c.repo_list, admin=True, super_user_actions=True)
78 repo_list=c.repo_list, admin=True, super_user_actions=True)
79 # json used to render the grid
79 # json used to render the grid
80 c.data = json.dumps(repos_data)
80 c.data = json.dumps(repos_data)
81
81
82 return self._get_template_context(c)
82 return self._get_template_context(c)
83
83
84 @LoginRequired()
84 @LoginRequired()
85 @NotAnonymous()
85 @NotAnonymous()
86 # perms check inside
86 # perms check inside
87 @view_config(
87 @view_config(
88 route_name='repo_new', request_method='GET',
88 route_name='repo_new', request_method='GET',
89 renderer='rhodecode:templates/admin/repos/repo_add.mako')
89 renderer='rhodecode:templates/admin/repos/repo_add.mako')
90 def repository_new(self):
90 def repository_new(self):
91 c = self.load_default_context()
91 c = self.load_default_context()
92
92
93 new_repo = self.request.GET.get('repo', '')
93 new_repo = self.request.GET.get('repo', '')
94 parent_group = safe_int(self.request.GET.get('parent_group'))
94 parent_group = safe_int(self.request.GET.get('parent_group'))
95 _gr = RepoGroup.get(parent_group)
95 _gr = RepoGroup.get(parent_group)
96
96
97 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
97 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
98 # you're not super admin nor have global create permissions,
98 # you're not super admin nor have global create permissions,
99 # but maybe you have at least write permission to a parent group ?
99 # but maybe you have at least write permission to a parent group ?
100
100
101 gr_name = _gr.group_name if _gr else None
101 gr_name = _gr.group_name if _gr else None
102 # create repositories with write permission on group is set to true
102 # create repositories with write permission on group is set to true
103 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
103 create_on_write = HasPermissionAny('hg.create.write_on_repogroup.true')()
104 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
104 group_admin = HasRepoGroupPermissionAny('group.admin')(group_name=gr_name)
105 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
105 group_write = HasRepoGroupPermissionAny('group.write')(group_name=gr_name)
106 if not (group_admin or (group_write and create_on_write)):
106 if not (group_admin or (group_write and create_on_write)):
107 raise HTTPForbidden()
107 raise HTTPForbidden()
108
108
109 self._load_form_data(c)
109 self._load_form_data(c)
110 c.new_repo = repo_name_slug(new_repo)
110 c.new_repo = repo_name_slug(new_repo)
111
111
112 # apply the defaults from defaults page
112 # apply the defaults from defaults page
113 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
113 defaults = SettingsModel().get_default_repo_settings(strip_prefix=True)
114 # set checkbox to autochecked
114 # set checkbox to autochecked
115 defaults['repo_copy_permissions'] = True
115 defaults['repo_copy_permissions'] = True
116
116
117 parent_group_choice = '-1'
117 parent_group_choice = '-1'
118 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
118 if not self._rhodecode_user.is_admin and self._rhodecode_user.personal_repo_group:
119 parent_group_choice = self._rhodecode_user.personal_repo_group
119 parent_group_choice = self._rhodecode_user.personal_repo_group
120
120
121 if parent_group and _gr:
121 if parent_group and _gr:
122 if parent_group in [x[0] for x in c.repo_groups]:
122 if parent_group in [x[0] for x in c.repo_groups]:
123 parent_group_choice = safe_unicode(parent_group)
123 parent_group_choice = safe_unicode(parent_group)
124
124
125 defaults.update({'repo_group': parent_group_choice})
125 defaults.update({'repo_group': parent_group_choice})
126
126
127 data = render('rhodecode:templates/admin/repos/repo_add.mako',
127 data = render('rhodecode:templates/admin/repos/repo_add.mako',
128 self._get_template_context(c), self.request)
128 self._get_template_context(c), self.request)
129 html = formencode.htmlfill.render(
129 html = formencode.htmlfill.render(
130 data,
130 data,
131 defaults=defaults,
131 defaults=defaults,
132 encoding="UTF-8",
132 encoding="UTF-8",
133 force_defaults=False
133 force_defaults=False
134 )
134 )
135 return Response(html)
135 return Response(html)
136
136
137 @LoginRequired()
137 @LoginRequired()
138 @NotAnonymous()
138 @NotAnonymous()
139 @CSRFRequired()
139 @CSRFRequired()
140 # perms check inside
140 # perms check inside
141 @view_config(
141 @view_config(
142 route_name='repo_create', request_method='POST',
142 route_name='repo_create', request_method='POST',
143 renderer='rhodecode:templates/admin/repos/repos.mako')
143 renderer='rhodecode:templates/admin/repos/repos.mako')
144 def repository_create(self):
144 def repository_create(self):
145 c = self.load_default_context()
145 c = self.load_default_context()
146
146
147 form_result = {}
147 form_result = {}
148 self._load_form_data(c)
148 self._load_form_data(c)
149 task_id = None
149
150 try:
150 try:
151 # CanWriteToGroup validators checks permissions of this POST
151 # CanWriteToGroup validators checks permissions of this POST
152 form = RepoForm(
152 form = RepoForm(
153 self.request.translate, repo_groups=c.repo_groups_choices,
153 self.request.translate, repo_groups=c.repo_groups_choices,
154 landing_revs=c.landing_revs_choices)()
154 landing_revs=c.landing_revs_choices)()
155 form_result = form.to_python(dict(self.request.POST))
155 form_result = form.to_python(dict(self.request.POST))
156
156 copy_permissions = form_result.get('repo_copy_permissions')
157 # create is done sometimes async on celery, db transaction
157 # create is done sometimes async on celery, db transaction
158 # management is handled there.
158 # management is handled there.
159 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
159 task = RepoModel().create(form_result, self._rhodecode_user.user_id)
160 task_id = get_task_id(task)
160 task_id = get_task_id(task)
161 except formencode.Invalid as errors:
161 except formencode.Invalid as errors:
162 data = render('rhodecode:templates/admin/repos/repo_add.mako',
162 data = render('rhodecode:templates/admin/repos/repo_add.mako',
163 self._get_template_context(c), self.request)
163 self._get_template_context(c), self.request)
164 html = formencode.htmlfill.render(
164 html = formencode.htmlfill.render(
165 data,
165 data,
166 defaults=errors.value,
166 defaults=errors.value,
167 errors=errors.error_dict or {},
167 errors=errors.error_dict or {},
168 prefix_error=False,
168 prefix_error=False,
169 encoding="UTF-8",
169 encoding="UTF-8",
170 force_defaults=False
170 force_defaults=False
171 )
171 )
172 return Response(html)
172 return Response(html)
173
173
174 except Exception as e:
174 except Exception as e:
175 msg = self._log_creation_exception(e, form_result.get('repo_name'))
175 msg = self._log_creation_exception(e, form_result.get('repo_name'))
176 h.flash(msg, category='error')
176 h.flash(msg, category='error')
177 raise HTTPFound(h.route_path('home'))
177 raise HTTPFound(h.route_path('home'))
178
178
179 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
179 repo_name = form_result.get('repo_name_full')
180
181 affected_user_ids = [self._rhodecode_user.user_id]
182 if copy_permissions:
183 repository = Repository.get_by_repo_name(repo_name)
184 # also include those newly created by copy
185 user_group_perms = repository.permissions(expand_from_user_groups=True)
186 copy_perms = [perm['user_id'] for perm in user_group_perms]
187 # also include those newly created by copy
188 affected_user_ids.extend(copy_perms)
189
190 events.trigger(events.UserPermissionsChange(affected_user_ids))
180
191
181 raise HTTPFound(
192 raise HTTPFound(
182 h.route_path('repo_creating',
193 h.route_path('repo_creating', repo_name=repo_name,
183 repo_name=form_result['repo_name_full'],
184 _query=dict(task_id=task_id)))
194 _query=dict(task_id=task_id)))
@@ -1,1261 +1,1266 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.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView
32 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.apps.ssh_support import SshKeyFileChangeEvent
33 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.authentication.plugins import auth_rhodecode
34 from rhodecode.events import trigger
35 from rhodecode.events import trigger
35 from rhodecode.model.db import true
36 from rhodecode.model.db import true
36
37
37 from rhodecode.lib import audit_logger, rc_cache
38 from rhodecode.lib import audit_logger, rc_cache
38 from rhodecode.lib.exceptions import (
39 from rhodecode.lib.exceptions import (
39 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException,
40 UserOwnsUserGroupsException, DefaultUserException)
41 UserOwnsUserGroupsException, DefaultUserException)
41 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.ext_json import json
42 from rhodecode.lib.auth import (
43 from rhodecode.lib.auth import (
43 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
44 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
44 from rhodecode.lib import helpers as h
45 from rhodecode.lib import helpers as h
45 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
46 from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict
46 from rhodecode.model.auth_token import AuthTokenModel
47 from rhodecode.model.auth_token import AuthTokenModel
47 from rhodecode.model.forms import (
48 from rhodecode.model.forms import (
48 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
49 UserForm, UserIndividualPermissionsForm, UserPermissionsForm,
49 UserExtraEmailForm, UserExtraIpForm)
50 UserExtraEmailForm, UserExtraIpForm)
50 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.permission import PermissionModel
51 from rhodecode.model.repo_group import RepoGroupModel
52 from rhodecode.model.repo_group import RepoGroupModel
52 from rhodecode.model.ssh_key import SshKeyModel
53 from rhodecode.model.ssh_key import SshKeyModel
53 from rhodecode.model.user import UserModel
54 from rhodecode.model.user import UserModel
54 from rhodecode.model.user_group import UserGroupModel
55 from rhodecode.model.user_group import UserGroupModel
55 from rhodecode.model.db import (
56 from rhodecode.model.db import (
56 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
57 or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap,
57 UserApiKeys, UserSshKeys, RepoGroup)
58 UserApiKeys, UserSshKeys, RepoGroup)
58 from rhodecode.model.meta import Session
59 from rhodecode.model.meta import Session
59
60
60 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
61
62
62
63
63 class AdminUsersView(BaseAppView, DataGridAppView):
64 class AdminUsersView(BaseAppView, DataGridAppView):
64
65
65 def load_default_context(self):
66 def load_default_context(self):
66 c = self._get_local_tmpl_context()
67 c = self._get_local_tmpl_context()
67 return c
68 return c
68
69
69 @LoginRequired()
70 @LoginRequired()
70 @HasPermissionAllDecorator('hg.admin')
71 @HasPermissionAllDecorator('hg.admin')
71 @view_config(
72 @view_config(
72 route_name='users', request_method='GET',
73 route_name='users', request_method='GET',
73 renderer='rhodecode:templates/admin/users/users.mako')
74 renderer='rhodecode:templates/admin/users/users.mako')
74 def users_list(self):
75 def users_list(self):
75 c = self.load_default_context()
76 c = self.load_default_context()
76 return self._get_template_context(c)
77 return self._get_template_context(c)
77
78
78 @LoginRequired()
79 @LoginRequired()
79 @HasPermissionAllDecorator('hg.admin')
80 @HasPermissionAllDecorator('hg.admin')
80 @view_config(
81 @view_config(
81 # renderer defined below
82 # renderer defined below
82 route_name='users_data', request_method='GET',
83 route_name='users_data', request_method='GET',
83 renderer='json_ext', xhr=True)
84 renderer='json_ext', xhr=True)
84 def users_list_data(self):
85 def users_list_data(self):
85 self.load_default_context()
86 self.load_default_context()
86 column_map = {
87 column_map = {
87 'first_name': 'name',
88 'first_name': 'name',
88 'last_name': 'lastname',
89 'last_name': 'lastname',
89 }
90 }
90 draw, start, limit = self._extract_chunk(self.request)
91 draw, start, limit = self._extract_chunk(self.request)
91 search_q, order_by, order_dir = self._extract_ordering(
92 search_q, order_by, order_dir = self._extract_ordering(
92 self.request, column_map=column_map)
93 self.request, column_map=column_map)
93 _render = self.request.get_partial_renderer(
94 _render = self.request.get_partial_renderer(
94 'rhodecode:templates/data_table/_dt_elements.mako')
95 'rhodecode:templates/data_table/_dt_elements.mako')
95
96
96 def user_actions(user_id, username):
97 def user_actions(user_id, username):
97 return _render("user_actions", user_id, username)
98 return _render("user_actions", user_id, username)
98
99
99 users_data_total_count = User.query()\
100 users_data_total_count = User.query()\
100 .filter(User.username != User.DEFAULT_USER) \
101 .filter(User.username != User.DEFAULT_USER) \
101 .count()
102 .count()
102
103
103 users_data_total_inactive_count = User.query()\
104 users_data_total_inactive_count = User.query()\
104 .filter(User.username != User.DEFAULT_USER) \
105 .filter(User.username != User.DEFAULT_USER) \
105 .filter(User.active != true())\
106 .filter(User.active != true())\
106 .count()
107 .count()
107
108
108 # json generate
109 # json generate
109 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 base_q = User.query().filter(User.username != User.DEFAULT_USER)
110 base_inactive_q = base_q.filter(User.active != true())
111 base_inactive_q = base_q.filter(User.active != true())
111
112
112 if search_q:
113 if search_q:
113 like_expression = u'%{}%'.format(safe_unicode(search_q))
114 like_expression = u'%{}%'.format(safe_unicode(search_q))
114 base_q = base_q.filter(or_(
115 base_q = base_q.filter(or_(
115 User.username.ilike(like_expression),
116 User.username.ilike(like_expression),
116 User._email.ilike(like_expression),
117 User._email.ilike(like_expression),
117 User.name.ilike(like_expression),
118 User.name.ilike(like_expression),
118 User.lastname.ilike(like_expression),
119 User.lastname.ilike(like_expression),
119 ))
120 ))
120 base_inactive_q = base_q.filter(User.active != true())
121 base_inactive_q = base_q.filter(User.active != true())
121
122
122 users_data_total_filtered_count = base_q.count()
123 users_data_total_filtered_count = base_q.count()
123 users_data_total_filtered_inactive_count = base_inactive_q.count()
124 users_data_total_filtered_inactive_count = base_inactive_q.count()
124
125
125 sort_col = getattr(User, order_by, None)
126 sort_col = getattr(User, order_by, None)
126 if sort_col:
127 if sort_col:
127 if order_dir == 'asc':
128 if order_dir == 'asc':
128 # handle null values properly to order by NULL last
129 # handle null values properly to order by NULL last
129 if order_by in ['last_activity']:
130 if order_by in ['last_activity']:
130 sort_col = coalesce(sort_col, datetime.date.max)
131 sort_col = coalesce(sort_col, datetime.date.max)
131 sort_col = sort_col.asc()
132 sort_col = sort_col.asc()
132 else:
133 else:
133 # handle null values properly to order by NULL last
134 # handle null values properly to order by NULL last
134 if order_by in ['last_activity']:
135 if order_by in ['last_activity']:
135 sort_col = coalesce(sort_col, datetime.date.min)
136 sort_col = coalesce(sort_col, datetime.date.min)
136 sort_col = sort_col.desc()
137 sort_col = sort_col.desc()
137
138
138 base_q = base_q.order_by(sort_col)
139 base_q = base_q.order_by(sort_col)
139 base_q = base_q.offset(start).limit(limit)
140 base_q = base_q.offset(start).limit(limit)
140
141
141 users_list = base_q.all()
142 users_list = base_q.all()
142
143
143 users_data = []
144 users_data = []
144 for user in users_list:
145 for user in users_list:
145 users_data.append({
146 users_data.append({
146 "username": h.gravatar_with_user(self.request, user.username),
147 "username": h.gravatar_with_user(self.request, user.username),
147 "email": user.email,
148 "email": user.email,
148 "first_name": user.first_name,
149 "first_name": user.first_name,
149 "last_name": user.last_name,
150 "last_name": user.last_name,
150 "last_login": h.format_date(user.last_login),
151 "last_login": h.format_date(user.last_login),
151 "last_activity": h.format_date(user.last_activity),
152 "last_activity": h.format_date(user.last_activity),
152 "active": h.bool2icon(user.active),
153 "active": h.bool2icon(user.active),
153 "active_raw": user.active,
154 "active_raw": user.active,
154 "admin": h.bool2icon(user.admin),
155 "admin": h.bool2icon(user.admin),
155 "extern_type": user.extern_type,
156 "extern_type": user.extern_type,
156 "extern_name": user.extern_name,
157 "extern_name": user.extern_name,
157 "action": user_actions(user.user_id, user.username),
158 "action": user_actions(user.user_id, user.username),
158 })
159 })
159 data = ({
160 data = ({
160 'draw': draw,
161 'draw': draw,
161 'data': users_data,
162 'data': users_data,
162 'recordsTotal': users_data_total_count,
163 'recordsTotal': users_data_total_count,
163 'recordsFiltered': users_data_total_filtered_count,
164 'recordsFiltered': users_data_total_filtered_count,
164 'recordsTotalInactive': users_data_total_inactive_count,
165 'recordsTotalInactive': users_data_total_inactive_count,
165 'recordsFilteredInactive': users_data_total_filtered_inactive_count
166 'recordsFilteredInactive': users_data_total_filtered_inactive_count
166 })
167 })
167
168
168 return data
169 return data
169
170
170 def _set_personal_repo_group_template_vars(self, c_obj):
171 def _set_personal_repo_group_template_vars(self, c_obj):
171 DummyUser = AttributeDict({
172 DummyUser = AttributeDict({
172 'username': '${username}',
173 'username': '${username}',
173 'user_id': '${user_id}',
174 'user_id': '${user_id}',
174 })
175 })
175 c_obj.default_create_repo_group = RepoGroupModel() \
176 c_obj.default_create_repo_group = RepoGroupModel() \
176 .get_default_create_personal_repo_group()
177 .get_default_create_personal_repo_group()
177 c_obj.personal_repo_group_name = RepoGroupModel() \
178 c_obj.personal_repo_group_name = RepoGroupModel() \
178 .get_personal_group_name(DummyUser)
179 .get_personal_group_name(DummyUser)
179
180
180 @LoginRequired()
181 @LoginRequired()
181 @HasPermissionAllDecorator('hg.admin')
182 @HasPermissionAllDecorator('hg.admin')
182 @view_config(
183 @view_config(
183 route_name='users_new', request_method='GET',
184 route_name='users_new', request_method='GET',
184 renderer='rhodecode:templates/admin/users/user_add.mako')
185 renderer='rhodecode:templates/admin/users/user_add.mako')
185 def users_new(self):
186 def users_new(self):
186 _ = self.request.translate
187 _ = self.request.translate
187 c = self.load_default_context()
188 c = self.load_default_context()
188 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
189 self._set_personal_repo_group_template_vars(c)
190 self._set_personal_repo_group_template_vars(c)
190 return self._get_template_context(c)
191 return self._get_template_context(c)
191
192
192 @LoginRequired()
193 @LoginRequired()
193 @HasPermissionAllDecorator('hg.admin')
194 @HasPermissionAllDecorator('hg.admin')
194 @CSRFRequired()
195 @CSRFRequired()
195 @view_config(
196 @view_config(
196 route_name='users_create', request_method='POST',
197 route_name='users_create', request_method='POST',
197 renderer='rhodecode:templates/admin/users/user_add.mako')
198 renderer='rhodecode:templates/admin/users/user_add.mako')
198 def users_create(self):
199 def users_create(self):
199 _ = self.request.translate
200 _ = self.request.translate
200 c = self.load_default_context()
201 c = self.load_default_context()
201 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
202 c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid
202 user_model = UserModel()
203 user_model = UserModel()
203 user_form = UserForm(self.request.translate)()
204 user_form = UserForm(self.request.translate)()
204 try:
205 try:
205 form_result = user_form.to_python(dict(self.request.POST))
206 form_result = user_form.to_python(dict(self.request.POST))
206 user = user_model.create(form_result)
207 user = user_model.create(form_result)
207 Session().flush()
208 Session().flush()
208 creation_data = user.get_api_data()
209 creation_data = user.get_api_data()
209 username = form_result['username']
210 username = form_result['username']
210
211
211 audit_logger.store_web(
212 audit_logger.store_web(
212 'user.create', action_data={'data': creation_data},
213 'user.create', action_data={'data': creation_data},
213 user=c.rhodecode_user)
214 user=c.rhodecode_user)
214
215
215 user_link = h.link_to(
216 user_link = h.link_to(
216 h.escape(username),
217 h.escape(username),
217 h.route_path('user_edit', user_id=user.user_id))
218 h.route_path('user_edit', user_id=user.user_id))
218 h.flash(h.literal(_('Created user %(user_link)s')
219 h.flash(h.literal(_('Created user %(user_link)s')
219 % {'user_link': user_link}), category='success')
220 % {'user_link': user_link}), category='success')
220 Session().commit()
221 Session().commit()
221 except formencode.Invalid as errors:
222 except formencode.Invalid as errors:
222 self._set_personal_repo_group_template_vars(c)
223 self._set_personal_repo_group_template_vars(c)
223 data = render(
224 data = render(
224 'rhodecode:templates/admin/users/user_add.mako',
225 'rhodecode:templates/admin/users/user_add.mako',
225 self._get_template_context(c), self.request)
226 self._get_template_context(c), self.request)
226 html = formencode.htmlfill.render(
227 html = formencode.htmlfill.render(
227 data,
228 data,
228 defaults=errors.value,
229 defaults=errors.value,
229 errors=errors.error_dict or {},
230 errors=errors.error_dict or {},
230 prefix_error=False,
231 prefix_error=False,
231 encoding="UTF-8",
232 encoding="UTF-8",
232 force_defaults=False
233 force_defaults=False
233 )
234 )
234 return Response(html)
235 return Response(html)
235 except UserCreationError as e:
236 except UserCreationError as e:
236 h.flash(e, 'error')
237 h.flash(e, 'error')
237 except Exception:
238 except Exception:
238 log.exception("Exception creation of user")
239 log.exception("Exception creation of user")
239 h.flash(_('Error occurred during creation of user %s')
240 h.flash(_('Error occurred during creation of user %s')
240 % self.request.POST.get('username'), category='error')
241 % self.request.POST.get('username'), category='error')
241 raise HTTPFound(h.route_path('users'))
242 raise HTTPFound(h.route_path('users'))
242
243
243
244
244 class UsersView(UserAppView):
245 class UsersView(UserAppView):
245 ALLOW_SCOPED_TOKENS = False
246 ALLOW_SCOPED_TOKENS = False
246 """
247 """
247 This view has alternative version inside EE, if modified please take a look
248 This view has alternative version inside EE, if modified please take a look
248 in there as well.
249 in there as well.
249 """
250 """
250
251
251 def load_default_context(self):
252 def load_default_context(self):
252 c = self._get_local_tmpl_context()
253 c = self._get_local_tmpl_context()
253 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
254 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
254 c.allowed_languages = [
255 c.allowed_languages = [
255 ('en', 'English (en)'),
256 ('en', 'English (en)'),
256 ('de', 'German (de)'),
257 ('de', 'German (de)'),
257 ('fr', 'French (fr)'),
258 ('fr', 'French (fr)'),
258 ('it', 'Italian (it)'),
259 ('it', 'Italian (it)'),
259 ('ja', 'Japanese (ja)'),
260 ('ja', 'Japanese (ja)'),
260 ('pl', 'Polish (pl)'),
261 ('pl', 'Polish (pl)'),
261 ('pt', 'Portuguese (pt)'),
262 ('pt', 'Portuguese (pt)'),
262 ('ru', 'Russian (ru)'),
263 ('ru', 'Russian (ru)'),
263 ('zh', 'Chinese (zh)'),
264 ('zh', 'Chinese (zh)'),
264 ]
265 ]
265 req = self.request
266 req = self.request
266
267
267 c.available_permissions = req.registry.settings['available_permissions']
268 c.available_permissions = req.registry.settings['available_permissions']
268 PermissionModel().set_global_permission_choices(
269 PermissionModel().set_global_permission_choices(
269 c, gettext_translator=req.translate)
270 c, gettext_translator=req.translate)
270
271
271 return c
272 return c
272
273
273 @LoginRequired()
274 @LoginRequired()
274 @HasPermissionAllDecorator('hg.admin')
275 @HasPermissionAllDecorator('hg.admin')
275 @CSRFRequired()
276 @CSRFRequired()
276 @view_config(
277 @view_config(
277 route_name='user_update', request_method='POST',
278 route_name='user_update', request_method='POST',
278 renderer='rhodecode:templates/admin/users/user_edit.mako')
279 renderer='rhodecode:templates/admin/users/user_edit.mako')
279 def user_update(self):
280 def user_update(self):
280 _ = self.request.translate
281 _ = self.request.translate
281 c = self.load_default_context()
282 c = self.load_default_context()
282
283
283 user_id = self.db_user_id
284 user_id = self.db_user_id
284 c.user = self.db_user
285 c.user = self.db_user
285
286
286 c.active = 'profile'
287 c.active = 'profile'
287 c.extern_type = c.user.extern_type
288 c.extern_type = c.user.extern_type
288 c.extern_name = c.user.extern_name
289 c.extern_name = c.user.extern_name
289 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
290 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
290 available_languages = [x[0] for x in c.allowed_languages]
291 available_languages = [x[0] for x in c.allowed_languages]
291 _form = UserForm(self.request.translate, edit=True,
292 _form = UserForm(self.request.translate, edit=True,
292 available_languages=available_languages,
293 available_languages=available_languages,
293 old_data={'user_id': user_id,
294 old_data={'user_id': user_id,
294 'email': c.user.email})()
295 'email': c.user.email})()
295 form_result = {}
296 form_result = {}
296 old_values = c.user.get_api_data()
297 old_values = c.user.get_api_data()
297 try:
298 try:
298 form_result = _form.to_python(dict(self.request.POST))
299 form_result = _form.to_python(dict(self.request.POST))
299 skip_attrs = ['extern_type', 'extern_name']
300 skip_attrs = ['extern_type', 'extern_name']
300 # TODO: plugin should define if username can be updated
301 # TODO: plugin should define if username can be updated
301 if c.extern_type != "rhodecode":
302 if c.extern_type != "rhodecode":
302 # forbid updating username for external accounts
303 # forbid updating username for external accounts
303 skip_attrs.append('username')
304 skip_attrs.append('username')
304
305
305 UserModel().update_user(
306 UserModel().update_user(
306 user_id, skip_attrs=skip_attrs, **form_result)
307 user_id, skip_attrs=skip_attrs, **form_result)
307
308
308 audit_logger.store_web(
309 audit_logger.store_web(
309 'user.edit', action_data={'old_data': old_values},
310 'user.edit', action_data={'old_data': old_values},
310 user=c.rhodecode_user)
311 user=c.rhodecode_user)
311
312
312 Session().commit()
313 Session().commit()
313 h.flash(_('User updated successfully'), category='success')
314 h.flash(_('User updated successfully'), category='success')
314 except formencode.Invalid as errors:
315 except formencode.Invalid as errors:
315 data = render(
316 data = render(
316 'rhodecode:templates/admin/users/user_edit.mako',
317 'rhodecode:templates/admin/users/user_edit.mako',
317 self._get_template_context(c), self.request)
318 self._get_template_context(c), self.request)
318 html = formencode.htmlfill.render(
319 html = formencode.htmlfill.render(
319 data,
320 data,
320 defaults=errors.value,
321 defaults=errors.value,
321 errors=errors.error_dict or {},
322 errors=errors.error_dict or {},
322 prefix_error=False,
323 prefix_error=False,
323 encoding="UTF-8",
324 encoding="UTF-8",
324 force_defaults=False
325 force_defaults=False
325 )
326 )
326 return Response(html)
327 return Response(html)
327 except UserCreationError as e:
328 except UserCreationError as e:
328 h.flash(e, 'error')
329 h.flash(e, 'error')
329 except Exception:
330 except Exception:
330 log.exception("Exception updating user")
331 log.exception("Exception updating user")
331 h.flash(_('Error occurred during update of user %s')
332 h.flash(_('Error occurred during update of user %s')
332 % form_result.get('username'), category='error')
333 % form_result.get('username'), category='error')
333 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
334 raise HTTPFound(h.route_path('user_edit', user_id=user_id))
334
335
335 @LoginRequired()
336 @LoginRequired()
336 @HasPermissionAllDecorator('hg.admin')
337 @HasPermissionAllDecorator('hg.admin')
337 @CSRFRequired()
338 @CSRFRequired()
338 @view_config(
339 @view_config(
339 route_name='user_delete', request_method='POST',
340 route_name='user_delete', request_method='POST',
340 renderer='rhodecode:templates/admin/users/user_edit.mako')
341 renderer='rhodecode:templates/admin/users/user_edit.mako')
341 def user_delete(self):
342 def user_delete(self):
342 _ = self.request.translate
343 _ = self.request.translate
343 c = self.load_default_context()
344 c = self.load_default_context()
344 c.user = self.db_user
345 c.user = self.db_user
345
346
346 _repos = c.user.repositories
347 _repos = c.user.repositories
347 _repo_groups = c.user.repository_groups
348 _repo_groups = c.user.repository_groups
348 _user_groups = c.user.user_groups
349 _user_groups = c.user.user_groups
349
350
350 handle_repos = None
351 handle_repos = None
351 handle_repo_groups = None
352 handle_repo_groups = None
352 handle_user_groups = None
353 handle_user_groups = None
353 # dummy call for flash of handle
354 # dummy call for flash of handle
354 set_handle_flash_repos = lambda: None
355 set_handle_flash_repos = lambda: None
355 set_handle_flash_repo_groups = lambda: None
356 set_handle_flash_repo_groups = lambda: None
356 set_handle_flash_user_groups = lambda: None
357 set_handle_flash_user_groups = lambda: None
357
358
358 if _repos and self.request.POST.get('user_repos'):
359 if _repos and self.request.POST.get('user_repos'):
359 do = self.request.POST['user_repos']
360 do = self.request.POST['user_repos']
360 if do == 'detach':
361 if do == 'detach':
361 handle_repos = 'detach'
362 handle_repos = 'detach'
362 set_handle_flash_repos = lambda: h.flash(
363 set_handle_flash_repos = lambda: h.flash(
363 _('Detached %s repositories') % len(_repos),
364 _('Detached %s repositories') % len(_repos),
364 category='success')
365 category='success')
365 elif do == 'delete':
366 elif do == 'delete':
366 handle_repos = 'delete'
367 handle_repos = 'delete'
367 set_handle_flash_repos = lambda: h.flash(
368 set_handle_flash_repos = lambda: h.flash(
368 _('Deleted %s repositories') % len(_repos),
369 _('Deleted %s repositories') % len(_repos),
369 category='success')
370 category='success')
370
371
371 if _repo_groups and self.request.POST.get('user_repo_groups'):
372 if _repo_groups and self.request.POST.get('user_repo_groups'):
372 do = self.request.POST['user_repo_groups']
373 do = self.request.POST['user_repo_groups']
373 if do == 'detach':
374 if do == 'detach':
374 handle_repo_groups = 'detach'
375 handle_repo_groups = 'detach'
375 set_handle_flash_repo_groups = lambda: h.flash(
376 set_handle_flash_repo_groups = lambda: h.flash(
376 _('Detached %s repository groups') % len(_repo_groups),
377 _('Detached %s repository groups') % len(_repo_groups),
377 category='success')
378 category='success')
378 elif do == 'delete':
379 elif do == 'delete':
379 handle_repo_groups = 'delete'
380 handle_repo_groups = 'delete'
380 set_handle_flash_repo_groups = lambda: h.flash(
381 set_handle_flash_repo_groups = lambda: h.flash(
381 _('Deleted %s repository groups') % len(_repo_groups),
382 _('Deleted %s repository groups') % len(_repo_groups),
382 category='success')
383 category='success')
383
384
384 if _user_groups and self.request.POST.get('user_user_groups'):
385 if _user_groups and self.request.POST.get('user_user_groups'):
385 do = self.request.POST['user_user_groups']
386 do = self.request.POST['user_user_groups']
386 if do == 'detach':
387 if do == 'detach':
387 handle_user_groups = 'detach'
388 handle_user_groups = 'detach'
388 set_handle_flash_user_groups = lambda: h.flash(
389 set_handle_flash_user_groups = lambda: h.flash(
389 _('Detached %s user groups') % len(_user_groups),
390 _('Detached %s user groups') % len(_user_groups),
390 category='success')
391 category='success')
391 elif do == 'delete':
392 elif do == 'delete':
392 handle_user_groups = 'delete'
393 handle_user_groups = 'delete'
393 set_handle_flash_user_groups = lambda: h.flash(
394 set_handle_flash_user_groups = lambda: h.flash(
394 _('Deleted %s user groups') % len(_user_groups),
395 _('Deleted %s user groups') % len(_user_groups),
395 category='success')
396 category='success')
396
397
397 old_values = c.user.get_api_data()
398 old_values = c.user.get_api_data()
398 try:
399 try:
399 UserModel().delete(c.user, handle_repos=handle_repos,
400 UserModel().delete(c.user, handle_repos=handle_repos,
400 handle_repo_groups=handle_repo_groups,
401 handle_repo_groups=handle_repo_groups,
401 handle_user_groups=handle_user_groups)
402 handle_user_groups=handle_user_groups)
402
403
403 audit_logger.store_web(
404 audit_logger.store_web(
404 'user.delete', action_data={'old_data': old_values},
405 'user.delete', action_data={'old_data': old_values},
405 user=c.rhodecode_user)
406 user=c.rhodecode_user)
406
407
407 Session().commit()
408 Session().commit()
408 set_handle_flash_repos()
409 set_handle_flash_repos()
409 set_handle_flash_repo_groups()
410 set_handle_flash_repo_groups()
410 set_handle_flash_user_groups()
411 set_handle_flash_user_groups()
411 h.flash(_('Successfully deleted user'), category='success')
412 h.flash(_('Successfully deleted user'), category='success')
412 except (UserOwnsReposException, UserOwnsRepoGroupsException,
413 except (UserOwnsReposException, UserOwnsRepoGroupsException,
413 UserOwnsUserGroupsException, DefaultUserException) as e:
414 UserOwnsUserGroupsException, DefaultUserException) as e:
414 h.flash(e, category='warning')
415 h.flash(e, category='warning')
415 except Exception:
416 except Exception:
416 log.exception("Exception during deletion of user")
417 log.exception("Exception during deletion of user")
417 h.flash(_('An error occurred during deletion of user'),
418 h.flash(_('An error occurred during deletion of user'),
418 category='error')
419 category='error')
419 raise HTTPFound(h.route_path('users'))
420 raise HTTPFound(h.route_path('users'))
420
421
421 @LoginRequired()
422 @LoginRequired()
422 @HasPermissionAllDecorator('hg.admin')
423 @HasPermissionAllDecorator('hg.admin')
423 @view_config(
424 @view_config(
424 route_name='user_edit', request_method='GET',
425 route_name='user_edit', request_method='GET',
425 renderer='rhodecode:templates/admin/users/user_edit.mako')
426 renderer='rhodecode:templates/admin/users/user_edit.mako')
426 def user_edit(self):
427 def user_edit(self):
427 _ = self.request.translate
428 _ = self.request.translate
428 c = self.load_default_context()
429 c = self.load_default_context()
429 c.user = self.db_user
430 c.user = self.db_user
430
431
431 c.active = 'profile'
432 c.active = 'profile'
432 c.extern_type = c.user.extern_type
433 c.extern_type = c.user.extern_type
433 c.extern_name = c.user.extern_name
434 c.extern_name = c.user.extern_name
434 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
435 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
435
436
436 defaults = c.user.get_dict()
437 defaults = c.user.get_dict()
437 defaults.update({'language': c.user.user_data.get('language')})
438 defaults.update({'language': c.user.user_data.get('language')})
438
439
439 data = render(
440 data = render(
440 'rhodecode:templates/admin/users/user_edit.mako',
441 'rhodecode:templates/admin/users/user_edit.mako',
441 self._get_template_context(c), self.request)
442 self._get_template_context(c), self.request)
442 html = formencode.htmlfill.render(
443 html = formencode.htmlfill.render(
443 data,
444 data,
444 defaults=defaults,
445 defaults=defaults,
445 encoding="UTF-8",
446 encoding="UTF-8",
446 force_defaults=False
447 force_defaults=False
447 )
448 )
448 return Response(html)
449 return Response(html)
449
450
450 @LoginRequired()
451 @LoginRequired()
451 @HasPermissionAllDecorator('hg.admin')
452 @HasPermissionAllDecorator('hg.admin')
452 @view_config(
453 @view_config(
453 route_name='user_edit_advanced', request_method='GET',
454 route_name='user_edit_advanced', request_method='GET',
454 renderer='rhodecode:templates/admin/users/user_edit.mako')
455 renderer='rhodecode:templates/admin/users/user_edit.mako')
455 def user_edit_advanced(self):
456 def user_edit_advanced(self):
456 _ = self.request.translate
457 _ = self.request.translate
457 c = self.load_default_context()
458 c = self.load_default_context()
458
459
459 user_id = self.db_user_id
460 user_id = self.db_user_id
460 c.user = self.db_user
461 c.user = self.db_user
461
462
462 c.active = 'advanced'
463 c.active = 'advanced'
463 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
464 c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id)
464 c.personal_repo_group_name = RepoGroupModel()\
465 c.personal_repo_group_name = RepoGroupModel()\
465 .get_personal_group_name(c.user)
466 .get_personal_group_name(c.user)
466
467
467 c.user_to_review_rules = sorted(
468 c.user_to_review_rules = sorted(
468 (x.user for x in c.user.user_review_rules),
469 (x.user for x in c.user.user_review_rules),
469 key=lambda u: u.username.lower())
470 key=lambda u: u.username.lower())
470
471
471 c.first_admin = User.get_first_super_admin()
472 c.first_admin = User.get_first_super_admin()
472 defaults = c.user.get_dict()
473 defaults = c.user.get_dict()
473
474
474 # Interim workaround if the user participated on any pull requests as a
475 # Interim workaround if the user participated on any pull requests as a
475 # reviewer.
476 # reviewer.
476 has_review = len(c.user.reviewer_pull_requests)
477 has_review = len(c.user.reviewer_pull_requests)
477 c.can_delete_user = not has_review
478 c.can_delete_user = not has_review
478 c.can_delete_user_message = ''
479 c.can_delete_user_message = ''
479 inactive_link = h.link_to(
480 inactive_link = h.link_to(
480 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
481 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active'))
481 if has_review == 1:
482 if has_review == 1:
482 c.can_delete_user_message = h.literal(_(
483 c.can_delete_user_message = h.literal(_(
483 'The user participates as reviewer in {} pull request and '
484 'The user participates as reviewer in {} pull request and '
484 'cannot be deleted. \nYou can set the user to '
485 'cannot be deleted. \nYou can set the user to '
485 '"{}" instead of deleting it.').format(
486 '"{}" instead of deleting it.').format(
486 has_review, inactive_link))
487 has_review, inactive_link))
487 elif has_review:
488 elif has_review:
488 c.can_delete_user_message = h.literal(_(
489 c.can_delete_user_message = h.literal(_(
489 'The user participates as reviewer in {} pull requests and '
490 'The user participates as reviewer in {} pull requests and '
490 'cannot be deleted. \nYou can set the user to '
491 'cannot be deleted. \nYou can set the user to '
491 '"{}" instead of deleting it.').format(
492 '"{}" instead of deleting it.').format(
492 has_review, inactive_link))
493 has_review, inactive_link))
493
494
494 data = render(
495 data = render(
495 'rhodecode:templates/admin/users/user_edit.mako',
496 'rhodecode:templates/admin/users/user_edit.mako',
496 self._get_template_context(c), self.request)
497 self._get_template_context(c), self.request)
497 html = formencode.htmlfill.render(
498 html = formencode.htmlfill.render(
498 data,
499 data,
499 defaults=defaults,
500 defaults=defaults,
500 encoding="UTF-8",
501 encoding="UTF-8",
501 force_defaults=False
502 force_defaults=False
502 )
503 )
503 return Response(html)
504 return Response(html)
504
505
505 @LoginRequired()
506 @LoginRequired()
506 @HasPermissionAllDecorator('hg.admin')
507 @HasPermissionAllDecorator('hg.admin')
507 @view_config(
508 @view_config(
508 route_name='user_edit_global_perms', request_method='GET',
509 route_name='user_edit_global_perms', request_method='GET',
509 renderer='rhodecode:templates/admin/users/user_edit.mako')
510 renderer='rhodecode:templates/admin/users/user_edit.mako')
510 def user_edit_global_perms(self):
511 def user_edit_global_perms(self):
511 _ = self.request.translate
512 _ = self.request.translate
512 c = self.load_default_context()
513 c = self.load_default_context()
513 c.user = self.db_user
514 c.user = self.db_user
514
515
515 c.active = 'global_perms'
516 c.active = 'global_perms'
516
517
517 c.default_user = User.get_default_user()
518 c.default_user = User.get_default_user()
518 defaults = c.user.get_dict()
519 defaults = c.user.get_dict()
519 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
520 defaults.update(c.default_user.get_default_perms(suffix='_inherited'))
520 defaults.update(c.default_user.get_default_perms())
521 defaults.update(c.default_user.get_default_perms())
521 defaults.update(c.user.get_default_perms())
522 defaults.update(c.user.get_default_perms())
522
523
523 data = render(
524 data = render(
524 'rhodecode:templates/admin/users/user_edit.mako',
525 'rhodecode:templates/admin/users/user_edit.mako',
525 self._get_template_context(c), self.request)
526 self._get_template_context(c), self.request)
526 html = formencode.htmlfill.render(
527 html = formencode.htmlfill.render(
527 data,
528 data,
528 defaults=defaults,
529 defaults=defaults,
529 encoding="UTF-8",
530 encoding="UTF-8",
530 force_defaults=False
531 force_defaults=False
531 )
532 )
532 return Response(html)
533 return Response(html)
533
534
534 @LoginRequired()
535 @LoginRequired()
535 @HasPermissionAllDecorator('hg.admin')
536 @HasPermissionAllDecorator('hg.admin')
536 @CSRFRequired()
537 @CSRFRequired()
537 @view_config(
538 @view_config(
538 route_name='user_edit_global_perms_update', request_method='POST',
539 route_name='user_edit_global_perms_update', request_method='POST',
539 renderer='rhodecode:templates/admin/users/user_edit.mako')
540 renderer='rhodecode:templates/admin/users/user_edit.mako')
540 def user_edit_global_perms_update(self):
541 def user_edit_global_perms_update(self):
541 _ = self.request.translate
542 _ = self.request.translate
542 c = self.load_default_context()
543 c = self.load_default_context()
543
544
544 user_id = self.db_user_id
545 user_id = self.db_user_id
545 c.user = self.db_user
546 c.user = self.db_user
546
547
547 c.active = 'global_perms'
548 c.active = 'global_perms'
548 try:
549 try:
549 # first stage that verifies the checkbox
550 # first stage that verifies the checkbox
550 _form = UserIndividualPermissionsForm(self.request.translate)
551 _form = UserIndividualPermissionsForm(self.request.translate)
551 form_result = _form.to_python(dict(self.request.POST))
552 form_result = _form.to_python(dict(self.request.POST))
552 inherit_perms = form_result['inherit_default_permissions']
553 inherit_perms = form_result['inherit_default_permissions']
553 c.user.inherit_default_permissions = inherit_perms
554 c.user.inherit_default_permissions = inherit_perms
554 Session().add(c.user)
555 Session().add(c.user)
555
556
556 if not inherit_perms:
557 if not inherit_perms:
557 # only update the individual ones if we un check the flag
558 # only update the individual ones if we un check the flag
558 _form = UserPermissionsForm(
559 _form = UserPermissionsForm(
559 self.request.translate,
560 self.request.translate,
560 [x[0] for x in c.repo_create_choices],
561 [x[0] for x in c.repo_create_choices],
561 [x[0] for x in c.repo_create_on_write_choices],
562 [x[0] for x in c.repo_create_on_write_choices],
562 [x[0] for x in c.repo_group_create_choices],
563 [x[0] for x in c.repo_group_create_choices],
563 [x[0] for x in c.user_group_create_choices],
564 [x[0] for x in c.user_group_create_choices],
564 [x[0] for x in c.fork_choices],
565 [x[0] for x in c.fork_choices],
565 [x[0] for x in c.inherit_default_permission_choices])()
566 [x[0] for x in c.inherit_default_permission_choices])()
566
567
567 form_result = _form.to_python(dict(self.request.POST))
568 form_result = _form.to_python(dict(self.request.POST))
568 form_result.update({'perm_user_id': c.user.user_id})
569 form_result.update({'perm_user_id': c.user.user_id})
569
570
570 PermissionModel().update_user_permissions(form_result)
571 PermissionModel().update_user_permissions(form_result)
571
572
572 # TODO(marcink): implement global permissions
573 # TODO(marcink): implement global permissions
573 # audit_log.store_web('user.edit.permissions')
574 # audit_log.store_web('user.edit.permissions')
574
575
575 Session().commit()
576 Session().commit()
577
576 h.flash(_('User global permissions updated successfully'),
578 h.flash(_('User global permissions updated successfully'),
577 category='success')
579 category='success')
578
580
579 except formencode.Invalid as errors:
581 except formencode.Invalid as errors:
580 data = render(
582 data = render(
581 'rhodecode:templates/admin/users/user_edit.mako',
583 'rhodecode:templates/admin/users/user_edit.mako',
582 self._get_template_context(c), self.request)
584 self._get_template_context(c), self.request)
583 html = formencode.htmlfill.render(
585 html = formencode.htmlfill.render(
584 data,
586 data,
585 defaults=errors.value,
587 defaults=errors.value,
586 errors=errors.error_dict or {},
588 errors=errors.error_dict or {},
587 prefix_error=False,
589 prefix_error=False,
588 encoding="UTF-8",
590 encoding="UTF-8",
589 force_defaults=False
591 force_defaults=False
590 )
592 )
591 return Response(html)
593 return Response(html)
592 except Exception:
594 except Exception:
593 log.exception("Exception during permissions saving")
595 log.exception("Exception during permissions saving")
594 h.flash(_('An error occurred during permissions saving'),
596 h.flash(_('An error occurred during permissions saving'),
595 category='error')
597 category='error')
598
599 affected_user_ids = [user_id]
600 events.trigger(events.UserPermissionsChange(affected_user_ids))
596 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
601 raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id))
597
602
598 @LoginRequired()
603 @LoginRequired()
599 @HasPermissionAllDecorator('hg.admin')
604 @HasPermissionAllDecorator('hg.admin')
600 @CSRFRequired()
605 @CSRFRequired()
601 @view_config(
606 @view_config(
602 route_name='user_enable_force_password_reset', request_method='POST',
607 route_name='user_enable_force_password_reset', request_method='POST',
603 renderer='rhodecode:templates/admin/users/user_edit.mako')
608 renderer='rhodecode:templates/admin/users/user_edit.mako')
604 def user_enable_force_password_reset(self):
609 def user_enable_force_password_reset(self):
605 _ = self.request.translate
610 _ = self.request.translate
606 c = self.load_default_context()
611 c = self.load_default_context()
607
612
608 user_id = self.db_user_id
613 user_id = self.db_user_id
609 c.user = self.db_user
614 c.user = self.db_user
610
615
611 try:
616 try:
612 c.user.update_userdata(force_password_change=True)
617 c.user.update_userdata(force_password_change=True)
613
618
614 msg = _('Force password change enabled for user')
619 msg = _('Force password change enabled for user')
615 audit_logger.store_web('user.edit.password_reset.enabled',
620 audit_logger.store_web('user.edit.password_reset.enabled',
616 user=c.rhodecode_user)
621 user=c.rhodecode_user)
617
622
618 Session().commit()
623 Session().commit()
619 h.flash(msg, category='success')
624 h.flash(msg, category='success')
620 except Exception:
625 except Exception:
621 log.exception("Exception during password reset for user")
626 log.exception("Exception during password reset for user")
622 h.flash(_('An error occurred during password reset for user'),
627 h.flash(_('An error occurred during password reset for user'),
623 category='error')
628 category='error')
624
629
625 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
630 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
626
631
627 @LoginRequired()
632 @LoginRequired()
628 @HasPermissionAllDecorator('hg.admin')
633 @HasPermissionAllDecorator('hg.admin')
629 @CSRFRequired()
634 @CSRFRequired()
630 @view_config(
635 @view_config(
631 route_name='user_disable_force_password_reset', request_method='POST',
636 route_name='user_disable_force_password_reset', request_method='POST',
632 renderer='rhodecode:templates/admin/users/user_edit.mako')
637 renderer='rhodecode:templates/admin/users/user_edit.mako')
633 def user_disable_force_password_reset(self):
638 def user_disable_force_password_reset(self):
634 _ = self.request.translate
639 _ = self.request.translate
635 c = self.load_default_context()
640 c = self.load_default_context()
636
641
637 user_id = self.db_user_id
642 user_id = self.db_user_id
638 c.user = self.db_user
643 c.user = self.db_user
639
644
640 try:
645 try:
641 c.user.update_userdata(force_password_change=False)
646 c.user.update_userdata(force_password_change=False)
642
647
643 msg = _('Force password change disabled for user')
648 msg = _('Force password change disabled for user')
644 audit_logger.store_web(
649 audit_logger.store_web(
645 'user.edit.password_reset.disabled',
650 'user.edit.password_reset.disabled',
646 user=c.rhodecode_user)
651 user=c.rhodecode_user)
647
652
648 Session().commit()
653 Session().commit()
649 h.flash(msg, category='success')
654 h.flash(msg, category='success')
650 except Exception:
655 except Exception:
651 log.exception("Exception during password reset for user")
656 log.exception("Exception during password reset for user")
652 h.flash(_('An error occurred during password reset for user'),
657 h.flash(_('An error occurred during password reset for user'),
653 category='error')
658 category='error')
654
659
655 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
660 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
656
661
657 @LoginRequired()
662 @LoginRequired()
658 @HasPermissionAllDecorator('hg.admin')
663 @HasPermissionAllDecorator('hg.admin')
659 @CSRFRequired()
664 @CSRFRequired()
660 @view_config(
665 @view_config(
661 route_name='user_create_personal_repo_group', request_method='POST',
666 route_name='user_create_personal_repo_group', request_method='POST',
662 renderer='rhodecode:templates/admin/users/user_edit.mako')
667 renderer='rhodecode:templates/admin/users/user_edit.mako')
663 def user_create_personal_repo_group(self):
668 def user_create_personal_repo_group(self):
664 """
669 """
665 Create personal repository group for this user
670 Create personal repository group for this user
666 """
671 """
667 from rhodecode.model.repo_group import RepoGroupModel
672 from rhodecode.model.repo_group import RepoGroupModel
668
673
669 _ = self.request.translate
674 _ = self.request.translate
670 c = self.load_default_context()
675 c = self.load_default_context()
671
676
672 user_id = self.db_user_id
677 user_id = self.db_user_id
673 c.user = self.db_user
678 c.user = self.db_user
674
679
675 personal_repo_group = RepoGroup.get_user_personal_repo_group(
680 personal_repo_group = RepoGroup.get_user_personal_repo_group(
676 c.user.user_id)
681 c.user.user_id)
677 if personal_repo_group:
682 if personal_repo_group:
678 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
683 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
679
684
680 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
685 personal_repo_group_name = RepoGroupModel().get_personal_group_name(
681 c.user)
686 c.user)
682 named_personal_group = RepoGroup.get_by_group_name(
687 named_personal_group = RepoGroup.get_by_group_name(
683 personal_repo_group_name)
688 personal_repo_group_name)
684 try:
689 try:
685
690
686 if named_personal_group and named_personal_group.user_id == c.user.user_id:
691 if named_personal_group and named_personal_group.user_id == c.user.user_id:
687 # migrate the same named group, and mark it as personal
692 # migrate the same named group, and mark it as personal
688 named_personal_group.personal = True
693 named_personal_group.personal = True
689 Session().add(named_personal_group)
694 Session().add(named_personal_group)
690 Session().commit()
695 Session().commit()
691 msg = _('Linked repository group `%s` as personal' % (
696 msg = _('Linked repository group `%s` as personal' % (
692 personal_repo_group_name,))
697 personal_repo_group_name,))
693 h.flash(msg, category='success')
698 h.flash(msg, category='success')
694 elif not named_personal_group:
699 elif not named_personal_group:
695 RepoGroupModel().create_personal_repo_group(c.user)
700 RepoGroupModel().create_personal_repo_group(c.user)
696
701
697 msg = _('Created repository group `%s`' % (
702 msg = _('Created repository group `%s`' % (
698 personal_repo_group_name,))
703 personal_repo_group_name,))
699 h.flash(msg, category='success')
704 h.flash(msg, category='success')
700 else:
705 else:
701 msg = _('Repository group `%s` is already taken' % (
706 msg = _('Repository group `%s` is already taken' % (
702 personal_repo_group_name,))
707 personal_repo_group_name,))
703 h.flash(msg, category='warning')
708 h.flash(msg, category='warning')
704 except Exception:
709 except Exception:
705 log.exception("Exception during repository group creation")
710 log.exception("Exception during repository group creation")
706 msg = _(
711 msg = _(
707 'An error occurred during repository group creation for user')
712 'An error occurred during repository group creation for user')
708 h.flash(msg, category='error')
713 h.flash(msg, category='error')
709 Session().rollback()
714 Session().rollback()
710
715
711 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
716 raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id))
712
717
713 @LoginRequired()
718 @LoginRequired()
714 @HasPermissionAllDecorator('hg.admin')
719 @HasPermissionAllDecorator('hg.admin')
715 @view_config(
720 @view_config(
716 route_name='edit_user_auth_tokens', request_method='GET',
721 route_name='edit_user_auth_tokens', request_method='GET',
717 renderer='rhodecode:templates/admin/users/user_edit.mako')
722 renderer='rhodecode:templates/admin/users/user_edit.mako')
718 def auth_tokens(self):
723 def auth_tokens(self):
719 _ = self.request.translate
724 _ = self.request.translate
720 c = self.load_default_context()
725 c = self.load_default_context()
721 c.user = self.db_user
726 c.user = self.db_user
722
727
723 c.active = 'auth_tokens'
728 c.active = 'auth_tokens'
724
729
725 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
730 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
726 c.role_values = [
731 c.role_values = [
727 (x, AuthTokenModel.cls._get_role_name(x))
732 (x, AuthTokenModel.cls._get_role_name(x))
728 for x in AuthTokenModel.cls.ROLES]
733 for x in AuthTokenModel.cls.ROLES]
729 c.role_options = [(c.role_values, _("Role"))]
734 c.role_options = [(c.role_values, _("Role"))]
730 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
735 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
731 c.user.user_id, show_expired=True)
736 c.user.user_id, show_expired=True)
732 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
737 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
733 return self._get_template_context(c)
738 return self._get_template_context(c)
734
739
735 def maybe_attach_token_scope(self, token):
740 def maybe_attach_token_scope(self, token):
736 # implemented in EE edition
741 # implemented in EE edition
737 pass
742 pass
738
743
739 @LoginRequired()
744 @LoginRequired()
740 @HasPermissionAllDecorator('hg.admin')
745 @HasPermissionAllDecorator('hg.admin')
741 @CSRFRequired()
746 @CSRFRequired()
742 @view_config(
747 @view_config(
743 route_name='edit_user_auth_tokens_add', request_method='POST')
748 route_name='edit_user_auth_tokens_add', request_method='POST')
744 def auth_tokens_add(self):
749 def auth_tokens_add(self):
745 _ = self.request.translate
750 _ = self.request.translate
746 c = self.load_default_context()
751 c = self.load_default_context()
747
752
748 user_id = self.db_user_id
753 user_id = self.db_user_id
749 c.user = self.db_user
754 c.user = self.db_user
750
755
751 user_data = c.user.get_api_data()
756 user_data = c.user.get_api_data()
752 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
757 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
753 description = self.request.POST.get('description')
758 description = self.request.POST.get('description')
754 role = self.request.POST.get('role')
759 role = self.request.POST.get('role')
755
760
756 token = UserModel().add_auth_token(
761 token = UserModel().add_auth_token(
757 user=c.user.user_id,
762 user=c.user.user_id,
758 lifetime_minutes=lifetime, role=role, description=description,
763 lifetime_minutes=lifetime, role=role, description=description,
759 scope_callback=self.maybe_attach_token_scope)
764 scope_callback=self.maybe_attach_token_scope)
760 token_data = token.get_api_data()
765 token_data = token.get_api_data()
761
766
762 audit_logger.store_web(
767 audit_logger.store_web(
763 'user.edit.token.add', action_data={
768 'user.edit.token.add', action_data={
764 'data': {'token': token_data, 'user': user_data}},
769 'data': {'token': token_data, 'user': user_data}},
765 user=self._rhodecode_user, )
770 user=self._rhodecode_user, )
766 Session().commit()
771 Session().commit()
767
772
768 h.flash(_("Auth token successfully created"), category='success')
773 h.flash(_("Auth token successfully created"), category='success')
769 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
774 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
770
775
771 @LoginRequired()
776 @LoginRequired()
772 @HasPermissionAllDecorator('hg.admin')
777 @HasPermissionAllDecorator('hg.admin')
773 @CSRFRequired()
778 @CSRFRequired()
774 @view_config(
779 @view_config(
775 route_name='edit_user_auth_tokens_delete', request_method='POST')
780 route_name='edit_user_auth_tokens_delete', request_method='POST')
776 def auth_tokens_delete(self):
781 def auth_tokens_delete(self):
777 _ = self.request.translate
782 _ = self.request.translate
778 c = self.load_default_context()
783 c = self.load_default_context()
779
784
780 user_id = self.db_user_id
785 user_id = self.db_user_id
781 c.user = self.db_user
786 c.user = self.db_user
782
787
783 user_data = c.user.get_api_data()
788 user_data = c.user.get_api_data()
784
789
785 del_auth_token = self.request.POST.get('del_auth_token')
790 del_auth_token = self.request.POST.get('del_auth_token')
786
791
787 if del_auth_token:
792 if del_auth_token:
788 token = UserApiKeys.get_or_404(del_auth_token)
793 token = UserApiKeys.get_or_404(del_auth_token)
789 token_data = token.get_api_data()
794 token_data = token.get_api_data()
790
795
791 AuthTokenModel().delete(del_auth_token, c.user.user_id)
796 AuthTokenModel().delete(del_auth_token, c.user.user_id)
792 audit_logger.store_web(
797 audit_logger.store_web(
793 'user.edit.token.delete', action_data={
798 'user.edit.token.delete', action_data={
794 'data': {'token': token_data, 'user': user_data}},
799 'data': {'token': token_data, 'user': user_data}},
795 user=self._rhodecode_user,)
800 user=self._rhodecode_user,)
796 Session().commit()
801 Session().commit()
797 h.flash(_("Auth token successfully deleted"), category='success')
802 h.flash(_("Auth token successfully deleted"), category='success')
798
803
799 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
804 return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id))
800
805
801 @LoginRequired()
806 @LoginRequired()
802 @HasPermissionAllDecorator('hg.admin')
807 @HasPermissionAllDecorator('hg.admin')
803 @view_config(
808 @view_config(
804 route_name='edit_user_ssh_keys', request_method='GET',
809 route_name='edit_user_ssh_keys', request_method='GET',
805 renderer='rhodecode:templates/admin/users/user_edit.mako')
810 renderer='rhodecode:templates/admin/users/user_edit.mako')
806 def ssh_keys(self):
811 def ssh_keys(self):
807 _ = self.request.translate
812 _ = self.request.translate
808 c = self.load_default_context()
813 c = self.load_default_context()
809 c.user = self.db_user
814 c.user = self.db_user
810
815
811 c.active = 'ssh_keys'
816 c.active = 'ssh_keys'
812 c.default_key = self.request.GET.get('default_key')
817 c.default_key = self.request.GET.get('default_key')
813 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
818 c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id)
814 return self._get_template_context(c)
819 return self._get_template_context(c)
815
820
816 @LoginRequired()
821 @LoginRequired()
817 @HasPermissionAllDecorator('hg.admin')
822 @HasPermissionAllDecorator('hg.admin')
818 @view_config(
823 @view_config(
819 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
824 route_name='edit_user_ssh_keys_generate_keypair', request_method='GET',
820 renderer='rhodecode:templates/admin/users/user_edit.mako')
825 renderer='rhodecode:templates/admin/users/user_edit.mako')
821 def ssh_keys_generate_keypair(self):
826 def ssh_keys_generate_keypair(self):
822 _ = self.request.translate
827 _ = self.request.translate
823 c = self.load_default_context()
828 c = self.load_default_context()
824
829
825 c.user = self.db_user
830 c.user = self.db_user
826
831
827 c.active = 'ssh_keys_generate'
832 c.active = 'ssh_keys_generate'
828 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
833 comment = 'RhodeCode-SSH {}'.format(c.user.email or '')
829 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
834 c.private, c.public = SshKeyModel().generate_keypair(comment=comment)
830
835
831 return self._get_template_context(c)
836 return self._get_template_context(c)
832
837
833 @LoginRequired()
838 @LoginRequired()
834 @HasPermissionAllDecorator('hg.admin')
839 @HasPermissionAllDecorator('hg.admin')
835 @CSRFRequired()
840 @CSRFRequired()
836 @view_config(
841 @view_config(
837 route_name='edit_user_ssh_keys_add', request_method='POST')
842 route_name='edit_user_ssh_keys_add', request_method='POST')
838 def ssh_keys_add(self):
843 def ssh_keys_add(self):
839 _ = self.request.translate
844 _ = self.request.translate
840 c = self.load_default_context()
845 c = self.load_default_context()
841
846
842 user_id = self.db_user_id
847 user_id = self.db_user_id
843 c.user = self.db_user
848 c.user = self.db_user
844
849
845 user_data = c.user.get_api_data()
850 user_data = c.user.get_api_data()
846 key_data = self.request.POST.get('key_data')
851 key_data = self.request.POST.get('key_data')
847 description = self.request.POST.get('description')
852 description = self.request.POST.get('description')
848
853
849 fingerprint = 'unknown'
854 fingerprint = 'unknown'
850 try:
855 try:
851 if not key_data:
856 if not key_data:
852 raise ValueError('Please add a valid public key')
857 raise ValueError('Please add a valid public key')
853
858
854 key = SshKeyModel().parse_key(key_data.strip())
859 key = SshKeyModel().parse_key(key_data.strip())
855 fingerprint = key.hash_md5()
860 fingerprint = key.hash_md5()
856
861
857 ssh_key = SshKeyModel().create(
862 ssh_key = SshKeyModel().create(
858 c.user.user_id, fingerprint, key.keydata, description)
863 c.user.user_id, fingerprint, key.keydata, description)
859 ssh_key_data = ssh_key.get_api_data()
864 ssh_key_data = ssh_key.get_api_data()
860
865
861 audit_logger.store_web(
866 audit_logger.store_web(
862 'user.edit.ssh_key.add', action_data={
867 'user.edit.ssh_key.add', action_data={
863 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
868 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
864 user=self._rhodecode_user, )
869 user=self._rhodecode_user, )
865 Session().commit()
870 Session().commit()
866
871
867 # Trigger an event on change of keys.
872 # Trigger an event on change of keys.
868 trigger(SshKeyFileChangeEvent(), self.request.registry)
873 trigger(SshKeyFileChangeEvent(), self.request.registry)
869
874
870 h.flash(_("Ssh Key successfully created"), category='success')
875 h.flash(_("Ssh Key successfully created"), category='success')
871
876
872 except IntegrityError:
877 except IntegrityError:
873 log.exception("Exception during ssh key saving")
878 log.exception("Exception during ssh key saving")
874 err = 'Such key with fingerprint `{}` already exists, ' \
879 err = 'Such key with fingerprint `{}` already exists, ' \
875 'please use a different one'.format(fingerprint)
880 'please use a different one'.format(fingerprint)
876 h.flash(_('An error occurred during ssh key saving: {}').format(err),
881 h.flash(_('An error occurred during ssh key saving: {}').format(err),
877 category='error')
882 category='error')
878 except Exception as e:
883 except Exception as e:
879 log.exception("Exception during ssh key saving")
884 log.exception("Exception during ssh key saving")
880 h.flash(_('An error occurred during ssh key saving: {}').format(e),
885 h.flash(_('An error occurred during ssh key saving: {}').format(e),
881 category='error')
886 category='error')
882
887
883 return HTTPFound(
888 return HTTPFound(
884 h.route_path('edit_user_ssh_keys', user_id=user_id))
889 h.route_path('edit_user_ssh_keys', user_id=user_id))
885
890
886 @LoginRequired()
891 @LoginRequired()
887 @HasPermissionAllDecorator('hg.admin')
892 @HasPermissionAllDecorator('hg.admin')
888 @CSRFRequired()
893 @CSRFRequired()
889 @view_config(
894 @view_config(
890 route_name='edit_user_ssh_keys_delete', request_method='POST')
895 route_name='edit_user_ssh_keys_delete', request_method='POST')
891 def ssh_keys_delete(self):
896 def ssh_keys_delete(self):
892 _ = self.request.translate
897 _ = self.request.translate
893 c = self.load_default_context()
898 c = self.load_default_context()
894
899
895 user_id = self.db_user_id
900 user_id = self.db_user_id
896 c.user = self.db_user
901 c.user = self.db_user
897
902
898 user_data = c.user.get_api_data()
903 user_data = c.user.get_api_data()
899
904
900 del_ssh_key = self.request.POST.get('del_ssh_key')
905 del_ssh_key = self.request.POST.get('del_ssh_key')
901
906
902 if del_ssh_key:
907 if del_ssh_key:
903 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
908 ssh_key = UserSshKeys.get_or_404(del_ssh_key)
904 ssh_key_data = ssh_key.get_api_data()
909 ssh_key_data = ssh_key.get_api_data()
905
910
906 SshKeyModel().delete(del_ssh_key, c.user.user_id)
911 SshKeyModel().delete(del_ssh_key, c.user.user_id)
907 audit_logger.store_web(
912 audit_logger.store_web(
908 'user.edit.ssh_key.delete', action_data={
913 'user.edit.ssh_key.delete', action_data={
909 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
914 'data': {'ssh_key': ssh_key_data, 'user': user_data}},
910 user=self._rhodecode_user,)
915 user=self._rhodecode_user,)
911 Session().commit()
916 Session().commit()
912 # Trigger an event on change of keys.
917 # Trigger an event on change of keys.
913 trigger(SshKeyFileChangeEvent(), self.request.registry)
918 trigger(SshKeyFileChangeEvent(), self.request.registry)
914 h.flash(_("Ssh key successfully deleted"), category='success')
919 h.flash(_("Ssh key successfully deleted"), category='success')
915
920
916 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
921 return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id))
917
922
918 @LoginRequired()
923 @LoginRequired()
919 @HasPermissionAllDecorator('hg.admin')
924 @HasPermissionAllDecorator('hg.admin')
920 @view_config(
925 @view_config(
921 route_name='edit_user_emails', request_method='GET',
926 route_name='edit_user_emails', request_method='GET',
922 renderer='rhodecode:templates/admin/users/user_edit.mako')
927 renderer='rhodecode:templates/admin/users/user_edit.mako')
923 def emails(self):
928 def emails(self):
924 _ = self.request.translate
929 _ = self.request.translate
925 c = self.load_default_context()
930 c = self.load_default_context()
926 c.user = self.db_user
931 c.user = self.db_user
927
932
928 c.active = 'emails'
933 c.active = 'emails'
929 c.user_email_map = UserEmailMap.query() \
934 c.user_email_map = UserEmailMap.query() \
930 .filter(UserEmailMap.user == c.user).all()
935 .filter(UserEmailMap.user == c.user).all()
931
936
932 return self._get_template_context(c)
937 return self._get_template_context(c)
933
938
934 @LoginRequired()
939 @LoginRequired()
935 @HasPermissionAllDecorator('hg.admin')
940 @HasPermissionAllDecorator('hg.admin')
936 @CSRFRequired()
941 @CSRFRequired()
937 @view_config(
942 @view_config(
938 route_name='edit_user_emails_add', request_method='POST')
943 route_name='edit_user_emails_add', request_method='POST')
939 def emails_add(self):
944 def emails_add(self):
940 _ = self.request.translate
945 _ = self.request.translate
941 c = self.load_default_context()
946 c = self.load_default_context()
942
947
943 user_id = self.db_user_id
948 user_id = self.db_user_id
944 c.user = self.db_user
949 c.user = self.db_user
945
950
946 email = self.request.POST.get('new_email')
951 email = self.request.POST.get('new_email')
947 user_data = c.user.get_api_data()
952 user_data = c.user.get_api_data()
948 try:
953 try:
949
954
950 form = UserExtraEmailForm(self.request.translate)()
955 form = UserExtraEmailForm(self.request.translate)()
951 data = form.to_python({'email': email})
956 data = form.to_python({'email': email})
952 email = data['email']
957 email = data['email']
953
958
954 UserModel().add_extra_email(c.user.user_id, email)
959 UserModel().add_extra_email(c.user.user_id, email)
955 audit_logger.store_web(
960 audit_logger.store_web(
956 'user.edit.email.add',
961 'user.edit.email.add',
957 action_data={'email': email, 'user': user_data},
962 action_data={'email': email, 'user': user_data},
958 user=self._rhodecode_user)
963 user=self._rhodecode_user)
959 Session().commit()
964 Session().commit()
960 h.flash(_("Added new email address `%s` for user account") % email,
965 h.flash(_("Added new email address `%s` for user account") % email,
961 category='success')
966 category='success')
962 except formencode.Invalid as error:
967 except formencode.Invalid as error:
963 h.flash(h.escape(error.error_dict['email']), category='error')
968 h.flash(h.escape(error.error_dict['email']), category='error')
964 except IntegrityError:
969 except IntegrityError:
965 log.warning("Email %s already exists", email)
970 log.warning("Email %s already exists", email)
966 h.flash(_('Email `{}` is already registered for another user.').format(email),
971 h.flash(_('Email `{}` is already registered for another user.').format(email),
967 category='error')
972 category='error')
968 except Exception:
973 except Exception:
969 log.exception("Exception during email saving")
974 log.exception("Exception during email saving")
970 h.flash(_('An error occurred during email saving'),
975 h.flash(_('An error occurred during email saving'),
971 category='error')
976 category='error')
972 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
977 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
973
978
974 @LoginRequired()
979 @LoginRequired()
975 @HasPermissionAllDecorator('hg.admin')
980 @HasPermissionAllDecorator('hg.admin')
976 @CSRFRequired()
981 @CSRFRequired()
977 @view_config(
982 @view_config(
978 route_name='edit_user_emails_delete', request_method='POST')
983 route_name='edit_user_emails_delete', request_method='POST')
979 def emails_delete(self):
984 def emails_delete(self):
980 _ = self.request.translate
985 _ = self.request.translate
981 c = self.load_default_context()
986 c = self.load_default_context()
982
987
983 user_id = self.db_user_id
988 user_id = self.db_user_id
984 c.user = self.db_user
989 c.user = self.db_user
985
990
986 email_id = self.request.POST.get('del_email_id')
991 email_id = self.request.POST.get('del_email_id')
987 user_model = UserModel()
992 user_model = UserModel()
988
993
989 email = UserEmailMap.query().get(email_id).email
994 email = UserEmailMap.query().get(email_id).email
990 user_data = c.user.get_api_data()
995 user_data = c.user.get_api_data()
991 user_model.delete_extra_email(c.user.user_id, email_id)
996 user_model.delete_extra_email(c.user.user_id, email_id)
992 audit_logger.store_web(
997 audit_logger.store_web(
993 'user.edit.email.delete',
998 'user.edit.email.delete',
994 action_data={'email': email, 'user': user_data},
999 action_data={'email': email, 'user': user_data},
995 user=self._rhodecode_user)
1000 user=self._rhodecode_user)
996 Session().commit()
1001 Session().commit()
997 h.flash(_("Removed email address from user account"),
1002 h.flash(_("Removed email address from user account"),
998 category='success')
1003 category='success')
999 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1004 raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id))
1000
1005
1001 @LoginRequired()
1006 @LoginRequired()
1002 @HasPermissionAllDecorator('hg.admin')
1007 @HasPermissionAllDecorator('hg.admin')
1003 @view_config(
1008 @view_config(
1004 route_name='edit_user_ips', request_method='GET',
1009 route_name='edit_user_ips', request_method='GET',
1005 renderer='rhodecode:templates/admin/users/user_edit.mako')
1010 renderer='rhodecode:templates/admin/users/user_edit.mako')
1006 def ips(self):
1011 def ips(self):
1007 _ = self.request.translate
1012 _ = self.request.translate
1008 c = self.load_default_context()
1013 c = self.load_default_context()
1009 c.user = self.db_user
1014 c.user = self.db_user
1010
1015
1011 c.active = 'ips'
1016 c.active = 'ips'
1012 c.user_ip_map = UserIpMap.query() \
1017 c.user_ip_map = UserIpMap.query() \
1013 .filter(UserIpMap.user == c.user).all()
1018 .filter(UserIpMap.user == c.user).all()
1014
1019
1015 c.inherit_default_ips = c.user.inherit_default_permissions
1020 c.inherit_default_ips = c.user.inherit_default_permissions
1016 c.default_user_ip_map = UserIpMap.query() \
1021 c.default_user_ip_map = UserIpMap.query() \
1017 .filter(UserIpMap.user == User.get_default_user()).all()
1022 .filter(UserIpMap.user == User.get_default_user()).all()
1018
1023
1019 return self._get_template_context(c)
1024 return self._get_template_context(c)
1020
1025
1021 @LoginRequired()
1026 @LoginRequired()
1022 @HasPermissionAllDecorator('hg.admin')
1027 @HasPermissionAllDecorator('hg.admin')
1023 @CSRFRequired()
1028 @CSRFRequired()
1024 @view_config(
1029 @view_config(
1025 route_name='edit_user_ips_add', request_method='POST')
1030 route_name='edit_user_ips_add', request_method='POST')
1026 # NOTE(marcink): this view is allowed for default users, as we can
1031 # NOTE(marcink): this view is allowed for default users, as we can
1027 # edit their IP white list
1032 # edit their IP white list
1028 def ips_add(self):
1033 def ips_add(self):
1029 _ = self.request.translate
1034 _ = self.request.translate
1030 c = self.load_default_context()
1035 c = self.load_default_context()
1031
1036
1032 user_id = self.db_user_id
1037 user_id = self.db_user_id
1033 c.user = self.db_user
1038 c.user = self.db_user
1034
1039
1035 user_model = UserModel()
1040 user_model = UserModel()
1036 desc = self.request.POST.get('description')
1041 desc = self.request.POST.get('description')
1037 try:
1042 try:
1038 ip_list = user_model.parse_ip_range(
1043 ip_list = user_model.parse_ip_range(
1039 self.request.POST.get('new_ip'))
1044 self.request.POST.get('new_ip'))
1040 except Exception as e:
1045 except Exception as e:
1041 ip_list = []
1046 ip_list = []
1042 log.exception("Exception during ip saving")
1047 log.exception("Exception during ip saving")
1043 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1048 h.flash(_('An error occurred during ip saving:%s' % (e,)),
1044 category='error')
1049 category='error')
1045 added = []
1050 added = []
1046 user_data = c.user.get_api_data()
1051 user_data = c.user.get_api_data()
1047 for ip in ip_list:
1052 for ip in ip_list:
1048 try:
1053 try:
1049 form = UserExtraIpForm(self.request.translate)()
1054 form = UserExtraIpForm(self.request.translate)()
1050 data = form.to_python({'ip': ip})
1055 data = form.to_python({'ip': ip})
1051 ip = data['ip']
1056 ip = data['ip']
1052
1057
1053 user_model.add_extra_ip(c.user.user_id, ip, desc)
1058 user_model.add_extra_ip(c.user.user_id, ip, desc)
1054 audit_logger.store_web(
1059 audit_logger.store_web(
1055 'user.edit.ip.add',
1060 'user.edit.ip.add',
1056 action_data={'ip': ip, 'user': user_data},
1061 action_data={'ip': ip, 'user': user_data},
1057 user=self._rhodecode_user)
1062 user=self._rhodecode_user)
1058 Session().commit()
1063 Session().commit()
1059 added.append(ip)
1064 added.append(ip)
1060 except formencode.Invalid as error:
1065 except formencode.Invalid as error:
1061 msg = error.error_dict['ip']
1066 msg = error.error_dict['ip']
1062 h.flash(msg, category='error')
1067 h.flash(msg, category='error')
1063 except Exception:
1068 except Exception:
1064 log.exception("Exception during ip saving")
1069 log.exception("Exception during ip saving")
1065 h.flash(_('An error occurred during ip saving'),
1070 h.flash(_('An error occurred during ip saving'),
1066 category='error')
1071 category='error')
1067 if added:
1072 if added:
1068 h.flash(
1073 h.flash(
1069 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1074 _("Added ips %s to user whitelist") % (', '.join(ip_list), ),
1070 category='success')
1075 category='success')
1071 if 'default_user' in self.request.POST:
1076 if 'default_user' in self.request.POST:
1072 # case for editing global IP list we do it for 'DEFAULT' user
1077 # case for editing global IP list we do it for 'DEFAULT' user
1073 raise HTTPFound(h.route_path('admin_permissions_ips'))
1078 raise HTTPFound(h.route_path('admin_permissions_ips'))
1074 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1079 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1075
1080
1076 @LoginRequired()
1081 @LoginRequired()
1077 @HasPermissionAllDecorator('hg.admin')
1082 @HasPermissionAllDecorator('hg.admin')
1078 @CSRFRequired()
1083 @CSRFRequired()
1079 @view_config(
1084 @view_config(
1080 route_name='edit_user_ips_delete', request_method='POST')
1085 route_name='edit_user_ips_delete', request_method='POST')
1081 # NOTE(marcink): this view is allowed for default users, as we can
1086 # NOTE(marcink): this view is allowed for default users, as we can
1082 # edit their IP white list
1087 # edit their IP white list
1083 def ips_delete(self):
1088 def ips_delete(self):
1084 _ = self.request.translate
1089 _ = self.request.translate
1085 c = self.load_default_context()
1090 c = self.load_default_context()
1086
1091
1087 user_id = self.db_user_id
1092 user_id = self.db_user_id
1088 c.user = self.db_user
1093 c.user = self.db_user
1089
1094
1090 ip_id = self.request.POST.get('del_ip_id')
1095 ip_id = self.request.POST.get('del_ip_id')
1091 user_model = UserModel()
1096 user_model = UserModel()
1092 user_data = c.user.get_api_data()
1097 user_data = c.user.get_api_data()
1093 ip = UserIpMap.query().get(ip_id).ip_addr
1098 ip = UserIpMap.query().get(ip_id).ip_addr
1094 user_model.delete_extra_ip(c.user.user_id, ip_id)
1099 user_model.delete_extra_ip(c.user.user_id, ip_id)
1095 audit_logger.store_web(
1100 audit_logger.store_web(
1096 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1101 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data},
1097 user=self._rhodecode_user)
1102 user=self._rhodecode_user)
1098 Session().commit()
1103 Session().commit()
1099 h.flash(_("Removed ip address from user whitelist"), category='success')
1104 h.flash(_("Removed ip address from user whitelist"), category='success')
1100
1105
1101 if 'default_user' in self.request.POST:
1106 if 'default_user' in self.request.POST:
1102 # case for editing global IP list we do it for 'DEFAULT' user
1107 # case for editing global IP list we do it for 'DEFAULT' user
1103 raise HTTPFound(h.route_path('admin_permissions_ips'))
1108 raise HTTPFound(h.route_path('admin_permissions_ips'))
1104 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1109 raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id))
1105
1110
1106 @LoginRequired()
1111 @LoginRequired()
1107 @HasPermissionAllDecorator('hg.admin')
1112 @HasPermissionAllDecorator('hg.admin')
1108 @view_config(
1113 @view_config(
1109 route_name='edit_user_groups_management', request_method='GET',
1114 route_name='edit_user_groups_management', request_method='GET',
1110 renderer='rhodecode:templates/admin/users/user_edit.mako')
1115 renderer='rhodecode:templates/admin/users/user_edit.mako')
1111 def groups_management(self):
1116 def groups_management(self):
1112 c = self.load_default_context()
1117 c = self.load_default_context()
1113 c.user = self.db_user
1118 c.user = self.db_user
1114 c.data = c.user.group_member
1119 c.data = c.user.group_member
1115
1120
1116 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1121 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
1117 for group in c.user.group_member]
1122 for group in c.user.group_member]
1118 c.groups = json.dumps(groups)
1123 c.groups = json.dumps(groups)
1119 c.active = 'groups'
1124 c.active = 'groups'
1120
1125
1121 return self._get_template_context(c)
1126 return self._get_template_context(c)
1122
1127
1123 @LoginRequired()
1128 @LoginRequired()
1124 @HasPermissionAllDecorator('hg.admin')
1129 @HasPermissionAllDecorator('hg.admin')
1125 @CSRFRequired()
1130 @CSRFRequired()
1126 @view_config(
1131 @view_config(
1127 route_name='edit_user_groups_management_updates', request_method='POST')
1132 route_name='edit_user_groups_management_updates', request_method='POST')
1128 def groups_management_updates(self):
1133 def groups_management_updates(self):
1129 _ = self.request.translate
1134 _ = self.request.translate
1130 c = self.load_default_context()
1135 c = self.load_default_context()
1131
1136
1132 user_id = self.db_user_id
1137 user_id = self.db_user_id
1133 c.user = self.db_user
1138 c.user = self.db_user
1134
1139
1135 user_groups = set(self.request.POST.getall('users_group_id'))
1140 user_groups = set(self.request.POST.getall('users_group_id'))
1136 user_groups_objects = []
1141 user_groups_objects = []
1137
1142
1138 for ugid in user_groups:
1143 for ugid in user_groups:
1139 user_groups_objects.append(
1144 user_groups_objects.append(
1140 UserGroupModel().get_group(safe_int(ugid)))
1145 UserGroupModel().get_group(safe_int(ugid)))
1141 user_group_model = UserGroupModel()
1146 user_group_model = UserGroupModel()
1142 added_to_groups, removed_from_groups = \
1147 added_to_groups, removed_from_groups = \
1143 user_group_model.change_groups(c.user, user_groups_objects)
1148 user_group_model.change_groups(c.user, user_groups_objects)
1144
1149
1145 user_data = c.user.get_api_data()
1150 user_data = c.user.get_api_data()
1146 for user_group_id in added_to_groups:
1151 for user_group_id in added_to_groups:
1147 user_group = UserGroup.get(user_group_id)
1152 user_group = UserGroup.get(user_group_id)
1148 old_values = user_group.get_api_data()
1153 old_values = user_group.get_api_data()
1149 audit_logger.store_web(
1154 audit_logger.store_web(
1150 'user_group.edit.member.add',
1155 'user_group.edit.member.add',
1151 action_data={'user': user_data, 'old_data': old_values},
1156 action_data={'user': user_data, 'old_data': old_values},
1152 user=self._rhodecode_user)
1157 user=self._rhodecode_user)
1153
1158
1154 for user_group_id in removed_from_groups:
1159 for user_group_id in removed_from_groups:
1155 user_group = UserGroup.get(user_group_id)
1160 user_group = UserGroup.get(user_group_id)
1156 old_values = user_group.get_api_data()
1161 old_values = user_group.get_api_data()
1157 audit_logger.store_web(
1162 audit_logger.store_web(
1158 'user_group.edit.member.delete',
1163 'user_group.edit.member.delete',
1159 action_data={'user': user_data, 'old_data': old_values},
1164 action_data={'user': user_data, 'old_data': old_values},
1160 user=self._rhodecode_user)
1165 user=self._rhodecode_user)
1161
1166
1162 Session().commit()
1167 Session().commit()
1163 c.active = 'user_groups_management'
1168 c.active = 'user_groups_management'
1164 h.flash(_("Groups successfully changed"), category='success')
1169 h.flash(_("Groups successfully changed"), category='success')
1165
1170
1166 return HTTPFound(h.route_path(
1171 return HTTPFound(h.route_path(
1167 'edit_user_groups_management', user_id=user_id))
1172 'edit_user_groups_management', user_id=user_id))
1168
1173
1169 @LoginRequired()
1174 @LoginRequired()
1170 @HasPermissionAllDecorator('hg.admin')
1175 @HasPermissionAllDecorator('hg.admin')
1171 @view_config(
1176 @view_config(
1172 route_name='edit_user_audit_logs', request_method='GET',
1177 route_name='edit_user_audit_logs', request_method='GET',
1173 renderer='rhodecode:templates/admin/users/user_edit.mako')
1178 renderer='rhodecode:templates/admin/users/user_edit.mako')
1174 def user_audit_logs(self):
1179 def user_audit_logs(self):
1175 _ = self.request.translate
1180 _ = self.request.translate
1176 c = self.load_default_context()
1181 c = self.load_default_context()
1177 c.user = self.db_user
1182 c.user = self.db_user
1178
1183
1179 c.active = 'audit'
1184 c.active = 'audit'
1180
1185
1181 p = safe_int(self.request.GET.get('page', 1), 1)
1186 p = safe_int(self.request.GET.get('page', 1), 1)
1182
1187
1183 filter_term = self.request.GET.get('filter')
1188 filter_term = self.request.GET.get('filter')
1184 user_log = UserModel().get_user_log(c.user, filter_term)
1189 user_log = UserModel().get_user_log(c.user, filter_term)
1185
1190
1186 def url_generator(**kw):
1191 def url_generator(**kw):
1187 if filter_term:
1192 if filter_term:
1188 kw['filter'] = filter_term
1193 kw['filter'] = filter_term
1189 return self.request.current_route_path(_query=kw)
1194 return self.request.current_route_path(_query=kw)
1190
1195
1191 c.audit_logs = h.Page(
1196 c.audit_logs = h.Page(
1192 user_log, page=p, items_per_page=10, url=url_generator)
1197 user_log, page=p, items_per_page=10, url=url_generator)
1193 c.filter_term = filter_term
1198 c.filter_term = filter_term
1194 return self._get_template_context(c)
1199 return self._get_template_context(c)
1195
1200
1196 @LoginRequired()
1201 @LoginRequired()
1197 @HasPermissionAllDecorator('hg.admin')
1202 @HasPermissionAllDecorator('hg.admin')
1198 @view_config(
1203 @view_config(
1199 route_name='edit_user_perms_summary', request_method='GET',
1204 route_name='edit_user_perms_summary', request_method='GET',
1200 renderer='rhodecode:templates/admin/users/user_edit.mako')
1205 renderer='rhodecode:templates/admin/users/user_edit.mako')
1201 def user_perms_summary(self):
1206 def user_perms_summary(self):
1202 _ = self.request.translate
1207 _ = self.request.translate
1203 c = self.load_default_context()
1208 c = self.load_default_context()
1204 c.user = self.db_user
1209 c.user = self.db_user
1205
1210
1206 c.active = 'perms_summary'
1211 c.active = 'perms_summary'
1207 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1212 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1208
1213
1209 return self._get_template_context(c)
1214 return self._get_template_context(c)
1210
1215
1211 @LoginRequired()
1216 @LoginRequired()
1212 @HasPermissionAllDecorator('hg.admin')
1217 @HasPermissionAllDecorator('hg.admin')
1213 @view_config(
1218 @view_config(
1214 route_name='edit_user_perms_summary_json', request_method='GET',
1219 route_name='edit_user_perms_summary_json', request_method='GET',
1215 renderer='json_ext')
1220 renderer='json_ext')
1216 def user_perms_summary_json(self):
1221 def user_perms_summary_json(self):
1217 self.load_default_context()
1222 self.load_default_context()
1218 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1223 perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr)
1219
1224
1220 return perm_user.permissions
1225 return perm_user.permissions
1221
1226
1222 @LoginRequired()
1227 @LoginRequired()
1223 @HasPermissionAllDecorator('hg.admin')
1228 @HasPermissionAllDecorator('hg.admin')
1224 @view_config(
1229 @view_config(
1225 route_name='edit_user_caches', request_method='GET',
1230 route_name='edit_user_caches', request_method='GET',
1226 renderer='rhodecode:templates/admin/users/user_edit.mako')
1231 renderer='rhodecode:templates/admin/users/user_edit.mako')
1227 def user_caches(self):
1232 def user_caches(self):
1228 _ = self.request.translate
1233 _ = self.request.translate
1229 c = self.load_default_context()
1234 c = self.load_default_context()
1230 c.user = self.db_user
1235 c.user = self.db_user
1231
1236
1232 c.active = 'caches'
1237 c.active = 'caches'
1233 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1238 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1234
1239
1235 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1240 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1236 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1241 c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1237 c.backend = c.region.backend
1242 c.backend = c.region.backend
1238 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1243 c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid))
1239
1244
1240 return self._get_template_context(c)
1245 return self._get_template_context(c)
1241
1246
1242 @LoginRequired()
1247 @LoginRequired()
1243 @HasPermissionAllDecorator('hg.admin')
1248 @HasPermissionAllDecorator('hg.admin')
1244 @CSRFRequired()
1249 @CSRFRequired()
1245 @view_config(
1250 @view_config(
1246 route_name='edit_user_caches_update', request_method='POST')
1251 route_name='edit_user_caches_update', request_method='POST')
1247 def user_caches_update(self):
1252 def user_caches_update(self):
1248 _ = self.request.translate
1253 _ = self.request.translate
1249 c = self.load_default_context()
1254 c = self.load_default_context()
1250 c.user = self.db_user
1255 c.user = self.db_user
1251
1256
1252 c.active = 'caches'
1257 c.active = 'caches'
1253 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1258 c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr)
1254
1259
1255 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1260 cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id)
1256 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1261 del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid)
1257
1262
1258 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1263 h.flash(_("Deleted {} cache keys").format(del_keys), category='success')
1259
1264
1260 return HTTPFound(h.route_path(
1265 return HTTPFound(h.route_path(
1261 'edit_user_caches', user_id=c.user.user_id))
1266 'edit_user_caches', user_id=c.user.user_id))
@@ -1,189 +1,193 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-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 deform
22 import deform
23
23
24 from pyramid.view import view_config
24 from pyramid.view import view_config
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
26
26
27 from rhodecode import events
27 from rhodecode import events
28 from rhodecode.apps._base import RepoGroupAppView
28 from rhodecode.apps._base import RepoGroupAppView
29 from rhodecode.forms import RcForm
29 from rhodecode.forms import RcForm
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib import audit_logger
31 from rhodecode.lib import audit_logger
32 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
33 LoginRequired, HasPermissionAll,
33 LoginRequired, HasPermissionAll,
34 HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
34 HasRepoGroupPermissionAny, HasRepoGroupPermissionAnyDecorator, CSRFRequired)
35 from rhodecode.model.db import Session, RepoGroup, User
35 from rhodecode.model.db import Session, RepoGroup, User
36 from rhodecode.model.scm import RepoGroupList
36 from rhodecode.model.scm import RepoGroupList
37 from rhodecode.model.repo_group import RepoGroupModel
37 from rhodecode.model.repo_group import RepoGroupModel
38 from rhodecode.model.validation_schema.schemas import repo_group_schema
38 from rhodecode.model.validation_schema.schemas import repo_group_schema
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class RepoGroupSettingsView(RepoGroupAppView):
43 class RepoGroupSettingsView(RepoGroupAppView):
44 def load_default_context(self):
44 def load_default_context(self):
45 c = self._get_local_tmpl_context()
45 c = self._get_local_tmpl_context()
46 c.repo_group = self.db_repo_group
46 c.repo_group = self.db_repo_group
47 no_parrent = not c.repo_group.parent_group
47 no_parrent = not c.repo_group.parent_group
48 can_create_in_root = self._can_create_repo_group()
48 can_create_in_root = self._can_create_repo_group()
49
49
50 show_root_location = False
50 show_root_location = False
51 if no_parrent or can_create_in_root:
51 if no_parrent or can_create_in_root:
52 # we're global admin, we're ok and we can create TOP level groups
52 # we're global admin, we're ok and we can create TOP level groups
53 # or in case this group is already at top-level we also allow
53 # or in case this group is already at top-level we also allow
54 # creation in root
54 # creation in root
55 show_root_location = True
55 show_root_location = True
56
56
57 acl_groups = RepoGroupList(
57 acl_groups = RepoGroupList(
58 RepoGroup.query().all(),
58 RepoGroup.query().all(),
59 perm_set=['group.admin'])
59 perm_set=['group.admin'])
60 c.repo_groups = RepoGroup.groups_choices(
60 c.repo_groups = RepoGroup.groups_choices(
61 groups=acl_groups,
61 groups=acl_groups,
62 show_empty_group=show_root_location)
62 show_empty_group=show_root_location)
63 # filter out current repo group
63 # filter out current repo group
64 exclude_group_ids = [c.repo_group.group_id]
64 exclude_group_ids = [c.repo_group.group_id]
65 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
65 c.repo_groups = filter(lambda x: x[0] not in exclude_group_ids,
66 c.repo_groups)
66 c.repo_groups)
67 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
67 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
68
68
69 parent_group = c.repo_group.parent_group
69 parent_group = c.repo_group.parent_group
70
70
71 add_parent_group = (parent_group and (
71 add_parent_group = (parent_group and (
72 parent_group.group_id not in c.repo_groups_choices))
72 parent_group.group_id not in c.repo_groups_choices))
73 if add_parent_group:
73 if add_parent_group:
74 c.repo_groups_choices.append(parent_group.group_id)
74 c.repo_groups_choices.append(parent_group.group_id)
75 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
75 c.repo_groups.append(RepoGroup._generate_choice(parent_group))
76 return c
76 return c
77
77
78 def _can_create_repo_group(self, parent_group_id=None):
78 def _can_create_repo_group(self, parent_group_id=None):
79 is_admin = HasPermissionAll('hg.admin')('group create controller')
79 is_admin = HasPermissionAll('hg.admin')('group create controller')
80 create_repo_group = HasPermissionAll(
80 create_repo_group = HasPermissionAll(
81 'hg.repogroup.create.true')('group create controller')
81 'hg.repogroup.create.true')('group create controller')
82 if is_admin or (create_repo_group and not parent_group_id):
82 if is_admin or (create_repo_group and not parent_group_id):
83 # we're global admin, or we have global repo group create
83 # we're global admin, or we have global repo group create
84 # permission
84 # permission
85 # we're ok and we can create TOP level groups
85 # we're ok and we can create TOP level groups
86 return True
86 return True
87 elif parent_group_id:
87 elif parent_group_id:
88 # we check the permission if we can write to parent group
88 # we check the permission if we can write to parent group
89 group = RepoGroup.get(parent_group_id)
89 group = RepoGroup.get(parent_group_id)
90 group_name = group.group_name if group else None
90 group_name = group.group_name if group else None
91 if HasRepoGroupPermissionAny('group.admin')(
91 if HasRepoGroupPermissionAny('group.admin')(
92 group_name, 'check if user is an admin of group'):
92 group_name, 'check if user is an admin of group'):
93 # we're an admin of passed in group, we're ok.
93 # we're an admin of passed in group, we're ok.
94 return True
94 return True
95 else:
95 else:
96 return False
96 return False
97 return False
97 return False
98
98
99 def _get_schema(self, c, old_values=None):
99 def _get_schema(self, c, old_values=None):
100 return repo_group_schema.RepoGroupSettingsSchema().bind(
100 return repo_group_schema.RepoGroupSettingsSchema().bind(
101 repo_group_repo_group_options=c.repo_groups_choices,
101 repo_group_repo_group_options=c.repo_groups_choices,
102 repo_group_repo_group_items=c.repo_groups,
102 repo_group_repo_group_items=c.repo_groups,
103
103
104 # user caller
104 # user caller
105 user=self._rhodecode_user,
105 user=self._rhodecode_user,
106 old_values=old_values
106 old_values=old_values
107 )
107 )
108
108
109 @LoginRequired()
109 @LoginRequired()
110 @HasRepoGroupPermissionAnyDecorator('group.admin')
110 @HasRepoGroupPermissionAnyDecorator('group.admin')
111 @view_config(
111 @view_config(
112 route_name='edit_repo_group', request_method='GET',
112 route_name='edit_repo_group', request_method='GET',
113 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
113 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
114 def edit_settings(self):
114 def edit_settings(self):
115 c = self.load_default_context()
115 c = self.load_default_context()
116 c.active = 'settings'
116 c.active = 'settings'
117
117
118 defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name)
118 defaults = RepoGroupModel()._get_defaults(self.db_repo_group_name)
119 defaults['repo_group_owner'] = defaults['user']
119 defaults['repo_group_owner'] = defaults['user']
120
120
121 schema = self._get_schema(c)
121 schema = self._get_schema(c)
122 c.form = RcForm(schema, appstruct=defaults)
122 c.form = RcForm(schema, appstruct=defaults)
123 return self._get_template_context(c)
123 return self._get_template_context(c)
124
124
125 @LoginRequired()
125 @LoginRequired()
126 @HasRepoGroupPermissionAnyDecorator('group.admin')
126 @HasRepoGroupPermissionAnyDecorator('group.admin')
127 @CSRFRequired()
127 @CSRFRequired()
128 @view_config(
128 @view_config(
129 route_name='edit_repo_group', request_method='POST',
129 route_name='edit_repo_group', request_method='POST',
130 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
130 renderer='rhodecode:templates/admin/repo_groups/repo_group_edit.mako')
131 def edit_settings_update(self):
131 def edit_settings_update(self):
132 _ = self.request.translate
132 _ = self.request.translate
133 c = self.load_default_context()
133 c = self.load_default_context()
134 c.active = 'settings'
134 c.active = 'settings'
135
135
136 old_repo_group_name = self.db_repo_group_name
136 old_repo_group_name = self.db_repo_group_name
137 new_repo_group_name = old_repo_group_name
137 new_repo_group_name = old_repo_group_name
138
138
139 old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name)
139 old_values = RepoGroupModel()._get_defaults(self.db_repo_group_name)
140 schema = self._get_schema(c, old_values=old_values)
140 schema = self._get_schema(c, old_values=old_values)
141
141
142 c.form = RcForm(schema)
142 c.form = RcForm(schema)
143 pstruct = self.request.POST.items()
143 pstruct = self.request.POST.items()
144
144
145 try:
145 try:
146 schema_data = c.form.validate(pstruct)
146 schema_data = c.form.validate(pstruct)
147 except deform.ValidationFailure as err_form:
147 except deform.ValidationFailure as err_form:
148 return self._get_template_context(c)
148 return self._get_template_context(c)
149
149
150 # data is now VALID, proceed with updates
150 # data is now VALID, proceed with updates
151 # save validated data back into the updates dict
151 # save validated data back into the updates dict
152 validated_updates = dict(
152 validated_updates = dict(
153 group_name=schema_data['repo_group']['repo_group_name_without_group'],
153 group_name=schema_data['repo_group']['repo_group_name_without_group'],
154 group_parent_id=schema_data['repo_group']['repo_group_id'],
154 group_parent_id=schema_data['repo_group']['repo_group_id'],
155 user=schema_data['repo_group_owner'],
155 user=schema_data['repo_group_owner'],
156 group_description=schema_data['repo_group_description'],
156 group_description=schema_data['repo_group_description'],
157 enable_locking=schema_data['repo_group_enable_locking'],
157 enable_locking=schema_data['repo_group_enable_locking'],
158 )
158 )
159
159
160 try:
160 try:
161 RepoGroupModel().update(self.db_repo_group, validated_updates)
161 RepoGroupModel().update(self.db_repo_group, validated_updates)
162
162
163 audit_logger.store_web(
163 audit_logger.store_web(
164 'repo_group.edit', action_data={'old_data': old_values},
164 'repo_group.edit', action_data={'old_data': old_values},
165 user=c.rhodecode_user)
165 user=c.rhodecode_user)
166
166
167 Session().commit()
167 Session().commit()
168
168
169 # use the new full name for redirect once we know we updated
169 # use the new full name for redirect once we know we updated
170 # the name on filesystem and in DB
170 # the name on filesystem and in DB
171 new_repo_group_name = schema_data['repo_group']['repo_group_name_with_group']
171 new_repo_group_name = schema_data['repo_group']['repo_group_name_with_group']
172
172
173 h.flash(_('Repository Group `{}` updated successfully').format(
173 h.flash(_('Repository Group `{}` updated successfully').format(
174 old_repo_group_name), category='success')
174 old_repo_group_name), category='success')
175
175
176 except Exception:
176 except Exception:
177 log.exception("Exception during update or repository group")
177 log.exception("Exception during update or repository group")
178 h.flash(_('Error occurred during update of repository group %s')
178 h.flash(_('Error occurred during update of repository group %s')
179 % old_repo_group_name, category='error')
179 % old_repo_group_name, category='error')
180
180
181 name_changed = old_repo_group_name != new_repo_group_name
181 name_changed = old_repo_group_name != new_repo_group_name
182 if name_changed:
182 if name_changed:
183 current_perms = self.db_repo_group.permissions(expand_from_user_groups=True)
184 affected_user_ids = [perm['user_id'] for perm in current_perms]
185
186 # NOTE(marcink): also add owner maybe it has changed
183 owner = User.get_by_username(schema_data['repo_group_owner'])
187 owner = User.get_by_username(schema_data['repo_group_owner'])
184 owner_id = owner.user_id if owner else self._rhodecode_user.user_id
188 owner_id = owner.user_id if owner else self._rhodecode_user.user_id
185 events.trigger(events.UserPermissionsChange([
189 affected_user_ids.extend([self._rhodecode_user.user_id, owner_id])
186 self._rhodecode_user.user_id, owner_id]))
190 events.trigger(events.UserPermissionsChange(affected_user_ids))
187
191
188 raise HTTPFound(
192 raise HTTPFound(
189 h.route_path('edit_repo_group', repo_group_name=new_repo_group_name))
193 h.route_path('edit_repo_group', repo_group_name=new_repo_group_name))
@@ -1,263 +1,270 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-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 RepoAppView, DataGridAppView
32 from rhodecode.apps._base import RepoAppView, DataGridAppView
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
34 LoginRequired, HasRepoPermissionAnyDecorator, NotAnonymous,
35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
35 HasRepoPermissionAny, HasPermissionAnyDecorator, CSRFRequired)
36 import rhodecode.lib.helpers as h
36 import rhodecode.lib.helpers as h
37 from rhodecode.lib.celerylib.utils import get_task_id
37 from rhodecode.lib.celerylib.utils import get_task_id
38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
38 from rhodecode.model.db import coalesce, or_, Repository, RepoGroup
39 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.repo import RepoModel
40 from rhodecode.model.forms import RepoForkForm
40 from rhodecode.model.forms import RepoForkForm
41 from rhodecode.model.scm import ScmModel, RepoGroupList
41 from rhodecode.model.scm import ScmModel, RepoGroupList
42 from rhodecode.lib.utils2 import safe_int, safe_unicode
42 from rhodecode.lib.utils2 import safe_int, safe_unicode
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class RepoForksView(RepoAppView, DataGridAppView):
47 class RepoForksView(RepoAppView, DataGridAppView):
48
48
49 def load_default_context(self):
49 def load_default_context(self):
50 c = self._get_local_tmpl_context(include_app_defaults=True)
50 c = self._get_local_tmpl_context(include_app_defaults=True)
51 c.rhodecode_repo = self.rhodecode_vcs_repo
51 c.rhodecode_repo = self.rhodecode_vcs_repo
52
52
53 acl_groups = RepoGroupList(
53 acl_groups = RepoGroupList(
54 RepoGroup.query().all(),
54 RepoGroup.query().all(),
55 perm_set=['group.write', 'group.admin'])
55 perm_set=['group.write', 'group.admin'])
56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
56 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
57 c.repo_groups_choices = map(lambda k: safe_unicode(k[0]), c.repo_groups)
58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
58 choices, c.landing_revs = ScmModel().get_repo_landing_revs(
59 self.request.translate)
59 self.request.translate)
60 c.landing_revs_choices = choices
60 c.landing_revs_choices = choices
61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
61 c.personal_repo_group = c.rhodecode_user.personal_repo_group
62
62
63 return c
63 return c
64
64
65 @LoginRequired()
65 @LoginRequired()
66 @HasRepoPermissionAnyDecorator(
66 @HasRepoPermissionAnyDecorator(
67 'repository.read', 'repository.write', 'repository.admin')
67 'repository.read', 'repository.write', 'repository.admin')
68 @view_config(
68 @view_config(
69 route_name='repo_forks_show_all', request_method='GET',
69 route_name='repo_forks_show_all', request_method='GET',
70 renderer='rhodecode:templates/forks/forks.mako')
70 renderer='rhodecode:templates/forks/forks.mako')
71 def repo_forks_show_all(self):
71 def repo_forks_show_all(self):
72 c = self.load_default_context()
72 c = self.load_default_context()
73 return self._get_template_context(c)
73 return self._get_template_context(c)
74
74
75 @LoginRequired()
75 @LoginRequired()
76 @HasRepoPermissionAnyDecorator(
76 @HasRepoPermissionAnyDecorator(
77 'repository.read', 'repository.write', 'repository.admin')
77 'repository.read', 'repository.write', 'repository.admin')
78 @view_config(
78 @view_config(
79 route_name='repo_forks_data', request_method='GET',
79 route_name='repo_forks_data', request_method='GET',
80 renderer='json_ext', xhr=True)
80 renderer='json_ext', xhr=True)
81 def repo_forks_data(self):
81 def repo_forks_data(self):
82 _ = self.request.translate
82 _ = self.request.translate
83 self.load_default_context()
83 self.load_default_context()
84 column_map = {
84 column_map = {
85 'fork_name': 'repo_name',
85 'fork_name': 'repo_name',
86 'fork_date': 'created_on',
86 'fork_date': 'created_on',
87 'last_activity': 'updated_on'
87 'last_activity': 'updated_on'
88 }
88 }
89 draw, start, limit = self._extract_chunk(self.request)
89 draw, start, limit = self._extract_chunk(self.request)
90 search_q, order_by, order_dir = self._extract_ordering(
90 search_q, order_by, order_dir = self._extract_ordering(
91 self.request, column_map=column_map)
91 self.request, column_map=column_map)
92
92
93 acl_check = HasRepoPermissionAny(
93 acl_check = HasRepoPermissionAny(
94 'repository.read', 'repository.write', 'repository.admin')
94 'repository.read', 'repository.write', 'repository.admin')
95 repo_id = self.db_repo.repo_id
95 repo_id = self.db_repo.repo_id
96 allowed_ids = [-1]
96 allowed_ids = [-1]
97 for f in Repository.query().filter(Repository.fork_id == repo_id):
97 for f in Repository.query().filter(Repository.fork_id == repo_id):
98 if acl_check(f.repo_name, 'get forks check'):
98 if acl_check(f.repo_name, 'get forks check'):
99 allowed_ids.append(f.repo_id)
99 allowed_ids.append(f.repo_id)
100
100
101 forks_data_total_count = Repository.query()\
101 forks_data_total_count = Repository.query()\
102 .filter(Repository.fork_id == repo_id)\
102 .filter(Repository.fork_id == repo_id)\
103 .filter(Repository.repo_id.in_(allowed_ids))\
103 .filter(Repository.repo_id.in_(allowed_ids))\
104 .count()
104 .count()
105
105
106 # json generate
106 # json generate
107 base_q = Repository.query()\
107 base_q = Repository.query()\
108 .filter(Repository.fork_id == repo_id)\
108 .filter(Repository.fork_id == repo_id)\
109 .filter(Repository.repo_id.in_(allowed_ids))\
109 .filter(Repository.repo_id.in_(allowed_ids))\
110
110
111 if search_q:
111 if search_q:
112 like_expression = u'%{}%'.format(safe_unicode(search_q))
112 like_expression = u'%{}%'.format(safe_unicode(search_q))
113 base_q = base_q.filter(or_(
113 base_q = base_q.filter(or_(
114 Repository.repo_name.ilike(like_expression),
114 Repository.repo_name.ilike(like_expression),
115 Repository.description.ilike(like_expression),
115 Repository.description.ilike(like_expression),
116 ))
116 ))
117
117
118 forks_data_total_filtered_count = base_q.count()
118 forks_data_total_filtered_count = base_q.count()
119
119
120 sort_col = getattr(Repository, order_by, None)
120 sort_col = getattr(Repository, order_by, None)
121 if sort_col:
121 if sort_col:
122 if order_dir == 'asc':
122 if order_dir == 'asc':
123 # handle null values properly to order by NULL last
123 # handle null values properly to order by NULL last
124 if order_by in ['last_activity']:
124 if order_by in ['last_activity']:
125 sort_col = coalesce(sort_col, datetime.date.max)
125 sort_col = coalesce(sort_col, datetime.date.max)
126 sort_col = sort_col.asc()
126 sort_col = sort_col.asc()
127 else:
127 else:
128 # handle null values properly to order by NULL last
128 # handle null values properly to order by NULL last
129 if order_by in ['last_activity']:
129 if order_by in ['last_activity']:
130 sort_col = coalesce(sort_col, datetime.date.min)
130 sort_col = coalesce(sort_col, datetime.date.min)
131 sort_col = sort_col.desc()
131 sort_col = sort_col.desc()
132
132
133 base_q = base_q.order_by(sort_col)
133 base_q = base_q.order_by(sort_col)
134 base_q = base_q.offset(start).limit(limit)
134 base_q = base_q.offset(start).limit(limit)
135
135
136 fork_list = base_q.all()
136 fork_list = base_q.all()
137
137
138 def fork_actions(fork):
138 def fork_actions(fork):
139 url_link = h.route_path(
139 url_link = h.route_path(
140 'repo_compare',
140 'repo_compare',
141 repo_name=fork.repo_name,
141 repo_name=fork.repo_name,
142 source_ref_type=self.db_repo.landing_rev[0],
142 source_ref_type=self.db_repo.landing_rev[0],
143 source_ref=self.db_repo.landing_rev[1],
143 source_ref=self.db_repo.landing_rev[1],
144 target_ref_type=self.db_repo.landing_rev[0],
144 target_ref_type=self.db_repo.landing_rev[0],
145 target_ref=self.db_repo.landing_rev[1],
145 target_ref=self.db_repo.landing_rev[1],
146 _query=dict(merge=1, target_repo=f.repo_name))
146 _query=dict(merge=1, target_repo=f.repo_name))
147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
147 return h.link_to(_('Compare fork'), url_link, class_='btn-link')
148
148
149 def fork_name(fork):
149 def fork_name(fork):
150 return h.link_to(fork.repo_name,
150 return h.link_to(fork.repo_name,
151 h.route_path('repo_summary', repo_name=fork.repo_name))
151 h.route_path('repo_summary', repo_name=fork.repo_name))
152
152
153 forks_data = []
153 forks_data = []
154 for fork in fork_list:
154 for fork in fork_list:
155 forks_data.append({
155 forks_data.append({
156 "username": h.gravatar_with_user(self.request, fork.user.username),
156 "username": h.gravatar_with_user(self.request, fork.user.username),
157 "fork_name": fork_name(fork),
157 "fork_name": fork_name(fork),
158 "description": fork.description_safe,
158 "description": fork.description_safe,
159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
159 "fork_date": h.age_component(fork.created_on, time_is_local=True),
160 "last_activity": h.format_date(fork.updated_on),
160 "last_activity": h.format_date(fork.updated_on),
161 "action": fork_actions(fork),
161 "action": fork_actions(fork),
162 })
162 })
163
163
164 data = ({
164 data = ({
165 'draw': draw,
165 'draw': draw,
166 'data': forks_data,
166 'data': forks_data,
167 'recordsTotal': forks_data_total_count,
167 'recordsTotal': forks_data_total_count,
168 'recordsFiltered': forks_data_total_filtered_count,
168 'recordsFiltered': forks_data_total_filtered_count,
169 })
169 })
170
170
171 return data
171 return data
172
172
173 @LoginRequired()
173 @LoginRequired()
174 @NotAnonymous()
174 @NotAnonymous()
175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
175 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
176 @HasRepoPermissionAnyDecorator(
176 @HasRepoPermissionAnyDecorator(
177 'repository.read', 'repository.write', 'repository.admin')
177 'repository.read', 'repository.write', 'repository.admin')
178 @view_config(
178 @view_config(
179 route_name='repo_fork_new', request_method='GET',
179 route_name='repo_fork_new', request_method='GET',
180 renderer='rhodecode:templates/forks/forks.mako')
180 renderer='rhodecode:templates/forks/forks.mako')
181 def repo_fork_new(self):
181 def repo_fork_new(self):
182 c = self.load_default_context()
182 c = self.load_default_context()
183
183
184 defaults = RepoModel()._get_defaults(self.db_repo_name)
184 defaults = RepoModel()._get_defaults(self.db_repo_name)
185 # alter the description to indicate a fork
185 # alter the description to indicate a fork
186 defaults['description'] = (
186 defaults['description'] = (
187 'fork of repository: %s \n%s' % (
187 'fork of repository: %s \n%s' % (
188 defaults['repo_name'], defaults['description']))
188 defaults['repo_name'], defaults['description']))
189 # add suffix to fork
189 # add suffix to fork
190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
190 defaults['repo_name'] = '%s-fork' % defaults['repo_name']
191
191
192 data = render('rhodecode:templates/forks/fork.mako',
192 data = render('rhodecode:templates/forks/fork.mako',
193 self._get_template_context(c), self.request)
193 self._get_template_context(c), self.request)
194 html = formencode.htmlfill.render(
194 html = formencode.htmlfill.render(
195 data,
195 data,
196 defaults=defaults,
196 defaults=defaults,
197 encoding="UTF-8",
197 encoding="UTF-8",
198 force_defaults=False
198 force_defaults=False
199 )
199 )
200 return Response(html)
200 return Response(html)
201
201
202 @LoginRequired()
202 @LoginRequired()
203 @NotAnonymous()
203 @NotAnonymous()
204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
204 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
205 @HasRepoPermissionAnyDecorator(
205 @HasRepoPermissionAnyDecorator(
206 'repository.read', 'repository.write', 'repository.admin')
206 'repository.read', 'repository.write', 'repository.admin')
207 @CSRFRequired()
207 @CSRFRequired()
208 @view_config(
208 @view_config(
209 route_name='repo_fork_create', request_method='POST',
209 route_name='repo_fork_create', request_method='POST',
210 renderer='rhodecode:templates/forks/fork.mako')
210 renderer='rhodecode:templates/forks/fork.mako')
211 def repo_fork_create(self):
211 def repo_fork_create(self):
212 _ = self.request.translate
212 _ = self.request.translate
213 c = self.load_default_context()
213 c = self.load_default_context()
214
214
215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
215 _form = RepoForkForm(self.request.translate, old_data={'repo_type': self.db_repo.repo_type},
216 repo_groups=c.repo_groups_choices,
216 repo_groups=c.repo_groups_choices,
217 landing_revs=c.landing_revs_choices)()
217 landing_revs=c.landing_revs_choices)()
218 post_data = dict(self.request.POST)
218 post_data = dict(self.request.POST)
219
219
220 # forbid injecting other repo by forging a request
220 # forbid injecting other repo by forging a request
221 post_data['fork_parent_id'] = self.db_repo.repo_id
221 post_data['fork_parent_id'] = self.db_repo.repo_id
222
222
223 form_result = {}
223 form_result = {}
224 task_id = None
224 task_id = None
225 try:
225 try:
226 form_result = _form.to_python(post_data)
226 form_result = _form.to_python(post_data)
227 copy_permissions = form_result.get('copy_permissions')
227 # create fork is done sometimes async on celery, db transaction
228 # create fork is done sometimes async on celery, db transaction
228 # management is handled there.
229 # management is handled there.
229 task = RepoModel().create_fork(
230 task = RepoModel().create_fork(
230 form_result, c.rhodecode_user.user_id)
231 form_result, c.rhodecode_user.user_id)
231
232
232 task_id = get_task_id(task)
233 task_id = get_task_id(task)
233 except formencode.Invalid as errors:
234 except formencode.Invalid as errors:
234 c.rhodecode_db_repo = self.db_repo
235 c.rhodecode_db_repo = self.db_repo
235
236
236 data = render('rhodecode:templates/forks/fork.mako',
237 data = render('rhodecode:templates/forks/fork.mako',
237 self._get_template_context(c), self.request)
238 self._get_template_context(c), self.request)
238 html = formencode.htmlfill.render(
239 html = formencode.htmlfill.render(
239 data,
240 data,
240 defaults=errors.value,
241 defaults=errors.value,
241 errors=errors.error_dict or {},
242 errors=errors.error_dict or {},
242 prefix_error=False,
243 prefix_error=False,
243 encoding="UTF-8",
244 encoding="UTF-8",
244 force_defaults=False
245 force_defaults=False
245 )
246 )
246 return Response(html)
247 return Response(html)
247 except Exception:
248 except Exception:
248 log.exception(
249 log.exception(
249 u'Exception while trying to fork the repository %s',
250 u'Exception while trying to fork the repository %s', self.db_repo_name)
250 self.db_repo_name)
251 msg = _('An error occurred during repository forking %s') % (self.db_repo_name, )
251 msg = (
252 _('An error occurred during repository forking %s') % (
253 self.db_repo_name, ))
254 h.flash(msg, category='error')
252 h.flash(msg, category='error')
253 raise HTTPFound(h.route_path('home'))
255
254
256 repo_name = form_result.get('repo_name_full', self.db_repo_name)
255 repo_name = form_result.get('repo_name_full', self.db_repo_name)
257
256
258 events.trigger(events.UserPermissionsChange([self._rhodecode_user.user_id]))
257 affected_user_ids = [self._rhodecode_user.user_id]
258 if copy_permissions:
259 repository = Repository.get_by_repo_name(repo_name)
260 # also include those newly created by copy
261 user_group_perms = repository.permissions(expand_from_user_groups=True)
262 copy_perms = [perm['user_id'] for perm in user_group_perms]
263 # also include those newly created by copy
264 affected_user_ids.extend(copy_perms)
265
266 events.trigger(events.UserPermissionsChange(affected_user_ids))
259
267
260 raise HTTPFound(
268 raise HTTPFound(
261 h.route_path('repo_creating',
269 h.route_path('repo_creating', repo_name=repo_name,
262 repo_name=repo_name,
263 _query=dict(task_id=task_id)))
270 _query=dict(task_id=task_id)))
@@ -1,262 +1,266 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2011-2019 RhodeCode GmbH
3 # Copyright (C) 2011-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 import deform
23 import deform
24 from pyramid.httpexceptions import HTTPFound
24 from pyramid.httpexceptions import HTTPFound
25 from pyramid.view import view_config
25 from pyramid.view import view_config
26
26
27 from rhodecode import events
27 from rhodecode import events
28 from rhodecode.apps._base import RepoAppView
28 from rhodecode.apps._base import RepoAppView
29 from rhodecode.forms import RcForm
29 from rhodecode.forms import RcForm
30 from rhodecode.lib import helpers as h
30 from rhodecode.lib import helpers as h
31 from rhodecode.lib import audit_logger
31 from rhodecode.lib import audit_logger
32 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
33 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
33 LoginRequired, HasRepoPermissionAnyDecorator, CSRFRequired)
34 from rhodecode.model.db import RepositoryField, RepoGroup, Repository, User
34 from rhodecode.model.db import RepositoryField, RepoGroup, Repository, User
35 from rhodecode.model.meta import Session
35 from rhodecode.model.meta import Session
36 from rhodecode.model.repo import RepoModel
36 from rhodecode.model.repo import RepoModel
37 from rhodecode.model.scm import RepoGroupList, ScmModel
37 from rhodecode.model.scm import RepoGroupList, ScmModel
38 from rhodecode.model.validation_schema.schemas import repo_schema
38 from rhodecode.model.validation_schema.schemas import repo_schema
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class RepoSettingsView(RepoAppView):
43 class RepoSettingsView(RepoAppView):
44
44
45 def load_default_context(self):
45 def load_default_context(self):
46 c = self._get_local_tmpl_context()
46 c = self._get_local_tmpl_context()
47
47
48 acl_groups = RepoGroupList(
48 acl_groups = RepoGroupList(
49 RepoGroup.query().all(),
49 RepoGroup.query().all(),
50 perm_set=['group.write', 'group.admin'])
50 perm_set=['group.write', 'group.admin'])
51 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
51 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
52 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
52 c.repo_groups_choices = map(lambda k: k[0], c.repo_groups)
53
53
54 # in case someone no longer have a group.write access to a repository
54 # in case someone no longer have a group.write access to a repository
55 # pre fill the list with this entry, we don't care if this is the same
55 # pre fill the list with this entry, we don't care if this is the same
56 # but it will allow saving repo data properly.
56 # but it will allow saving repo data properly.
57 repo_group = self.db_repo.group
57 repo_group = self.db_repo.group
58 if repo_group and repo_group.group_id not in c.repo_groups_choices:
58 if repo_group and repo_group.group_id not in c.repo_groups_choices:
59 c.repo_groups_choices.append(repo_group.group_id)
59 c.repo_groups_choices.append(repo_group.group_id)
60 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
60 c.repo_groups.append(RepoGroup._generate_choice(repo_group))
61
61
62 if c.repository_requirements_missing or self.rhodecode_vcs_repo is None:
62 if c.repository_requirements_missing or self.rhodecode_vcs_repo is None:
63 # we might be in missing requirement state, so we load things
63 # we might be in missing requirement state, so we load things
64 # without touching scm_instance()
64 # without touching scm_instance()
65 c.landing_revs_choices, c.landing_revs = \
65 c.landing_revs_choices, c.landing_revs = \
66 ScmModel().get_repo_landing_revs(self.request.translate)
66 ScmModel().get_repo_landing_revs(self.request.translate)
67 else:
67 else:
68 c.landing_revs_choices, c.landing_revs = \
68 c.landing_revs_choices, c.landing_revs = \
69 ScmModel().get_repo_landing_revs(
69 ScmModel().get_repo_landing_revs(
70 self.request.translate, self.db_repo)
70 self.request.translate, self.db_repo)
71
71
72 c.personal_repo_group = c.auth_user.personal_repo_group
72 c.personal_repo_group = c.auth_user.personal_repo_group
73 c.repo_fields = RepositoryField.query()\
73 c.repo_fields = RepositoryField.query()\
74 .filter(RepositoryField.repository == self.db_repo).all()
74 .filter(RepositoryField.repository == self.db_repo).all()
75 return c
75 return c
76
76
77 def _get_schema(self, c, old_values=None):
77 def _get_schema(self, c, old_values=None):
78 return repo_schema.RepoSettingsSchema().bind(
78 return repo_schema.RepoSettingsSchema().bind(
79 repo_type=self.db_repo.repo_type,
79 repo_type=self.db_repo.repo_type,
80 repo_type_options=[self.db_repo.repo_type],
80 repo_type_options=[self.db_repo.repo_type],
81 repo_ref_options=c.landing_revs_choices,
81 repo_ref_options=c.landing_revs_choices,
82 repo_ref_items=c.landing_revs,
82 repo_ref_items=c.landing_revs,
83 repo_repo_group_options=c.repo_groups_choices,
83 repo_repo_group_options=c.repo_groups_choices,
84 repo_repo_group_items=c.repo_groups,
84 repo_repo_group_items=c.repo_groups,
85 # user caller
85 # user caller
86 user=self._rhodecode_user,
86 user=self._rhodecode_user,
87 old_values=old_values
87 old_values=old_values
88 )
88 )
89
89
90 @LoginRequired()
90 @LoginRequired()
91 @HasRepoPermissionAnyDecorator('repository.admin')
91 @HasRepoPermissionAnyDecorator('repository.admin')
92 @view_config(
92 @view_config(
93 route_name='edit_repo', request_method='GET',
93 route_name='edit_repo', request_method='GET',
94 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
94 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
95 def edit_settings(self):
95 def edit_settings(self):
96 c = self.load_default_context()
96 c = self.load_default_context()
97 c.active = 'settings'
97 c.active = 'settings'
98
98
99 defaults = RepoModel()._get_defaults(self.db_repo_name)
99 defaults = RepoModel()._get_defaults(self.db_repo_name)
100 defaults['repo_owner'] = defaults['user']
100 defaults['repo_owner'] = defaults['user']
101 defaults['repo_landing_commit_ref'] = defaults['repo_landing_rev']
101 defaults['repo_landing_commit_ref'] = defaults['repo_landing_rev']
102
102
103 schema = self._get_schema(c)
103 schema = self._get_schema(c)
104 c.form = RcForm(schema, appstruct=defaults)
104 c.form = RcForm(schema, appstruct=defaults)
105 return self._get_template_context(c)
105 return self._get_template_context(c)
106
106
107 @LoginRequired()
107 @LoginRequired()
108 @HasRepoPermissionAnyDecorator('repository.admin')
108 @HasRepoPermissionAnyDecorator('repository.admin')
109 @CSRFRequired()
109 @CSRFRequired()
110 @view_config(
110 @view_config(
111 route_name='edit_repo', request_method='POST',
111 route_name='edit_repo', request_method='POST',
112 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
112 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
113 def edit_settings_update(self):
113 def edit_settings_update(self):
114 _ = self.request.translate
114 _ = self.request.translate
115 c = self.load_default_context()
115 c = self.load_default_context()
116 c.active = 'settings'
116 c.active = 'settings'
117 old_repo_name = self.db_repo_name
117 old_repo_name = self.db_repo_name
118
118
119 old_values = self.db_repo.get_api_data()
119 old_values = self.db_repo.get_api_data()
120 schema = self._get_schema(c, old_values=old_values)
120 schema = self._get_schema(c, old_values=old_values)
121
121
122 c.form = RcForm(schema)
122 c.form = RcForm(schema)
123 pstruct = self.request.POST.items()
123 pstruct = self.request.POST.items()
124 pstruct.append(('repo_type', self.db_repo.repo_type))
124 pstruct.append(('repo_type', self.db_repo.repo_type))
125 try:
125 try:
126 schema_data = c.form.validate(pstruct)
126 schema_data = c.form.validate(pstruct)
127 except deform.ValidationFailure as err_form:
127 except deform.ValidationFailure as err_form:
128 return self._get_template_context(c)
128 return self._get_template_context(c)
129
129
130 # data is now VALID, proceed with updates
130 # data is now VALID, proceed with updates
131 # save validated data back into the updates dict
131 # save validated data back into the updates dict
132 validated_updates = dict(
132 validated_updates = dict(
133 repo_name=schema_data['repo_group']['repo_name_without_group'],
133 repo_name=schema_data['repo_group']['repo_name_without_group'],
134 repo_group=schema_data['repo_group']['repo_group_id'],
134 repo_group=schema_data['repo_group']['repo_group_id'],
135
135
136 user=schema_data['repo_owner'],
136 user=schema_data['repo_owner'],
137 repo_description=schema_data['repo_description'],
137 repo_description=schema_data['repo_description'],
138 repo_private=schema_data['repo_private'],
138 repo_private=schema_data['repo_private'],
139 clone_uri=schema_data['repo_clone_uri'],
139 clone_uri=schema_data['repo_clone_uri'],
140 push_uri=schema_data['repo_push_uri'],
140 push_uri=schema_data['repo_push_uri'],
141 repo_landing_rev=schema_data['repo_landing_commit_ref'],
141 repo_landing_rev=schema_data['repo_landing_commit_ref'],
142 repo_enable_statistics=schema_data['repo_enable_statistics'],
142 repo_enable_statistics=schema_data['repo_enable_statistics'],
143 repo_enable_locking=schema_data['repo_enable_locking'],
143 repo_enable_locking=schema_data['repo_enable_locking'],
144 repo_enable_downloads=schema_data['repo_enable_downloads'],
144 repo_enable_downloads=schema_data['repo_enable_downloads'],
145 )
145 )
146 # detect if SYNC URI changed, if we get OLD means we keep old values
146 # detect if SYNC URI changed, if we get OLD means we keep old values
147 if schema_data['repo_clone_uri_change'] == 'OLD':
147 if schema_data['repo_clone_uri_change'] == 'OLD':
148 validated_updates['clone_uri'] = self.db_repo.clone_uri
148 validated_updates['clone_uri'] = self.db_repo.clone_uri
149
149
150 if schema_data['repo_push_uri_change'] == 'OLD':
150 if schema_data['repo_push_uri_change'] == 'OLD':
151 validated_updates['push_uri'] = self.db_repo.push_uri
151 validated_updates['push_uri'] = self.db_repo.push_uri
152
152
153 # use the new full name for redirect
153 # use the new full name for redirect
154 new_repo_name = schema_data['repo_group']['repo_name_with_group']
154 new_repo_name = schema_data['repo_group']['repo_name_with_group']
155
155
156 # save extra fields into our validated data
156 # save extra fields into our validated data
157 for key, value in pstruct:
157 for key, value in pstruct:
158 if key.startswith(RepositoryField.PREFIX):
158 if key.startswith(RepositoryField.PREFIX):
159 validated_updates[key] = value
159 validated_updates[key] = value
160
160
161 try:
161 try:
162 RepoModel().update(self.db_repo, **validated_updates)
162 RepoModel().update(self.db_repo, **validated_updates)
163 ScmModel().mark_for_invalidation(new_repo_name)
163 ScmModel().mark_for_invalidation(new_repo_name)
164
164
165 audit_logger.store_web(
165 audit_logger.store_web(
166 'repo.edit', action_data={'old_data': old_values},
166 'repo.edit', action_data={'old_data': old_values},
167 user=self._rhodecode_user, repo=self.db_repo)
167 user=self._rhodecode_user, repo=self.db_repo)
168
168
169 Session().commit()
169 Session().commit()
170
170
171 h.flash(_('Repository `{}` updated successfully').format(
171 h.flash(_('Repository `{}` updated successfully').format(old_repo_name),
172 old_repo_name), category='success')
172 category='success')
173 except Exception:
173 except Exception:
174 log.exception("Exception during update of repository")
174 log.exception("Exception during update of repository")
175 h.flash(_('Error occurred during update of repository {}').format(
175 h.flash(_('Error occurred during update of repository {}').format(
176 old_repo_name), category='error')
176 old_repo_name), category='error')
177
177
178 name_changed = old_repo_name != new_repo_name
178 name_changed = old_repo_name != new_repo_name
179 if name_changed:
179 if name_changed:
180 current_perms = self.db_repo.permissions(expand_from_user_groups=True)
181 affected_user_ids = [perm['user_id'] for perm in current_perms]
182
183 # NOTE(marcink): also add owner maybe it has changed
180 owner = User.get_by_username(schema_data['repo_owner'])
184 owner = User.get_by_username(schema_data['repo_owner'])
181 owner_id = owner.user_id if owner else self._rhodecode_user.user_id
185 owner_id = owner.user_id if owner else self._rhodecode_user.user_id
182 events.trigger(events.UserPermissionsChange([
186 affected_user_ids.extend([self._rhodecode_user.user_id, owner_id])
183 self._rhodecode_user.user_id, owner_id]))
187 events.trigger(events.UserPermissionsChange(affected_user_ids))
184
188
185 raise HTTPFound(
189 raise HTTPFound(
186 h.route_path('edit_repo', repo_name=new_repo_name))
190 h.route_path('edit_repo', repo_name=new_repo_name))
187
191
188 @LoginRequired()
192 @LoginRequired()
189 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
193 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
190 @view_config(
194 @view_config(
191 route_name='repo_edit_toggle_locking', request_method='GET',
195 route_name='repo_edit_toggle_locking', request_method='GET',
192 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
196 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
193 def toggle_locking(self):
197 def toggle_locking(self):
194 """
198 """
195 Toggle locking of repository by simple GET call to url
199 Toggle locking of repository by simple GET call to url
196 """
200 """
197 _ = self.request.translate
201 _ = self.request.translate
198 repo = self.db_repo
202 repo = self.db_repo
199
203
200 try:
204 try:
201 if repo.enable_locking:
205 if repo.enable_locking:
202 if repo.locked[0]:
206 if repo.locked[0]:
203 Repository.unlock(repo)
207 Repository.unlock(repo)
204 action = _('Unlocked')
208 action = _('Unlocked')
205 else:
209 else:
206 Repository.lock(
210 Repository.lock(
207 repo, self._rhodecode_user.user_id,
211 repo, self._rhodecode_user.user_id,
208 lock_reason=Repository.LOCK_WEB)
212 lock_reason=Repository.LOCK_WEB)
209 action = _('Locked')
213 action = _('Locked')
210
214
211 h.flash(_('Repository has been %s') % action,
215 h.flash(_('Repository has been %s') % action,
212 category='success')
216 category='success')
213 except Exception:
217 except Exception:
214 log.exception("Exception during unlocking")
218 log.exception("Exception during unlocking")
215 h.flash(_('An error occurred during unlocking'),
219 h.flash(_('An error occurred during unlocking'),
216 category='error')
220 category='error')
217 raise HTTPFound(
221 raise HTTPFound(
218 h.route_path('repo_summary', repo_name=self.db_repo_name))
222 h.route_path('repo_summary', repo_name=self.db_repo_name))
219
223
220 @LoginRequired()
224 @LoginRequired()
221 @HasRepoPermissionAnyDecorator('repository.admin')
225 @HasRepoPermissionAnyDecorator('repository.admin')
222 @view_config(
226 @view_config(
223 route_name='edit_repo_statistics', request_method='GET',
227 route_name='edit_repo_statistics', request_method='GET',
224 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
228 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
225 def edit_statistics_form(self):
229 def edit_statistics_form(self):
226 c = self.load_default_context()
230 c = self.load_default_context()
227
231
228 if self.db_repo.stats:
232 if self.db_repo.stats:
229 # this is on what revision we ended up so we add +1 for count
233 # this is on what revision we ended up so we add +1 for count
230 last_rev = self.db_repo.stats.stat_on_revision + 1
234 last_rev = self.db_repo.stats.stat_on_revision + 1
231 else:
235 else:
232 last_rev = 0
236 last_rev = 0
233
237
234 c.active = 'statistics'
238 c.active = 'statistics'
235 c.stats_revision = last_rev
239 c.stats_revision = last_rev
236 c.repo_last_rev = self.rhodecode_vcs_repo.count()
240 c.repo_last_rev = self.rhodecode_vcs_repo.count()
237
241
238 if last_rev == 0 or c.repo_last_rev == 0:
242 if last_rev == 0 or c.repo_last_rev == 0:
239 c.stats_percentage = 0
243 c.stats_percentage = 0
240 else:
244 else:
241 c.stats_percentage = '%.2f' % (
245 c.stats_percentage = '%.2f' % (
242 (float((last_rev)) / c.repo_last_rev) * 100)
246 (float((last_rev)) / c.repo_last_rev) * 100)
243 return self._get_template_context(c)
247 return self._get_template_context(c)
244
248
245 @LoginRequired()
249 @LoginRequired()
246 @HasRepoPermissionAnyDecorator('repository.admin')
250 @HasRepoPermissionAnyDecorator('repository.admin')
247 @CSRFRequired()
251 @CSRFRequired()
248 @view_config(
252 @view_config(
249 route_name='edit_repo_statistics_reset', request_method='POST',
253 route_name='edit_repo_statistics_reset', request_method='POST',
250 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
254 renderer='rhodecode:templates/admin/repos/repo_edit.mako')
251 def repo_statistics_reset(self):
255 def repo_statistics_reset(self):
252 _ = self.request.translate
256 _ = self.request.translate
253
257
254 try:
258 try:
255 RepoModel().delete_stats(self.db_repo_name)
259 RepoModel().delete_stats(self.db_repo_name)
256 Session().commit()
260 Session().commit()
257 except Exception:
261 except Exception:
258 log.exception('Edit statistics failure')
262 log.exception('Edit statistics failure')
259 h.flash(_('An error occurred during deletion of repository stats'),
263 h.flash(_('An error occurred during deletion of repository stats'),
260 category='error')
264 category='error')
261 raise HTTPFound(
265 raise HTTPFound(
262 h.route_path('edit_repo_statistics', repo_name=self.db_repo_name))
266 h.route_path('edit_repo_statistics', repo_name=self.db_repo_name))
1 NO CONTENT: modified file
NO CONTENT: modified file
The requested commit or file is too big and content was truncated. Show full diff
General Comments 0
You need to be logged in to leave comments. Login now