Show More
@@ -1,1414 +1,1418 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2020 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2020 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import logging |
|
21 | import logging | |
22 | import datetime |
|
22 | import datetime | |
23 | import formencode |
|
23 | import formencode | |
24 | import formencode.htmlfill |
|
24 | import formencode.htmlfill | |
25 |
|
25 | |||
26 | from pyramid.httpexceptions import HTTPFound |
|
26 | from pyramid.httpexceptions import HTTPFound | |
27 | from pyramid.view import view_config |
|
27 | from pyramid.view import view_config | |
28 | from pyramid.renderers import render |
|
28 | from pyramid.renderers import render | |
29 | from pyramid.response import Response |
|
29 | from pyramid.response import Response | |
30 |
|
30 | |||
31 | from rhodecode import events |
|
31 | from rhodecode import events | |
32 | from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView |
|
32 | from rhodecode.apps._base import BaseAppView, DataGridAppView, UserAppView | |
33 | from rhodecode.apps.ssh_support import SshKeyFileChangeEvent |
|
33 | from rhodecode.apps.ssh_support import SshKeyFileChangeEvent | |
34 | from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin |
|
34 | from rhodecode.authentication.base import get_authn_registry, RhodeCodeExternalAuthPlugin | |
35 | from rhodecode.authentication.plugins import auth_rhodecode |
|
35 | from rhodecode.authentication.plugins import auth_rhodecode | |
36 | from rhodecode.events import trigger |
|
36 | from rhodecode.events import trigger | |
37 | from rhodecode.model.db import true, UserNotice |
|
37 | from rhodecode.model.db import true, UserNotice | |
38 |
|
38 | |||
39 | from rhodecode.lib import audit_logger, rc_cache |
|
39 | from rhodecode.lib import audit_logger, rc_cache, auth | |
40 | from rhodecode.lib.exceptions import ( |
|
40 | from rhodecode.lib.exceptions import ( | |
41 | UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException, |
|
41 | UserCreationError, UserOwnsReposException, UserOwnsRepoGroupsException, | |
42 | UserOwnsUserGroupsException, UserOwnsPullRequestsException, |
|
42 | UserOwnsUserGroupsException, UserOwnsPullRequestsException, | |
43 | UserOwnsArtifactsException, DefaultUserException) |
|
43 | UserOwnsArtifactsException, DefaultUserException) | |
44 | from rhodecode.lib.ext_json import json |
|
44 | from rhodecode.lib.ext_json import json | |
45 | from rhodecode.lib.auth import ( |
|
45 | from rhodecode.lib.auth import ( | |
46 | LoginRequired, HasPermissionAllDecorator, CSRFRequired) |
|
46 | LoginRequired, HasPermissionAllDecorator, CSRFRequired) | |
47 | from rhodecode.lib import helpers as h |
|
47 | from rhodecode.lib import helpers as h | |
48 | from rhodecode.lib.helpers import SqlPage |
|
48 | from rhodecode.lib.helpers import SqlPage | |
49 | from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict |
|
49 | from rhodecode.lib.utils2 import safe_int, safe_unicode, AttributeDict | |
50 | from rhodecode.model.auth_token import AuthTokenModel |
|
50 | from rhodecode.model.auth_token import AuthTokenModel | |
51 | from rhodecode.model.forms import ( |
|
51 | from rhodecode.model.forms import ( | |
52 | UserForm, UserIndividualPermissionsForm, UserPermissionsForm, |
|
52 | UserForm, UserIndividualPermissionsForm, UserPermissionsForm, | |
53 | UserExtraEmailForm, UserExtraIpForm) |
|
53 | UserExtraEmailForm, UserExtraIpForm) | |
54 | from rhodecode.model.permission import PermissionModel |
|
54 | from rhodecode.model.permission import PermissionModel | |
55 | from rhodecode.model.repo_group import RepoGroupModel |
|
55 | from rhodecode.model.repo_group import RepoGroupModel | |
56 | from rhodecode.model.ssh_key import SshKeyModel |
|
56 | from rhodecode.model.ssh_key import SshKeyModel | |
57 | from rhodecode.model.user import UserModel |
|
57 | from rhodecode.model.user import UserModel | |
58 | from rhodecode.model.user_group import UserGroupModel |
|
58 | from rhodecode.model.user_group import UserGroupModel | |
59 | from rhodecode.model.db import ( |
|
59 | from rhodecode.model.db import ( | |
60 | or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap, |
|
60 | or_, coalesce,IntegrityError, User, UserGroup, UserIpMap, UserEmailMap, | |
61 | UserApiKeys, UserSshKeys, RepoGroup) |
|
61 | UserApiKeys, UserSshKeys, RepoGroup) | |
62 | from rhodecode.model.meta import Session |
|
62 | from rhodecode.model.meta import Session | |
63 |
|
63 | |||
64 | log = logging.getLogger(__name__) |
|
64 | log = logging.getLogger(__name__) | |
65 |
|
65 | |||
66 |
|
66 | |||
67 | class AdminUsersView(BaseAppView, DataGridAppView): |
|
67 | class AdminUsersView(BaseAppView, DataGridAppView): | |
68 |
|
68 | |||
69 | def load_default_context(self): |
|
69 | def load_default_context(self): | |
70 | c = self._get_local_tmpl_context() |
|
70 | c = self._get_local_tmpl_context() | |
71 | return c |
|
71 | return c | |
72 |
|
72 | |||
73 | @LoginRequired() |
|
73 | @LoginRequired() | |
74 | @HasPermissionAllDecorator('hg.admin') |
|
74 | @HasPermissionAllDecorator('hg.admin') | |
75 | @view_config( |
|
75 | @view_config( | |
76 | route_name='users', request_method='GET', |
|
76 | route_name='users', request_method='GET', | |
77 | renderer='rhodecode:templates/admin/users/users.mako') |
|
77 | renderer='rhodecode:templates/admin/users/users.mako') | |
78 | def users_list(self): |
|
78 | def users_list(self): | |
79 | c = self.load_default_context() |
|
79 | c = self.load_default_context() | |
80 | return self._get_template_context(c) |
|
80 | return self._get_template_context(c) | |
81 |
|
81 | |||
82 | @LoginRequired() |
|
82 | @LoginRequired() | |
83 | @HasPermissionAllDecorator('hg.admin') |
|
83 | @HasPermissionAllDecorator('hg.admin') | |
84 | @view_config( |
|
84 | @view_config( | |
85 | # renderer defined below |
|
85 | # renderer defined below | |
86 | route_name='users_data', request_method='GET', |
|
86 | route_name='users_data', request_method='GET', | |
87 | renderer='json_ext', xhr=True) |
|
87 | renderer='json_ext', xhr=True) | |
88 | def users_list_data(self): |
|
88 | def users_list_data(self): | |
89 | self.load_default_context() |
|
89 | self.load_default_context() | |
90 | column_map = { |
|
90 | column_map = { | |
91 | 'first_name': 'name', |
|
91 | 'first_name': 'name', | |
92 | 'last_name': 'lastname', |
|
92 | 'last_name': 'lastname', | |
93 | } |
|
93 | } | |
94 | draw, start, limit = self._extract_chunk(self.request) |
|
94 | draw, start, limit = self._extract_chunk(self.request) | |
95 | search_q, order_by, order_dir = self._extract_ordering( |
|
95 | search_q, order_by, order_dir = self._extract_ordering( | |
96 | self.request, column_map=column_map) |
|
96 | self.request, column_map=column_map) | |
97 | _render = self.request.get_partial_renderer( |
|
97 | _render = self.request.get_partial_renderer( | |
98 | 'rhodecode:templates/data_table/_dt_elements.mako') |
|
98 | 'rhodecode:templates/data_table/_dt_elements.mako') | |
99 |
|
99 | |||
100 | def user_actions(user_id, username): |
|
100 | def user_actions(user_id, username): | |
101 | return _render("user_actions", user_id, username) |
|
101 | return _render("user_actions", user_id, username) | |
102 |
|
102 | |||
103 | users_data_total_count = User.query()\ |
|
103 | users_data_total_count = User.query()\ | |
104 | .filter(User.username != User.DEFAULT_USER) \ |
|
104 | .filter(User.username != User.DEFAULT_USER) \ | |
105 | .count() |
|
105 | .count() | |
106 |
|
106 | |||
107 | users_data_total_inactive_count = User.query()\ |
|
107 | users_data_total_inactive_count = User.query()\ | |
108 | .filter(User.username != User.DEFAULT_USER) \ |
|
108 | .filter(User.username != User.DEFAULT_USER) \ | |
109 | .filter(User.active != true())\ |
|
109 | .filter(User.active != true())\ | |
110 | .count() |
|
110 | .count() | |
111 |
|
111 | |||
112 | # json generate |
|
112 | # json generate | |
113 | base_q = User.query().filter(User.username != User.DEFAULT_USER) |
|
113 | base_q = User.query().filter(User.username != User.DEFAULT_USER) | |
114 | base_inactive_q = base_q.filter(User.active != true()) |
|
114 | base_inactive_q = base_q.filter(User.active != true()) | |
115 |
|
115 | |||
116 | if search_q: |
|
116 | if search_q: | |
117 | like_expression = u'%{}%'.format(safe_unicode(search_q)) |
|
117 | like_expression = u'%{}%'.format(safe_unicode(search_q)) | |
118 | base_q = base_q.filter(or_( |
|
118 | base_q = base_q.filter(or_( | |
119 | User.username.ilike(like_expression), |
|
119 | User.username.ilike(like_expression), | |
120 | User._email.ilike(like_expression), |
|
120 | User._email.ilike(like_expression), | |
121 | User.name.ilike(like_expression), |
|
121 | User.name.ilike(like_expression), | |
122 | User.lastname.ilike(like_expression), |
|
122 | User.lastname.ilike(like_expression), | |
123 | )) |
|
123 | )) | |
124 | base_inactive_q = base_q.filter(User.active != true()) |
|
124 | base_inactive_q = base_q.filter(User.active != true()) | |
125 |
|
125 | |||
126 | users_data_total_filtered_count = base_q.count() |
|
126 | users_data_total_filtered_count = base_q.count() | |
127 | users_data_total_filtered_inactive_count = base_inactive_q.count() |
|
127 | users_data_total_filtered_inactive_count = base_inactive_q.count() | |
128 |
|
128 | |||
129 | sort_col = getattr(User, order_by, None) |
|
129 | sort_col = getattr(User, order_by, None) | |
130 | if sort_col: |
|
130 | if sort_col: | |
131 | if order_dir == 'asc': |
|
131 | if order_dir == 'asc': | |
132 | # handle null values properly to order by NULL last |
|
132 | # handle null values properly to order by NULL last | |
133 | if order_by in ['last_activity']: |
|
133 | if order_by in ['last_activity']: | |
134 | sort_col = coalesce(sort_col, datetime.date.max) |
|
134 | sort_col = coalesce(sort_col, datetime.date.max) | |
135 | sort_col = sort_col.asc() |
|
135 | sort_col = sort_col.asc() | |
136 | else: |
|
136 | else: | |
137 | # handle null values properly to order by NULL last |
|
137 | # handle null values properly to order by NULL last | |
138 | if order_by in ['last_activity']: |
|
138 | if order_by in ['last_activity']: | |
139 | sort_col = coalesce(sort_col, datetime.date.min) |
|
139 | sort_col = coalesce(sort_col, datetime.date.min) | |
140 | sort_col = sort_col.desc() |
|
140 | sort_col = sort_col.desc() | |
141 |
|
141 | |||
142 | base_q = base_q.order_by(sort_col) |
|
142 | base_q = base_q.order_by(sort_col) | |
143 | base_q = base_q.offset(start).limit(limit) |
|
143 | base_q = base_q.offset(start).limit(limit) | |
144 |
|
144 | |||
145 | users_list = base_q.all() |
|
145 | users_list = base_q.all() | |
146 |
|
146 | |||
147 | users_data = [] |
|
147 | users_data = [] | |
148 | for user in users_list: |
|
148 | for user in users_list: | |
149 | users_data.append({ |
|
149 | users_data.append({ | |
150 | "username": h.gravatar_with_user(self.request, user.username), |
|
150 | "username": h.gravatar_with_user(self.request, user.username), | |
151 | "email": user.email, |
|
151 | "email": user.email, | |
152 | "first_name": user.first_name, |
|
152 | "first_name": user.first_name, | |
153 | "last_name": user.last_name, |
|
153 | "last_name": user.last_name, | |
154 | "last_login": h.format_date(user.last_login), |
|
154 | "last_login": h.format_date(user.last_login), | |
155 | "last_activity": h.format_date(user.last_activity), |
|
155 | "last_activity": h.format_date(user.last_activity), | |
156 | "active": h.bool2icon(user.active), |
|
156 | "active": h.bool2icon(user.active), | |
157 | "active_raw": user.active, |
|
157 | "active_raw": user.active, | |
158 | "admin": h.bool2icon(user.admin), |
|
158 | "admin": h.bool2icon(user.admin), | |
159 | "extern_type": user.extern_type, |
|
159 | "extern_type": user.extern_type, | |
160 | "extern_name": user.extern_name, |
|
160 | "extern_name": user.extern_name, | |
161 | "action": user_actions(user.user_id, user.username), |
|
161 | "action": user_actions(user.user_id, user.username), | |
162 | }) |
|
162 | }) | |
163 | data = ({ |
|
163 | data = ({ | |
164 | 'draw': draw, |
|
164 | 'draw': draw, | |
165 | 'data': users_data, |
|
165 | 'data': users_data, | |
166 | 'recordsTotal': users_data_total_count, |
|
166 | 'recordsTotal': users_data_total_count, | |
167 | 'recordsFiltered': users_data_total_filtered_count, |
|
167 | 'recordsFiltered': users_data_total_filtered_count, | |
168 | 'recordsTotalInactive': users_data_total_inactive_count, |
|
168 | 'recordsTotalInactive': users_data_total_inactive_count, | |
169 | 'recordsFilteredInactive': users_data_total_filtered_inactive_count |
|
169 | 'recordsFilteredInactive': users_data_total_filtered_inactive_count | |
170 | }) |
|
170 | }) | |
171 |
|
171 | |||
172 | return data |
|
172 | return data | |
173 |
|
173 | |||
174 | def _set_personal_repo_group_template_vars(self, c_obj): |
|
174 | def _set_personal_repo_group_template_vars(self, c_obj): | |
175 | DummyUser = AttributeDict({ |
|
175 | DummyUser = AttributeDict({ | |
176 | 'username': '${username}', |
|
176 | 'username': '${username}', | |
177 | 'user_id': '${user_id}', |
|
177 | 'user_id': '${user_id}', | |
178 | }) |
|
178 | }) | |
179 | c_obj.default_create_repo_group = RepoGroupModel() \ |
|
179 | c_obj.default_create_repo_group = RepoGroupModel() \ | |
180 | .get_default_create_personal_repo_group() |
|
180 | .get_default_create_personal_repo_group() | |
181 | c_obj.personal_repo_group_name = RepoGroupModel() \ |
|
181 | c_obj.personal_repo_group_name = RepoGroupModel() \ | |
182 | .get_personal_group_name(DummyUser) |
|
182 | .get_personal_group_name(DummyUser) | |
183 |
|
183 | |||
184 | @LoginRequired() |
|
184 | @LoginRequired() | |
185 | @HasPermissionAllDecorator('hg.admin') |
|
185 | @HasPermissionAllDecorator('hg.admin') | |
186 | @view_config( |
|
186 | @view_config( | |
187 | route_name='users_new', request_method='GET', |
|
187 | route_name='users_new', request_method='GET', | |
188 | renderer='rhodecode:templates/admin/users/user_add.mako') |
|
188 | renderer='rhodecode:templates/admin/users/user_add.mako') | |
189 | def users_new(self): |
|
189 | def users_new(self): | |
190 | _ = self.request.translate |
|
190 | _ = self.request.translate | |
191 | c = self.load_default_context() |
|
191 | c = self.load_default_context() | |
192 | c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid |
|
192 | c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid | |
193 | self._set_personal_repo_group_template_vars(c) |
|
193 | self._set_personal_repo_group_template_vars(c) | |
194 | return self._get_template_context(c) |
|
194 | return self._get_template_context(c) | |
195 |
|
195 | |||
196 | @LoginRequired() |
|
196 | @LoginRequired() | |
197 | @HasPermissionAllDecorator('hg.admin') |
|
197 | @HasPermissionAllDecorator('hg.admin') | |
198 | @CSRFRequired() |
|
198 | @CSRFRequired() | |
199 | @view_config( |
|
199 | @view_config( | |
200 | route_name='users_create', request_method='POST', |
|
200 | route_name='users_create', request_method='POST', | |
201 | renderer='rhodecode:templates/admin/users/user_add.mako') |
|
201 | renderer='rhodecode:templates/admin/users/user_add.mako') | |
202 | def users_create(self): |
|
202 | def users_create(self): | |
203 | _ = self.request.translate |
|
203 | _ = self.request.translate | |
204 | c = self.load_default_context() |
|
204 | c = self.load_default_context() | |
205 | c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid |
|
205 | c.default_extern_type = auth_rhodecode.RhodeCodeAuthPlugin.uid | |
206 | user_model = UserModel() |
|
206 | user_model = UserModel() | |
207 | user_form = UserForm(self.request.translate)() |
|
207 | user_form = UserForm(self.request.translate)() | |
208 | try: |
|
208 | try: | |
209 | form_result = user_form.to_python(dict(self.request.POST)) |
|
209 | form_result = user_form.to_python(dict(self.request.POST)) | |
210 | user = user_model.create(form_result) |
|
210 | user = user_model.create(form_result) | |
211 | Session().flush() |
|
211 | Session().flush() | |
212 | creation_data = user.get_api_data() |
|
212 | creation_data = user.get_api_data() | |
213 | username = form_result['username'] |
|
213 | username = form_result['username'] | |
214 |
|
214 | |||
215 | audit_logger.store_web( |
|
215 | audit_logger.store_web( | |
216 | 'user.create', action_data={'data': creation_data}, |
|
216 | 'user.create', action_data={'data': creation_data}, | |
217 | user=c.rhodecode_user) |
|
217 | user=c.rhodecode_user) | |
218 |
|
218 | |||
219 | user_link = h.link_to( |
|
219 | user_link = h.link_to( | |
220 | h.escape(username), |
|
220 | h.escape(username), | |
221 | h.route_path('user_edit', user_id=user.user_id)) |
|
221 | h.route_path('user_edit', user_id=user.user_id)) | |
222 | h.flash(h.literal(_('Created user %(user_link)s') |
|
222 | h.flash(h.literal(_('Created user %(user_link)s') | |
223 | % {'user_link': user_link}), category='success') |
|
223 | % {'user_link': user_link}), category='success') | |
224 | Session().commit() |
|
224 | Session().commit() | |
225 | except formencode.Invalid as errors: |
|
225 | except formencode.Invalid as errors: | |
226 | self._set_personal_repo_group_template_vars(c) |
|
226 | self._set_personal_repo_group_template_vars(c) | |
227 | data = render( |
|
227 | data = render( | |
228 | 'rhodecode:templates/admin/users/user_add.mako', |
|
228 | 'rhodecode:templates/admin/users/user_add.mako', | |
229 | self._get_template_context(c), self.request) |
|
229 | self._get_template_context(c), self.request) | |
230 | html = formencode.htmlfill.render( |
|
230 | html = formencode.htmlfill.render( | |
231 | data, |
|
231 | data, | |
232 | defaults=errors.value, |
|
232 | defaults=errors.value, | |
233 | errors=errors.error_dict or {}, |
|
233 | errors=errors.error_dict or {}, | |
234 | prefix_error=False, |
|
234 | prefix_error=False, | |
235 | encoding="UTF-8", |
|
235 | encoding="UTF-8", | |
236 | force_defaults=False |
|
236 | force_defaults=False | |
237 | ) |
|
237 | ) | |
238 | return Response(html) |
|
238 | return Response(html) | |
239 | except UserCreationError as e: |
|
239 | except UserCreationError as e: | |
240 | h.flash(e, 'error') |
|
240 | h.flash(e, 'error') | |
241 | except Exception: |
|
241 | except Exception: | |
242 | log.exception("Exception creation of user") |
|
242 | log.exception("Exception creation of user") | |
243 | h.flash(_('Error occurred during creation of user %s') |
|
243 | h.flash(_('Error occurred during creation of user %s') | |
244 | % self.request.POST.get('username'), category='error') |
|
244 | % self.request.POST.get('username'), category='error') | |
245 | raise HTTPFound(h.route_path('users')) |
|
245 | raise HTTPFound(h.route_path('users')) | |
246 |
|
246 | |||
247 |
|
247 | |||
248 | class UsersView(UserAppView): |
|
248 | class UsersView(UserAppView): | |
249 | ALLOW_SCOPED_TOKENS = False |
|
249 | ALLOW_SCOPED_TOKENS = False | |
250 | """ |
|
250 | """ | |
251 | This view has alternative version inside EE, if modified please take a look |
|
251 | This view has alternative version inside EE, if modified please take a look | |
252 | in there as well. |
|
252 | in there as well. | |
253 | """ |
|
253 | """ | |
254 |
|
254 | |||
255 | def get_auth_plugins(self): |
|
255 | def get_auth_plugins(self): | |
256 | valid_plugins = [] |
|
256 | valid_plugins = [] | |
257 | authn_registry = get_authn_registry(self.request.registry) |
|
257 | authn_registry = get_authn_registry(self.request.registry) | |
258 | for plugin in authn_registry.get_plugins_for_authentication(): |
|
258 | for plugin in authn_registry.get_plugins_for_authentication(): | |
259 | if isinstance(plugin, RhodeCodeExternalAuthPlugin): |
|
259 | if isinstance(plugin, RhodeCodeExternalAuthPlugin): | |
260 | valid_plugins.append(plugin) |
|
260 | valid_plugins.append(plugin) | |
261 | elif plugin.name == 'rhodecode': |
|
261 | elif plugin.name == 'rhodecode': | |
262 | valid_plugins.append(plugin) |
|
262 | valid_plugins.append(plugin) | |
263 |
|
263 | |||
264 | # extend our choices if user has set a bound plugin which isn't enabled at the |
|
264 | # extend our choices if user has set a bound plugin which isn't enabled at the | |
265 | # moment |
|
265 | # moment | |
266 | extern_type = self.db_user.extern_type |
|
266 | extern_type = self.db_user.extern_type | |
267 | if extern_type not in [x.uid for x in valid_plugins]: |
|
267 | if extern_type not in [x.uid for x in valid_plugins]: | |
268 | try: |
|
268 | try: | |
269 | plugin = authn_registry.get_plugin_by_uid(extern_type) |
|
269 | plugin = authn_registry.get_plugin_by_uid(extern_type) | |
270 | if plugin: |
|
270 | if plugin: | |
271 | valid_plugins.append(plugin) |
|
271 | valid_plugins.append(plugin) | |
272 |
|
272 | |||
273 | except Exception: |
|
273 | except Exception: | |
274 | log.exception( |
|
274 | log.exception( | |
275 | 'Could not extend user plugins with `{}`'.format(extern_type)) |
|
275 | 'Could not extend user plugins with `{}`'.format(extern_type)) | |
276 | return valid_plugins |
|
276 | return valid_plugins | |
277 |
|
277 | |||
278 | def load_default_context(self): |
|
278 | def load_default_context(self): | |
279 | req = self.request |
|
279 | req = self.request | |
280 |
|
280 | |||
281 | c = self._get_local_tmpl_context() |
|
281 | c = self._get_local_tmpl_context() | |
282 | c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS |
|
282 | c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS | |
283 | c.allowed_languages = [ |
|
283 | c.allowed_languages = [ | |
284 | ('en', 'English (en)'), |
|
284 | ('en', 'English (en)'), | |
285 | ('de', 'German (de)'), |
|
285 | ('de', 'German (de)'), | |
286 | ('fr', 'French (fr)'), |
|
286 | ('fr', 'French (fr)'), | |
287 | ('it', 'Italian (it)'), |
|
287 | ('it', 'Italian (it)'), | |
288 | ('ja', 'Japanese (ja)'), |
|
288 | ('ja', 'Japanese (ja)'), | |
289 | ('pl', 'Polish (pl)'), |
|
289 | ('pl', 'Polish (pl)'), | |
290 | ('pt', 'Portuguese (pt)'), |
|
290 | ('pt', 'Portuguese (pt)'), | |
291 | ('ru', 'Russian (ru)'), |
|
291 | ('ru', 'Russian (ru)'), | |
292 | ('zh', 'Chinese (zh)'), |
|
292 | ('zh', 'Chinese (zh)'), | |
293 | ] |
|
293 | ] | |
294 |
|
294 | |||
295 | c.allowed_extern_types = [ |
|
295 | c.allowed_extern_types = [ | |
296 | (x.uid, x.get_display_name()) for x in self.get_auth_plugins() |
|
296 | (x.uid, x.get_display_name()) for x in self.get_auth_plugins() | |
297 | ] |
|
297 | ] | |
|
298 | perms = req.registry.settings.get('available_permissions') | |||
|
299 | if not perms: | |||
|
300 | # inject info about available permissions | |||
|
301 | auth.set_available_permissions(req.registry.settings) | |||
298 |
|
302 | |||
299 | c.available_permissions = req.registry.settings['available_permissions'] |
|
303 | c.available_permissions = req.registry.settings['available_permissions'] | |
300 | PermissionModel().set_global_permission_choices( |
|
304 | PermissionModel().set_global_permission_choices( | |
301 | c, gettext_translator=req.translate) |
|
305 | c, gettext_translator=req.translate) | |
302 |
|
306 | |||
303 | return c |
|
307 | return c | |
304 |
|
308 | |||
305 | @LoginRequired() |
|
309 | @LoginRequired() | |
306 | @HasPermissionAllDecorator('hg.admin') |
|
310 | @HasPermissionAllDecorator('hg.admin') | |
307 | @CSRFRequired() |
|
311 | @CSRFRequired() | |
308 | @view_config( |
|
312 | @view_config( | |
309 | route_name='user_update', request_method='POST', |
|
313 | route_name='user_update', request_method='POST', | |
310 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
314 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
311 | def user_update(self): |
|
315 | def user_update(self): | |
312 | _ = self.request.translate |
|
316 | _ = self.request.translate | |
313 | c = self.load_default_context() |
|
317 | c = self.load_default_context() | |
314 |
|
318 | |||
315 | user_id = self.db_user_id |
|
319 | user_id = self.db_user_id | |
316 | c.user = self.db_user |
|
320 | c.user = self.db_user | |
317 |
|
321 | |||
318 | c.active = 'profile' |
|
322 | c.active = 'profile' | |
319 | c.extern_type = c.user.extern_type |
|
323 | c.extern_type = c.user.extern_type | |
320 | c.extern_name = c.user.extern_name |
|
324 | c.extern_name = c.user.extern_name | |
321 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) |
|
325 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) | |
322 | available_languages = [x[0] for x in c.allowed_languages] |
|
326 | available_languages = [x[0] for x in c.allowed_languages] | |
323 | _form = UserForm(self.request.translate, edit=True, |
|
327 | _form = UserForm(self.request.translate, edit=True, | |
324 | available_languages=available_languages, |
|
328 | available_languages=available_languages, | |
325 | old_data={'user_id': user_id, |
|
329 | old_data={'user_id': user_id, | |
326 | 'email': c.user.email})() |
|
330 | 'email': c.user.email})() | |
327 | form_result = {} |
|
331 | form_result = {} | |
328 | old_values = c.user.get_api_data() |
|
332 | old_values = c.user.get_api_data() | |
329 | try: |
|
333 | try: | |
330 | form_result = _form.to_python(dict(self.request.POST)) |
|
334 | form_result = _form.to_python(dict(self.request.POST)) | |
331 | skip_attrs = ['extern_name'] |
|
335 | skip_attrs = ['extern_name'] | |
332 | # TODO: plugin should define if username can be updated |
|
336 | # TODO: plugin should define if username can be updated | |
333 | if c.extern_type != "rhodecode": |
|
337 | if c.extern_type != "rhodecode": | |
334 | # forbid updating username for external accounts |
|
338 | # forbid updating username for external accounts | |
335 | skip_attrs.append('username') |
|
339 | skip_attrs.append('username') | |
336 |
|
340 | |||
337 | UserModel().update_user( |
|
341 | UserModel().update_user( | |
338 | user_id, skip_attrs=skip_attrs, **form_result) |
|
342 | user_id, skip_attrs=skip_attrs, **form_result) | |
339 |
|
343 | |||
340 | audit_logger.store_web( |
|
344 | audit_logger.store_web( | |
341 | 'user.edit', action_data={'old_data': old_values}, |
|
345 | 'user.edit', action_data={'old_data': old_values}, | |
342 | user=c.rhodecode_user) |
|
346 | user=c.rhodecode_user) | |
343 |
|
347 | |||
344 | Session().commit() |
|
348 | Session().commit() | |
345 | h.flash(_('User updated successfully'), category='success') |
|
349 | h.flash(_('User updated successfully'), category='success') | |
346 | except formencode.Invalid as errors: |
|
350 | except formencode.Invalid as errors: | |
347 | data = render( |
|
351 | data = render( | |
348 | 'rhodecode:templates/admin/users/user_edit.mako', |
|
352 | 'rhodecode:templates/admin/users/user_edit.mako', | |
349 | self._get_template_context(c), self.request) |
|
353 | self._get_template_context(c), self.request) | |
350 | html = formencode.htmlfill.render( |
|
354 | html = formencode.htmlfill.render( | |
351 | data, |
|
355 | data, | |
352 | defaults=errors.value, |
|
356 | defaults=errors.value, | |
353 | errors=errors.error_dict or {}, |
|
357 | errors=errors.error_dict or {}, | |
354 | prefix_error=False, |
|
358 | prefix_error=False, | |
355 | encoding="UTF-8", |
|
359 | encoding="UTF-8", | |
356 | force_defaults=False |
|
360 | force_defaults=False | |
357 | ) |
|
361 | ) | |
358 | return Response(html) |
|
362 | return Response(html) | |
359 | except UserCreationError as e: |
|
363 | except UserCreationError as e: | |
360 | h.flash(e, 'error') |
|
364 | h.flash(e, 'error') | |
361 | except Exception: |
|
365 | except Exception: | |
362 | log.exception("Exception updating user") |
|
366 | log.exception("Exception updating user") | |
363 | h.flash(_('Error occurred during update of user %s') |
|
367 | h.flash(_('Error occurred during update of user %s') | |
364 | % form_result.get('username'), category='error') |
|
368 | % form_result.get('username'), category='error') | |
365 | raise HTTPFound(h.route_path('user_edit', user_id=user_id)) |
|
369 | raise HTTPFound(h.route_path('user_edit', user_id=user_id)) | |
366 |
|
370 | |||
367 | @LoginRequired() |
|
371 | @LoginRequired() | |
368 | @HasPermissionAllDecorator('hg.admin') |
|
372 | @HasPermissionAllDecorator('hg.admin') | |
369 | @CSRFRequired() |
|
373 | @CSRFRequired() | |
370 | @view_config( |
|
374 | @view_config( | |
371 | route_name='user_delete', request_method='POST', |
|
375 | route_name='user_delete', request_method='POST', | |
372 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
376 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
373 | def user_delete(self): |
|
377 | def user_delete(self): | |
374 | _ = self.request.translate |
|
378 | _ = self.request.translate | |
375 | c = self.load_default_context() |
|
379 | c = self.load_default_context() | |
376 | c.user = self.db_user |
|
380 | c.user = self.db_user | |
377 |
|
381 | |||
378 | _repos = c.user.repositories |
|
382 | _repos = c.user.repositories | |
379 | _repo_groups = c.user.repository_groups |
|
383 | _repo_groups = c.user.repository_groups | |
380 | _user_groups = c.user.user_groups |
|
384 | _user_groups = c.user.user_groups | |
381 | _pull_requests = c.user.user_pull_requests |
|
385 | _pull_requests = c.user.user_pull_requests | |
382 | _artifacts = c.user.artifacts |
|
386 | _artifacts = c.user.artifacts | |
383 |
|
387 | |||
384 | handle_repos = None |
|
388 | handle_repos = None | |
385 | handle_repo_groups = None |
|
389 | handle_repo_groups = None | |
386 | handle_user_groups = None |
|
390 | handle_user_groups = None | |
387 | handle_pull_requests = None |
|
391 | handle_pull_requests = None | |
388 | handle_artifacts = None |
|
392 | handle_artifacts = None | |
389 |
|
393 | |||
390 | # calls for flash of handle based on handle case detach or delete |
|
394 | # calls for flash of handle based on handle case detach or delete | |
391 | def set_handle_flash_repos(): |
|
395 | def set_handle_flash_repos(): | |
392 | handle = handle_repos |
|
396 | handle = handle_repos | |
393 | if handle == 'detach': |
|
397 | if handle == 'detach': | |
394 | h.flash(_('Detached %s repositories') % len(_repos), |
|
398 | h.flash(_('Detached %s repositories') % len(_repos), | |
395 | category='success') |
|
399 | category='success') | |
396 | elif handle == 'delete': |
|
400 | elif handle == 'delete': | |
397 | h.flash(_('Deleted %s repositories') % len(_repos), |
|
401 | h.flash(_('Deleted %s repositories') % len(_repos), | |
398 | category='success') |
|
402 | category='success') | |
399 |
|
403 | |||
400 | def set_handle_flash_repo_groups(): |
|
404 | def set_handle_flash_repo_groups(): | |
401 | handle = handle_repo_groups |
|
405 | handle = handle_repo_groups | |
402 | if handle == 'detach': |
|
406 | if handle == 'detach': | |
403 | h.flash(_('Detached %s repository groups') % len(_repo_groups), |
|
407 | h.flash(_('Detached %s repository groups') % len(_repo_groups), | |
404 | category='success') |
|
408 | category='success') | |
405 | elif handle == 'delete': |
|
409 | elif handle == 'delete': | |
406 | h.flash(_('Deleted %s repository groups') % len(_repo_groups), |
|
410 | h.flash(_('Deleted %s repository groups') % len(_repo_groups), | |
407 | category='success') |
|
411 | category='success') | |
408 |
|
412 | |||
409 | def set_handle_flash_user_groups(): |
|
413 | def set_handle_flash_user_groups(): | |
410 | handle = handle_user_groups |
|
414 | handle = handle_user_groups | |
411 | if handle == 'detach': |
|
415 | if handle == 'detach': | |
412 | h.flash(_('Detached %s user groups') % len(_user_groups), |
|
416 | h.flash(_('Detached %s user groups') % len(_user_groups), | |
413 | category='success') |
|
417 | category='success') | |
414 | elif handle == 'delete': |
|
418 | elif handle == 'delete': | |
415 | h.flash(_('Deleted %s user groups') % len(_user_groups), |
|
419 | h.flash(_('Deleted %s user groups') % len(_user_groups), | |
416 | category='success') |
|
420 | category='success') | |
417 |
|
421 | |||
418 | def set_handle_flash_pull_requests(): |
|
422 | def set_handle_flash_pull_requests(): | |
419 | handle = handle_pull_requests |
|
423 | handle = handle_pull_requests | |
420 | if handle == 'detach': |
|
424 | if handle == 'detach': | |
421 | h.flash(_('Detached %s pull requests') % len(_pull_requests), |
|
425 | h.flash(_('Detached %s pull requests') % len(_pull_requests), | |
422 | category='success') |
|
426 | category='success') | |
423 | elif handle == 'delete': |
|
427 | elif handle == 'delete': | |
424 | h.flash(_('Deleted %s pull requests') % len(_pull_requests), |
|
428 | h.flash(_('Deleted %s pull requests') % len(_pull_requests), | |
425 | category='success') |
|
429 | category='success') | |
426 |
|
430 | |||
427 | def set_handle_flash_artifacts(): |
|
431 | def set_handle_flash_artifacts(): | |
428 | handle = handle_artifacts |
|
432 | handle = handle_artifacts | |
429 | if handle == 'detach': |
|
433 | if handle == 'detach': | |
430 | h.flash(_('Detached %s artifacts') % len(_artifacts), |
|
434 | h.flash(_('Detached %s artifacts') % len(_artifacts), | |
431 | category='success') |
|
435 | category='success') | |
432 | elif handle == 'delete': |
|
436 | elif handle == 'delete': | |
433 | h.flash(_('Deleted %s artifacts') % len(_artifacts), |
|
437 | h.flash(_('Deleted %s artifacts') % len(_artifacts), | |
434 | category='success') |
|
438 | category='success') | |
435 |
|
439 | |||
436 | handle_user = User.get_first_super_admin() |
|
440 | handle_user = User.get_first_super_admin() | |
437 | handle_user_id = safe_int(self.request.POST.get('detach_user_id')) |
|
441 | handle_user_id = safe_int(self.request.POST.get('detach_user_id')) | |
438 | if handle_user_id: |
|
442 | if handle_user_id: | |
439 | # NOTE(marcink): we get new owner for objects... |
|
443 | # NOTE(marcink): we get new owner for objects... | |
440 | handle_user = User.get_or_404(handle_user_id) |
|
444 | handle_user = User.get_or_404(handle_user_id) | |
441 |
|
445 | |||
442 | if _repos and self.request.POST.get('user_repos'): |
|
446 | if _repos and self.request.POST.get('user_repos'): | |
443 | handle_repos = self.request.POST['user_repos'] |
|
447 | handle_repos = self.request.POST['user_repos'] | |
444 |
|
448 | |||
445 | if _repo_groups and self.request.POST.get('user_repo_groups'): |
|
449 | if _repo_groups and self.request.POST.get('user_repo_groups'): | |
446 | handle_repo_groups = self.request.POST['user_repo_groups'] |
|
450 | handle_repo_groups = self.request.POST['user_repo_groups'] | |
447 |
|
451 | |||
448 | if _user_groups and self.request.POST.get('user_user_groups'): |
|
452 | if _user_groups and self.request.POST.get('user_user_groups'): | |
449 | handle_user_groups = self.request.POST['user_user_groups'] |
|
453 | handle_user_groups = self.request.POST['user_user_groups'] | |
450 |
|
454 | |||
451 | if _pull_requests and self.request.POST.get('user_pull_requests'): |
|
455 | if _pull_requests and self.request.POST.get('user_pull_requests'): | |
452 | handle_pull_requests = self.request.POST['user_pull_requests'] |
|
456 | handle_pull_requests = self.request.POST['user_pull_requests'] | |
453 |
|
457 | |||
454 | if _artifacts and self.request.POST.get('user_artifacts'): |
|
458 | if _artifacts and self.request.POST.get('user_artifacts'): | |
455 | handle_artifacts = self.request.POST['user_artifacts'] |
|
459 | handle_artifacts = self.request.POST['user_artifacts'] | |
456 |
|
460 | |||
457 | old_values = c.user.get_api_data() |
|
461 | old_values = c.user.get_api_data() | |
458 |
|
462 | |||
459 | try: |
|
463 | try: | |
460 |
|
464 | |||
461 | UserModel().delete( |
|
465 | UserModel().delete( | |
462 | c.user, |
|
466 | c.user, | |
463 | handle_repos=handle_repos, |
|
467 | handle_repos=handle_repos, | |
464 | handle_repo_groups=handle_repo_groups, |
|
468 | handle_repo_groups=handle_repo_groups, | |
465 | handle_user_groups=handle_user_groups, |
|
469 | handle_user_groups=handle_user_groups, | |
466 | handle_pull_requests=handle_pull_requests, |
|
470 | handle_pull_requests=handle_pull_requests, | |
467 | handle_artifacts=handle_artifacts, |
|
471 | handle_artifacts=handle_artifacts, | |
468 | handle_new_owner=handle_user |
|
472 | handle_new_owner=handle_user | |
469 | ) |
|
473 | ) | |
470 |
|
474 | |||
471 | audit_logger.store_web( |
|
475 | audit_logger.store_web( | |
472 | 'user.delete', action_data={'old_data': old_values}, |
|
476 | 'user.delete', action_data={'old_data': old_values}, | |
473 | user=c.rhodecode_user) |
|
477 | user=c.rhodecode_user) | |
474 |
|
478 | |||
475 | Session().commit() |
|
479 | Session().commit() | |
476 | set_handle_flash_repos() |
|
480 | set_handle_flash_repos() | |
477 | set_handle_flash_repo_groups() |
|
481 | set_handle_flash_repo_groups() | |
478 | set_handle_flash_user_groups() |
|
482 | set_handle_flash_user_groups() | |
479 | set_handle_flash_pull_requests() |
|
483 | set_handle_flash_pull_requests() | |
480 | set_handle_flash_artifacts() |
|
484 | set_handle_flash_artifacts() | |
481 | username = h.escape(old_values['username']) |
|
485 | username = h.escape(old_values['username']) | |
482 | h.flash(_('Successfully deleted user `{}`').format(username), category='success') |
|
486 | h.flash(_('Successfully deleted user `{}`').format(username), category='success') | |
483 | except (UserOwnsReposException, UserOwnsRepoGroupsException, |
|
487 | except (UserOwnsReposException, UserOwnsRepoGroupsException, | |
484 | UserOwnsUserGroupsException, UserOwnsPullRequestsException, |
|
488 | UserOwnsUserGroupsException, UserOwnsPullRequestsException, | |
485 | UserOwnsArtifactsException, DefaultUserException) as e: |
|
489 | UserOwnsArtifactsException, DefaultUserException) as e: | |
486 | h.flash(e, category='warning') |
|
490 | h.flash(e, category='warning') | |
487 | except Exception: |
|
491 | except Exception: | |
488 | log.exception("Exception during deletion of user") |
|
492 | log.exception("Exception during deletion of user") | |
489 | h.flash(_('An error occurred during deletion of user'), |
|
493 | h.flash(_('An error occurred during deletion of user'), | |
490 | category='error') |
|
494 | category='error') | |
491 | raise HTTPFound(h.route_path('users')) |
|
495 | raise HTTPFound(h.route_path('users')) | |
492 |
|
496 | |||
493 | @LoginRequired() |
|
497 | @LoginRequired() | |
494 | @HasPermissionAllDecorator('hg.admin') |
|
498 | @HasPermissionAllDecorator('hg.admin') | |
495 | @view_config( |
|
499 | @view_config( | |
496 | route_name='user_edit', request_method='GET', |
|
500 | route_name='user_edit', request_method='GET', | |
497 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
501 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
498 | def user_edit(self): |
|
502 | def user_edit(self): | |
499 | _ = self.request.translate |
|
503 | _ = self.request.translate | |
500 | c = self.load_default_context() |
|
504 | c = self.load_default_context() | |
501 | c.user = self.db_user |
|
505 | c.user = self.db_user | |
502 |
|
506 | |||
503 | c.active = 'profile' |
|
507 | c.active = 'profile' | |
504 | c.extern_type = c.user.extern_type |
|
508 | c.extern_type = c.user.extern_type | |
505 | c.extern_name = c.user.extern_name |
|
509 | c.extern_name = c.user.extern_name | |
506 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) |
|
510 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) | |
507 |
|
511 | |||
508 | defaults = c.user.get_dict() |
|
512 | defaults = c.user.get_dict() | |
509 | defaults.update({'language': c.user.user_data.get('language')}) |
|
513 | defaults.update({'language': c.user.user_data.get('language')}) | |
510 |
|
514 | |||
511 | data = render( |
|
515 | data = render( | |
512 | 'rhodecode:templates/admin/users/user_edit.mako', |
|
516 | 'rhodecode:templates/admin/users/user_edit.mako', | |
513 | self._get_template_context(c), self.request) |
|
517 | self._get_template_context(c), self.request) | |
514 | html = formencode.htmlfill.render( |
|
518 | html = formencode.htmlfill.render( | |
515 | data, |
|
519 | data, | |
516 | defaults=defaults, |
|
520 | defaults=defaults, | |
517 | encoding="UTF-8", |
|
521 | encoding="UTF-8", | |
518 | force_defaults=False |
|
522 | force_defaults=False | |
519 | ) |
|
523 | ) | |
520 | return Response(html) |
|
524 | return Response(html) | |
521 |
|
525 | |||
522 | @LoginRequired() |
|
526 | @LoginRequired() | |
523 | @HasPermissionAllDecorator('hg.admin') |
|
527 | @HasPermissionAllDecorator('hg.admin') | |
524 | @view_config( |
|
528 | @view_config( | |
525 | route_name='user_edit_advanced', request_method='GET', |
|
529 | route_name='user_edit_advanced', request_method='GET', | |
526 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
530 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
527 | def user_edit_advanced(self): |
|
531 | def user_edit_advanced(self): | |
528 | _ = self.request.translate |
|
532 | _ = self.request.translate | |
529 | c = self.load_default_context() |
|
533 | c = self.load_default_context() | |
530 |
|
534 | |||
531 | user_id = self.db_user_id |
|
535 | user_id = self.db_user_id | |
532 | c.user = self.db_user |
|
536 | c.user = self.db_user | |
533 |
|
537 | |||
534 | c.detach_user = User.get_first_super_admin() |
|
538 | c.detach_user = User.get_first_super_admin() | |
535 | detach_user_id = safe_int(self.request.GET.get('detach_user_id')) |
|
539 | detach_user_id = safe_int(self.request.GET.get('detach_user_id')) | |
536 | if detach_user_id: |
|
540 | if detach_user_id: | |
537 | c.detach_user = User.get_or_404(detach_user_id) |
|
541 | c.detach_user = User.get_or_404(detach_user_id) | |
538 |
|
542 | |||
539 | c.active = 'advanced' |
|
543 | c.active = 'advanced' | |
540 | c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id) |
|
544 | c.personal_repo_group = RepoGroup.get_user_personal_repo_group(user_id) | |
541 | c.personal_repo_group_name = RepoGroupModel()\ |
|
545 | c.personal_repo_group_name = RepoGroupModel()\ | |
542 | .get_personal_group_name(c.user) |
|
546 | .get_personal_group_name(c.user) | |
543 |
|
547 | |||
544 | c.user_to_review_rules = sorted( |
|
548 | c.user_to_review_rules = sorted( | |
545 | (x.user for x in c.user.user_review_rules), |
|
549 | (x.user for x in c.user.user_review_rules), | |
546 | key=lambda u: u.username.lower()) |
|
550 | key=lambda u: u.username.lower()) | |
547 |
|
551 | |||
548 | defaults = c.user.get_dict() |
|
552 | defaults = c.user.get_dict() | |
549 |
|
553 | |||
550 | # Interim workaround if the user participated on any pull requests as a |
|
554 | # Interim workaround if the user participated on any pull requests as a | |
551 | # reviewer. |
|
555 | # reviewer. | |
552 | has_review = len(c.user.reviewer_pull_requests) |
|
556 | has_review = len(c.user.reviewer_pull_requests) | |
553 | c.can_delete_user = not has_review |
|
557 | c.can_delete_user = not has_review | |
554 | c.can_delete_user_message = '' |
|
558 | c.can_delete_user_message = '' | |
555 | inactive_link = h.link_to( |
|
559 | inactive_link = h.link_to( | |
556 | 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active')) |
|
560 | 'inactive', h.route_path('user_edit', user_id=user_id, _anchor='active')) | |
557 | if has_review == 1: |
|
561 | if has_review == 1: | |
558 | c.can_delete_user_message = h.literal(_( |
|
562 | c.can_delete_user_message = h.literal(_( | |
559 | 'The user participates as reviewer in {} pull request and ' |
|
563 | 'The user participates as reviewer in {} pull request and ' | |
560 | 'cannot be deleted. \nYou can set the user to ' |
|
564 | 'cannot be deleted. \nYou can set the user to ' | |
561 | '"{}" instead of deleting it.').format( |
|
565 | '"{}" instead of deleting it.').format( | |
562 | has_review, inactive_link)) |
|
566 | has_review, inactive_link)) | |
563 | elif has_review: |
|
567 | elif has_review: | |
564 | c.can_delete_user_message = h.literal(_( |
|
568 | c.can_delete_user_message = h.literal(_( | |
565 | 'The user participates as reviewer in {} pull requests and ' |
|
569 | 'The user participates as reviewer in {} pull requests and ' | |
566 | 'cannot be deleted. \nYou can set the user to ' |
|
570 | 'cannot be deleted. \nYou can set the user to ' | |
567 | '"{}" instead of deleting it.').format( |
|
571 | '"{}" instead of deleting it.').format( | |
568 | has_review, inactive_link)) |
|
572 | has_review, inactive_link)) | |
569 |
|
573 | |||
570 | data = render( |
|
574 | data = render( | |
571 | 'rhodecode:templates/admin/users/user_edit.mako', |
|
575 | 'rhodecode:templates/admin/users/user_edit.mako', | |
572 | self._get_template_context(c), self.request) |
|
576 | self._get_template_context(c), self.request) | |
573 | html = formencode.htmlfill.render( |
|
577 | html = formencode.htmlfill.render( | |
574 | data, |
|
578 | data, | |
575 | defaults=defaults, |
|
579 | defaults=defaults, | |
576 | encoding="UTF-8", |
|
580 | encoding="UTF-8", | |
577 | force_defaults=False |
|
581 | force_defaults=False | |
578 | ) |
|
582 | ) | |
579 | return Response(html) |
|
583 | return Response(html) | |
580 |
|
584 | |||
581 | @LoginRequired() |
|
585 | @LoginRequired() | |
582 | @HasPermissionAllDecorator('hg.admin') |
|
586 | @HasPermissionAllDecorator('hg.admin') | |
583 | @view_config( |
|
587 | @view_config( | |
584 | route_name='user_edit_global_perms', request_method='GET', |
|
588 | route_name='user_edit_global_perms', request_method='GET', | |
585 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
589 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
586 | def user_edit_global_perms(self): |
|
590 | def user_edit_global_perms(self): | |
587 | _ = self.request.translate |
|
591 | _ = self.request.translate | |
588 | c = self.load_default_context() |
|
592 | c = self.load_default_context() | |
589 | c.user = self.db_user |
|
593 | c.user = self.db_user | |
590 |
|
594 | |||
591 | c.active = 'global_perms' |
|
595 | c.active = 'global_perms' | |
592 |
|
596 | |||
593 | c.default_user = User.get_default_user() |
|
597 | c.default_user = User.get_default_user() | |
594 | defaults = c.user.get_dict() |
|
598 | defaults = c.user.get_dict() | |
595 | defaults.update(c.default_user.get_default_perms(suffix='_inherited')) |
|
599 | defaults.update(c.default_user.get_default_perms(suffix='_inherited')) | |
596 | defaults.update(c.default_user.get_default_perms()) |
|
600 | defaults.update(c.default_user.get_default_perms()) | |
597 | defaults.update(c.user.get_default_perms()) |
|
601 | defaults.update(c.user.get_default_perms()) | |
598 |
|
602 | |||
599 | data = render( |
|
603 | data = render( | |
600 | 'rhodecode:templates/admin/users/user_edit.mako', |
|
604 | 'rhodecode:templates/admin/users/user_edit.mako', | |
601 | self._get_template_context(c), self.request) |
|
605 | self._get_template_context(c), self.request) | |
602 | html = formencode.htmlfill.render( |
|
606 | html = formencode.htmlfill.render( | |
603 | data, |
|
607 | data, | |
604 | defaults=defaults, |
|
608 | defaults=defaults, | |
605 | encoding="UTF-8", |
|
609 | encoding="UTF-8", | |
606 | force_defaults=False |
|
610 | force_defaults=False | |
607 | ) |
|
611 | ) | |
608 | return Response(html) |
|
612 | return Response(html) | |
609 |
|
613 | |||
610 | @LoginRequired() |
|
614 | @LoginRequired() | |
611 | @HasPermissionAllDecorator('hg.admin') |
|
615 | @HasPermissionAllDecorator('hg.admin') | |
612 | @CSRFRequired() |
|
616 | @CSRFRequired() | |
613 | @view_config( |
|
617 | @view_config( | |
614 | route_name='user_edit_global_perms_update', request_method='POST', |
|
618 | route_name='user_edit_global_perms_update', request_method='POST', | |
615 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
619 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
616 | def user_edit_global_perms_update(self): |
|
620 | def user_edit_global_perms_update(self): | |
617 | _ = self.request.translate |
|
621 | _ = self.request.translate | |
618 | c = self.load_default_context() |
|
622 | c = self.load_default_context() | |
619 |
|
623 | |||
620 | user_id = self.db_user_id |
|
624 | user_id = self.db_user_id | |
621 | c.user = self.db_user |
|
625 | c.user = self.db_user | |
622 |
|
626 | |||
623 | c.active = 'global_perms' |
|
627 | c.active = 'global_perms' | |
624 | try: |
|
628 | try: | |
625 | # first stage that verifies the checkbox |
|
629 | # first stage that verifies the checkbox | |
626 | _form = UserIndividualPermissionsForm(self.request.translate) |
|
630 | _form = UserIndividualPermissionsForm(self.request.translate) | |
627 | form_result = _form.to_python(dict(self.request.POST)) |
|
631 | form_result = _form.to_python(dict(self.request.POST)) | |
628 | inherit_perms = form_result['inherit_default_permissions'] |
|
632 | inherit_perms = form_result['inherit_default_permissions'] | |
629 | c.user.inherit_default_permissions = inherit_perms |
|
633 | c.user.inherit_default_permissions = inherit_perms | |
630 | Session().add(c.user) |
|
634 | Session().add(c.user) | |
631 |
|
635 | |||
632 | if not inherit_perms: |
|
636 | if not inherit_perms: | |
633 | # only update the individual ones if we un check the flag |
|
637 | # only update the individual ones if we un check the flag | |
634 | _form = UserPermissionsForm( |
|
638 | _form = UserPermissionsForm( | |
635 | self.request.translate, |
|
639 | self.request.translate, | |
636 | [x[0] for x in c.repo_create_choices], |
|
640 | [x[0] for x in c.repo_create_choices], | |
637 | [x[0] for x in c.repo_create_on_write_choices], |
|
641 | [x[0] for x in c.repo_create_on_write_choices], | |
638 | [x[0] for x in c.repo_group_create_choices], |
|
642 | [x[0] for x in c.repo_group_create_choices], | |
639 | [x[0] for x in c.user_group_create_choices], |
|
643 | [x[0] for x in c.user_group_create_choices], | |
640 | [x[0] for x in c.fork_choices], |
|
644 | [x[0] for x in c.fork_choices], | |
641 | [x[0] for x in c.inherit_default_permission_choices])() |
|
645 | [x[0] for x in c.inherit_default_permission_choices])() | |
642 |
|
646 | |||
643 | form_result = _form.to_python(dict(self.request.POST)) |
|
647 | form_result = _form.to_python(dict(self.request.POST)) | |
644 | form_result.update({'perm_user_id': c.user.user_id}) |
|
648 | form_result.update({'perm_user_id': c.user.user_id}) | |
645 |
|
649 | |||
646 | PermissionModel().update_user_permissions(form_result) |
|
650 | PermissionModel().update_user_permissions(form_result) | |
647 |
|
651 | |||
648 | # TODO(marcink): implement global permissions |
|
652 | # TODO(marcink): implement global permissions | |
649 | # audit_log.store_web('user.edit.permissions') |
|
653 | # audit_log.store_web('user.edit.permissions') | |
650 |
|
654 | |||
651 | Session().commit() |
|
655 | Session().commit() | |
652 |
|
656 | |||
653 | h.flash(_('User global permissions updated successfully'), |
|
657 | h.flash(_('User global permissions updated successfully'), | |
654 | category='success') |
|
658 | category='success') | |
655 |
|
659 | |||
656 | except formencode.Invalid as errors: |
|
660 | except formencode.Invalid as errors: | |
657 | data = render( |
|
661 | data = render( | |
658 | 'rhodecode:templates/admin/users/user_edit.mako', |
|
662 | 'rhodecode:templates/admin/users/user_edit.mako', | |
659 | self._get_template_context(c), self.request) |
|
663 | self._get_template_context(c), self.request) | |
660 | html = formencode.htmlfill.render( |
|
664 | html = formencode.htmlfill.render( | |
661 | data, |
|
665 | data, | |
662 | defaults=errors.value, |
|
666 | defaults=errors.value, | |
663 | errors=errors.error_dict or {}, |
|
667 | errors=errors.error_dict or {}, | |
664 | prefix_error=False, |
|
668 | prefix_error=False, | |
665 | encoding="UTF-8", |
|
669 | encoding="UTF-8", | |
666 | force_defaults=False |
|
670 | force_defaults=False | |
667 | ) |
|
671 | ) | |
668 | return Response(html) |
|
672 | return Response(html) | |
669 | except Exception: |
|
673 | except Exception: | |
670 | log.exception("Exception during permissions saving") |
|
674 | log.exception("Exception during permissions saving") | |
671 | h.flash(_('An error occurred during permissions saving'), |
|
675 | h.flash(_('An error occurred during permissions saving'), | |
672 | category='error') |
|
676 | category='error') | |
673 |
|
677 | |||
674 | affected_user_ids = [user_id] |
|
678 | affected_user_ids = [user_id] | |
675 | PermissionModel().trigger_permission_flush(affected_user_ids) |
|
679 | PermissionModel().trigger_permission_flush(affected_user_ids) | |
676 | raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id)) |
|
680 | raise HTTPFound(h.route_path('user_edit_global_perms', user_id=user_id)) | |
677 |
|
681 | |||
678 | @LoginRequired() |
|
682 | @LoginRequired() | |
679 | @HasPermissionAllDecorator('hg.admin') |
|
683 | @HasPermissionAllDecorator('hg.admin') | |
680 | @CSRFRequired() |
|
684 | @CSRFRequired() | |
681 | @view_config( |
|
685 | @view_config( | |
682 | route_name='user_enable_force_password_reset', request_method='POST', |
|
686 | route_name='user_enable_force_password_reset', request_method='POST', | |
683 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
687 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
684 | def user_enable_force_password_reset(self): |
|
688 | def user_enable_force_password_reset(self): | |
685 | _ = self.request.translate |
|
689 | _ = self.request.translate | |
686 | c = self.load_default_context() |
|
690 | c = self.load_default_context() | |
687 |
|
691 | |||
688 | user_id = self.db_user_id |
|
692 | user_id = self.db_user_id | |
689 | c.user = self.db_user |
|
693 | c.user = self.db_user | |
690 |
|
694 | |||
691 | try: |
|
695 | try: | |
692 | c.user.update_userdata(force_password_change=True) |
|
696 | c.user.update_userdata(force_password_change=True) | |
693 |
|
697 | |||
694 | msg = _('Force password change enabled for user') |
|
698 | msg = _('Force password change enabled for user') | |
695 | audit_logger.store_web('user.edit.password_reset.enabled', |
|
699 | audit_logger.store_web('user.edit.password_reset.enabled', | |
696 | user=c.rhodecode_user) |
|
700 | user=c.rhodecode_user) | |
697 |
|
701 | |||
698 | Session().commit() |
|
702 | Session().commit() | |
699 | h.flash(msg, category='success') |
|
703 | h.flash(msg, category='success') | |
700 | except Exception: |
|
704 | except Exception: | |
701 | log.exception("Exception during password reset for user") |
|
705 | log.exception("Exception during password reset for user") | |
702 | h.flash(_('An error occurred during password reset for user'), |
|
706 | h.flash(_('An error occurred during password reset for user'), | |
703 | category='error') |
|
707 | category='error') | |
704 |
|
708 | |||
705 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) |
|
709 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) | |
706 |
|
710 | |||
707 | @LoginRequired() |
|
711 | @LoginRequired() | |
708 | @HasPermissionAllDecorator('hg.admin') |
|
712 | @HasPermissionAllDecorator('hg.admin') | |
709 | @CSRFRequired() |
|
713 | @CSRFRequired() | |
710 | @view_config( |
|
714 | @view_config( | |
711 | route_name='user_disable_force_password_reset', request_method='POST', |
|
715 | route_name='user_disable_force_password_reset', request_method='POST', | |
712 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
716 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
713 | def user_disable_force_password_reset(self): |
|
717 | def user_disable_force_password_reset(self): | |
714 | _ = self.request.translate |
|
718 | _ = self.request.translate | |
715 | c = self.load_default_context() |
|
719 | c = self.load_default_context() | |
716 |
|
720 | |||
717 | user_id = self.db_user_id |
|
721 | user_id = self.db_user_id | |
718 | c.user = self.db_user |
|
722 | c.user = self.db_user | |
719 |
|
723 | |||
720 | try: |
|
724 | try: | |
721 | c.user.update_userdata(force_password_change=False) |
|
725 | c.user.update_userdata(force_password_change=False) | |
722 |
|
726 | |||
723 | msg = _('Force password change disabled for user') |
|
727 | msg = _('Force password change disabled for user') | |
724 | audit_logger.store_web( |
|
728 | audit_logger.store_web( | |
725 | 'user.edit.password_reset.disabled', |
|
729 | 'user.edit.password_reset.disabled', | |
726 | user=c.rhodecode_user) |
|
730 | user=c.rhodecode_user) | |
727 |
|
731 | |||
728 | Session().commit() |
|
732 | Session().commit() | |
729 | h.flash(msg, category='success') |
|
733 | h.flash(msg, category='success') | |
730 | except Exception: |
|
734 | except Exception: | |
731 | log.exception("Exception during password reset for user") |
|
735 | log.exception("Exception during password reset for user") | |
732 | h.flash(_('An error occurred during password reset for user'), |
|
736 | h.flash(_('An error occurred during password reset for user'), | |
733 | category='error') |
|
737 | category='error') | |
734 |
|
738 | |||
735 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) |
|
739 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) | |
736 |
|
740 | |||
737 | @LoginRequired() |
|
741 | @LoginRequired() | |
738 | @HasPermissionAllDecorator('hg.admin') |
|
742 | @HasPermissionAllDecorator('hg.admin') | |
739 | @CSRFRequired() |
|
743 | @CSRFRequired() | |
740 | @view_config( |
|
744 | @view_config( | |
741 | route_name='user_notice_dismiss', request_method='POST', |
|
745 | route_name='user_notice_dismiss', request_method='POST', | |
742 | renderer='json_ext', xhr=True) |
|
746 | renderer='json_ext', xhr=True) | |
743 | def user_notice_dismiss(self): |
|
747 | def user_notice_dismiss(self): | |
744 | _ = self.request.translate |
|
748 | _ = self.request.translate | |
745 | c = self.load_default_context() |
|
749 | c = self.load_default_context() | |
746 |
|
750 | |||
747 | user_id = self.db_user_id |
|
751 | user_id = self.db_user_id | |
748 | c.user = self.db_user |
|
752 | c.user = self.db_user | |
749 | user_notice_id = safe_int(self.request.POST.get('notice_id')) |
|
753 | user_notice_id = safe_int(self.request.POST.get('notice_id')) | |
750 | notice = UserNotice().query()\ |
|
754 | notice = UserNotice().query()\ | |
751 | .filter(UserNotice.user_id == user_id)\ |
|
755 | .filter(UserNotice.user_id == user_id)\ | |
752 | .filter(UserNotice.user_notice_id == user_notice_id)\ |
|
756 | .filter(UserNotice.user_notice_id == user_notice_id)\ | |
753 | .scalar() |
|
757 | .scalar() | |
754 | read = False |
|
758 | read = False | |
755 | if notice: |
|
759 | if notice: | |
756 | notice.notice_read = True |
|
760 | notice.notice_read = True | |
757 | Session().add(notice) |
|
761 | Session().add(notice) | |
758 | Session().commit() |
|
762 | Session().commit() | |
759 | read = True |
|
763 | read = True | |
760 |
|
764 | |||
761 | return {'notice': user_notice_id, 'read': read} |
|
765 | return {'notice': user_notice_id, 'read': read} | |
762 |
|
766 | |||
763 | @LoginRequired() |
|
767 | @LoginRequired() | |
764 | @HasPermissionAllDecorator('hg.admin') |
|
768 | @HasPermissionAllDecorator('hg.admin') | |
765 | @CSRFRequired() |
|
769 | @CSRFRequired() | |
766 | @view_config( |
|
770 | @view_config( | |
767 | route_name='user_create_personal_repo_group', request_method='POST', |
|
771 | route_name='user_create_personal_repo_group', request_method='POST', | |
768 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
772 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
769 | def user_create_personal_repo_group(self): |
|
773 | def user_create_personal_repo_group(self): | |
770 | """ |
|
774 | """ | |
771 | Create personal repository group for this user |
|
775 | Create personal repository group for this user | |
772 | """ |
|
776 | """ | |
773 | from rhodecode.model.repo_group import RepoGroupModel |
|
777 | from rhodecode.model.repo_group import RepoGroupModel | |
774 |
|
778 | |||
775 | _ = self.request.translate |
|
779 | _ = self.request.translate | |
776 | c = self.load_default_context() |
|
780 | c = self.load_default_context() | |
777 |
|
781 | |||
778 | user_id = self.db_user_id |
|
782 | user_id = self.db_user_id | |
779 | c.user = self.db_user |
|
783 | c.user = self.db_user | |
780 |
|
784 | |||
781 | personal_repo_group = RepoGroup.get_user_personal_repo_group( |
|
785 | personal_repo_group = RepoGroup.get_user_personal_repo_group( | |
782 | c.user.user_id) |
|
786 | c.user.user_id) | |
783 | if personal_repo_group: |
|
787 | if personal_repo_group: | |
784 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) |
|
788 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) | |
785 |
|
789 | |||
786 | personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user) |
|
790 | personal_repo_group_name = RepoGroupModel().get_personal_group_name(c.user) | |
787 | named_personal_group = RepoGroup.get_by_group_name( |
|
791 | named_personal_group = RepoGroup.get_by_group_name( | |
788 | personal_repo_group_name) |
|
792 | personal_repo_group_name) | |
789 | try: |
|
793 | try: | |
790 |
|
794 | |||
791 | if named_personal_group and named_personal_group.user_id == c.user.user_id: |
|
795 | if named_personal_group and named_personal_group.user_id == c.user.user_id: | |
792 | # migrate the same named group, and mark it as personal |
|
796 | # migrate the same named group, and mark it as personal | |
793 | named_personal_group.personal = True |
|
797 | named_personal_group.personal = True | |
794 | Session().add(named_personal_group) |
|
798 | Session().add(named_personal_group) | |
795 | Session().commit() |
|
799 | Session().commit() | |
796 | msg = _('Linked repository group `%s` as personal' % ( |
|
800 | msg = _('Linked repository group `%s` as personal' % ( | |
797 | personal_repo_group_name,)) |
|
801 | personal_repo_group_name,)) | |
798 | h.flash(msg, category='success') |
|
802 | h.flash(msg, category='success') | |
799 | elif not named_personal_group: |
|
803 | elif not named_personal_group: | |
800 | RepoGroupModel().create_personal_repo_group(c.user) |
|
804 | RepoGroupModel().create_personal_repo_group(c.user) | |
801 |
|
805 | |||
802 | msg = _('Created repository group `%s`' % ( |
|
806 | msg = _('Created repository group `%s`' % ( | |
803 | personal_repo_group_name,)) |
|
807 | personal_repo_group_name,)) | |
804 | h.flash(msg, category='success') |
|
808 | h.flash(msg, category='success') | |
805 | else: |
|
809 | else: | |
806 | msg = _('Repository group `%s` is already taken' % ( |
|
810 | msg = _('Repository group `%s` is already taken' % ( | |
807 | personal_repo_group_name,)) |
|
811 | personal_repo_group_name,)) | |
808 | h.flash(msg, category='warning') |
|
812 | h.flash(msg, category='warning') | |
809 | except Exception: |
|
813 | except Exception: | |
810 | log.exception("Exception during repository group creation") |
|
814 | log.exception("Exception during repository group creation") | |
811 | msg = _( |
|
815 | msg = _( | |
812 | 'An error occurred during repository group creation for user') |
|
816 | 'An error occurred during repository group creation for user') | |
813 | h.flash(msg, category='error') |
|
817 | h.flash(msg, category='error') | |
814 | Session().rollback() |
|
818 | Session().rollback() | |
815 |
|
819 | |||
816 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) |
|
820 | raise HTTPFound(h.route_path('user_edit_advanced', user_id=user_id)) | |
817 |
|
821 | |||
818 | @LoginRequired() |
|
822 | @LoginRequired() | |
819 | @HasPermissionAllDecorator('hg.admin') |
|
823 | @HasPermissionAllDecorator('hg.admin') | |
820 | @view_config( |
|
824 | @view_config( | |
821 | route_name='edit_user_auth_tokens', request_method='GET', |
|
825 | route_name='edit_user_auth_tokens', request_method='GET', | |
822 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
826 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
823 | def auth_tokens(self): |
|
827 | def auth_tokens(self): | |
824 | _ = self.request.translate |
|
828 | _ = self.request.translate | |
825 | c = self.load_default_context() |
|
829 | c = self.load_default_context() | |
826 | c.user = self.db_user |
|
830 | c.user = self.db_user | |
827 |
|
831 | |||
828 | c.active = 'auth_tokens' |
|
832 | c.active = 'auth_tokens' | |
829 |
|
833 | |||
830 | c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_) |
|
834 | c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_) | |
831 | c.role_values = [ |
|
835 | c.role_values = [ | |
832 | (x, AuthTokenModel.cls._get_role_name(x)) |
|
836 | (x, AuthTokenModel.cls._get_role_name(x)) | |
833 | for x in AuthTokenModel.cls.ROLES] |
|
837 | for x in AuthTokenModel.cls.ROLES] | |
834 | c.role_options = [(c.role_values, _("Role"))] |
|
838 | c.role_options = [(c.role_values, _("Role"))] | |
835 | c.user_auth_tokens = AuthTokenModel().get_auth_tokens( |
|
839 | c.user_auth_tokens = AuthTokenModel().get_auth_tokens( | |
836 | c.user.user_id, show_expired=True) |
|
840 | c.user.user_id, show_expired=True) | |
837 | c.role_vcs = AuthTokenModel.cls.ROLE_VCS |
|
841 | c.role_vcs = AuthTokenModel.cls.ROLE_VCS | |
838 | return self._get_template_context(c) |
|
842 | return self._get_template_context(c) | |
839 |
|
843 | |||
840 | @LoginRequired() |
|
844 | @LoginRequired() | |
841 | @HasPermissionAllDecorator('hg.admin') |
|
845 | @HasPermissionAllDecorator('hg.admin') | |
842 | @view_config( |
|
846 | @view_config( | |
843 | route_name='edit_user_auth_tokens_view', request_method='POST', |
|
847 | route_name='edit_user_auth_tokens_view', request_method='POST', | |
844 | renderer='json_ext', xhr=True) |
|
848 | renderer='json_ext', xhr=True) | |
845 | def auth_tokens_view(self): |
|
849 | def auth_tokens_view(self): | |
846 | _ = self.request.translate |
|
850 | _ = self.request.translate | |
847 | c = self.load_default_context() |
|
851 | c = self.load_default_context() | |
848 | c.user = self.db_user |
|
852 | c.user = self.db_user | |
849 |
|
853 | |||
850 | auth_token_id = self.request.POST.get('auth_token_id') |
|
854 | auth_token_id = self.request.POST.get('auth_token_id') | |
851 |
|
855 | |||
852 | if auth_token_id: |
|
856 | if auth_token_id: | |
853 | token = UserApiKeys.get_or_404(auth_token_id) |
|
857 | token = UserApiKeys.get_or_404(auth_token_id) | |
854 |
|
858 | |||
855 | return { |
|
859 | return { | |
856 | 'auth_token': token.api_key |
|
860 | 'auth_token': token.api_key | |
857 | } |
|
861 | } | |
858 |
|
862 | |||
859 | def maybe_attach_token_scope(self, token): |
|
863 | def maybe_attach_token_scope(self, token): | |
860 | # implemented in EE edition |
|
864 | # implemented in EE edition | |
861 | pass |
|
865 | pass | |
862 |
|
866 | |||
863 | @LoginRequired() |
|
867 | @LoginRequired() | |
864 | @HasPermissionAllDecorator('hg.admin') |
|
868 | @HasPermissionAllDecorator('hg.admin') | |
865 | @CSRFRequired() |
|
869 | @CSRFRequired() | |
866 | @view_config( |
|
870 | @view_config( | |
867 | route_name='edit_user_auth_tokens_add', request_method='POST') |
|
871 | route_name='edit_user_auth_tokens_add', request_method='POST') | |
868 | def auth_tokens_add(self): |
|
872 | def auth_tokens_add(self): | |
869 | _ = self.request.translate |
|
873 | _ = self.request.translate | |
870 | c = self.load_default_context() |
|
874 | c = self.load_default_context() | |
871 |
|
875 | |||
872 | user_id = self.db_user_id |
|
876 | user_id = self.db_user_id | |
873 | c.user = self.db_user |
|
877 | c.user = self.db_user | |
874 |
|
878 | |||
875 | user_data = c.user.get_api_data() |
|
879 | user_data = c.user.get_api_data() | |
876 | lifetime = safe_int(self.request.POST.get('lifetime'), -1) |
|
880 | lifetime = safe_int(self.request.POST.get('lifetime'), -1) | |
877 | description = self.request.POST.get('description') |
|
881 | description = self.request.POST.get('description') | |
878 | role = self.request.POST.get('role') |
|
882 | role = self.request.POST.get('role') | |
879 |
|
883 | |||
880 | token = UserModel().add_auth_token( |
|
884 | token = UserModel().add_auth_token( | |
881 | user=c.user.user_id, |
|
885 | user=c.user.user_id, | |
882 | lifetime_minutes=lifetime, role=role, description=description, |
|
886 | lifetime_minutes=lifetime, role=role, description=description, | |
883 | scope_callback=self.maybe_attach_token_scope) |
|
887 | scope_callback=self.maybe_attach_token_scope) | |
884 | token_data = token.get_api_data() |
|
888 | token_data = token.get_api_data() | |
885 |
|
889 | |||
886 | audit_logger.store_web( |
|
890 | audit_logger.store_web( | |
887 | 'user.edit.token.add', action_data={ |
|
891 | 'user.edit.token.add', action_data={ | |
888 | 'data': {'token': token_data, 'user': user_data}}, |
|
892 | 'data': {'token': token_data, 'user': user_data}}, | |
889 | user=self._rhodecode_user, ) |
|
893 | user=self._rhodecode_user, ) | |
890 | Session().commit() |
|
894 | Session().commit() | |
891 |
|
895 | |||
892 | h.flash(_("Auth token successfully created"), category='success') |
|
896 | h.flash(_("Auth token successfully created"), category='success') | |
893 | return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) |
|
897 | return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) | |
894 |
|
898 | |||
895 | @LoginRequired() |
|
899 | @LoginRequired() | |
896 | @HasPermissionAllDecorator('hg.admin') |
|
900 | @HasPermissionAllDecorator('hg.admin') | |
897 | @CSRFRequired() |
|
901 | @CSRFRequired() | |
898 | @view_config( |
|
902 | @view_config( | |
899 | route_name='edit_user_auth_tokens_delete', request_method='POST') |
|
903 | route_name='edit_user_auth_tokens_delete', request_method='POST') | |
900 | def auth_tokens_delete(self): |
|
904 | def auth_tokens_delete(self): | |
901 | _ = self.request.translate |
|
905 | _ = self.request.translate | |
902 | c = self.load_default_context() |
|
906 | c = self.load_default_context() | |
903 |
|
907 | |||
904 | user_id = self.db_user_id |
|
908 | user_id = self.db_user_id | |
905 | c.user = self.db_user |
|
909 | c.user = self.db_user | |
906 |
|
910 | |||
907 | user_data = c.user.get_api_data() |
|
911 | user_data = c.user.get_api_data() | |
908 |
|
912 | |||
909 | del_auth_token = self.request.POST.get('del_auth_token') |
|
913 | del_auth_token = self.request.POST.get('del_auth_token') | |
910 |
|
914 | |||
911 | if del_auth_token: |
|
915 | if del_auth_token: | |
912 | token = UserApiKeys.get_or_404(del_auth_token) |
|
916 | token = UserApiKeys.get_or_404(del_auth_token) | |
913 | token_data = token.get_api_data() |
|
917 | token_data = token.get_api_data() | |
914 |
|
918 | |||
915 | AuthTokenModel().delete(del_auth_token, c.user.user_id) |
|
919 | AuthTokenModel().delete(del_auth_token, c.user.user_id) | |
916 | audit_logger.store_web( |
|
920 | audit_logger.store_web( | |
917 | 'user.edit.token.delete', action_data={ |
|
921 | 'user.edit.token.delete', action_data={ | |
918 | 'data': {'token': token_data, 'user': user_data}}, |
|
922 | 'data': {'token': token_data, 'user': user_data}}, | |
919 | user=self._rhodecode_user,) |
|
923 | user=self._rhodecode_user,) | |
920 | Session().commit() |
|
924 | Session().commit() | |
921 | h.flash(_("Auth token successfully deleted"), category='success') |
|
925 | h.flash(_("Auth token successfully deleted"), category='success') | |
922 |
|
926 | |||
923 | return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) |
|
927 | return HTTPFound(h.route_path('edit_user_auth_tokens', user_id=user_id)) | |
924 |
|
928 | |||
925 | @LoginRequired() |
|
929 | @LoginRequired() | |
926 | @HasPermissionAllDecorator('hg.admin') |
|
930 | @HasPermissionAllDecorator('hg.admin') | |
927 | @view_config( |
|
931 | @view_config( | |
928 | route_name='edit_user_ssh_keys', request_method='GET', |
|
932 | route_name='edit_user_ssh_keys', request_method='GET', | |
929 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
933 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
930 | def ssh_keys(self): |
|
934 | def ssh_keys(self): | |
931 | _ = self.request.translate |
|
935 | _ = self.request.translate | |
932 | c = self.load_default_context() |
|
936 | c = self.load_default_context() | |
933 | c.user = self.db_user |
|
937 | c.user = self.db_user | |
934 |
|
938 | |||
935 | c.active = 'ssh_keys' |
|
939 | c.active = 'ssh_keys' | |
936 | c.default_key = self.request.GET.get('default_key') |
|
940 | c.default_key = self.request.GET.get('default_key') | |
937 | c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id) |
|
941 | c.user_ssh_keys = SshKeyModel().get_ssh_keys(c.user.user_id) | |
938 | return self._get_template_context(c) |
|
942 | return self._get_template_context(c) | |
939 |
|
943 | |||
940 | @LoginRequired() |
|
944 | @LoginRequired() | |
941 | @HasPermissionAllDecorator('hg.admin') |
|
945 | @HasPermissionAllDecorator('hg.admin') | |
942 | @view_config( |
|
946 | @view_config( | |
943 | route_name='edit_user_ssh_keys_generate_keypair', request_method='GET', |
|
947 | route_name='edit_user_ssh_keys_generate_keypair', request_method='GET', | |
944 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
948 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
945 | def ssh_keys_generate_keypair(self): |
|
949 | def ssh_keys_generate_keypair(self): | |
946 | _ = self.request.translate |
|
950 | _ = self.request.translate | |
947 | c = self.load_default_context() |
|
951 | c = self.load_default_context() | |
948 |
|
952 | |||
949 | c.user = self.db_user |
|
953 | c.user = self.db_user | |
950 |
|
954 | |||
951 | c.active = 'ssh_keys_generate' |
|
955 | c.active = 'ssh_keys_generate' | |
952 | comment = 'RhodeCode-SSH {}'.format(c.user.email or '') |
|
956 | comment = 'RhodeCode-SSH {}'.format(c.user.email or '') | |
953 | private_format = self.request.GET.get('private_format') \ |
|
957 | private_format = self.request.GET.get('private_format') \ | |
954 | or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT |
|
958 | or SshKeyModel.DEFAULT_PRIVATE_KEY_FORMAT | |
955 | c.private, c.public = SshKeyModel().generate_keypair( |
|
959 | c.private, c.public = SshKeyModel().generate_keypair( | |
956 | comment=comment, private_format=private_format) |
|
960 | comment=comment, private_format=private_format) | |
957 |
|
961 | |||
958 | return self._get_template_context(c) |
|
962 | return self._get_template_context(c) | |
959 |
|
963 | |||
960 | @LoginRequired() |
|
964 | @LoginRequired() | |
961 | @HasPermissionAllDecorator('hg.admin') |
|
965 | @HasPermissionAllDecorator('hg.admin') | |
962 | @CSRFRequired() |
|
966 | @CSRFRequired() | |
963 | @view_config( |
|
967 | @view_config( | |
964 | route_name='edit_user_ssh_keys_add', request_method='POST') |
|
968 | route_name='edit_user_ssh_keys_add', request_method='POST') | |
965 | def ssh_keys_add(self): |
|
969 | def ssh_keys_add(self): | |
966 | _ = self.request.translate |
|
970 | _ = self.request.translate | |
967 | c = self.load_default_context() |
|
971 | c = self.load_default_context() | |
968 |
|
972 | |||
969 | user_id = self.db_user_id |
|
973 | user_id = self.db_user_id | |
970 | c.user = self.db_user |
|
974 | c.user = self.db_user | |
971 |
|
975 | |||
972 | user_data = c.user.get_api_data() |
|
976 | user_data = c.user.get_api_data() | |
973 | key_data = self.request.POST.get('key_data') |
|
977 | key_data = self.request.POST.get('key_data') | |
974 | description = self.request.POST.get('description') |
|
978 | description = self.request.POST.get('description') | |
975 |
|
979 | |||
976 | fingerprint = 'unknown' |
|
980 | fingerprint = 'unknown' | |
977 | try: |
|
981 | try: | |
978 | if not key_data: |
|
982 | if not key_data: | |
979 | raise ValueError('Please add a valid public key') |
|
983 | raise ValueError('Please add a valid public key') | |
980 |
|
984 | |||
981 | key = SshKeyModel().parse_key(key_data.strip()) |
|
985 | key = SshKeyModel().parse_key(key_data.strip()) | |
982 | fingerprint = key.hash_md5() |
|
986 | fingerprint = key.hash_md5() | |
983 |
|
987 | |||
984 | ssh_key = SshKeyModel().create( |
|
988 | ssh_key = SshKeyModel().create( | |
985 | c.user.user_id, fingerprint, key.keydata, description) |
|
989 | c.user.user_id, fingerprint, key.keydata, description) | |
986 | ssh_key_data = ssh_key.get_api_data() |
|
990 | ssh_key_data = ssh_key.get_api_data() | |
987 |
|
991 | |||
988 | audit_logger.store_web( |
|
992 | audit_logger.store_web( | |
989 | 'user.edit.ssh_key.add', action_data={ |
|
993 | 'user.edit.ssh_key.add', action_data={ | |
990 | 'data': {'ssh_key': ssh_key_data, 'user': user_data}}, |
|
994 | 'data': {'ssh_key': ssh_key_data, 'user': user_data}}, | |
991 | user=self._rhodecode_user, ) |
|
995 | user=self._rhodecode_user, ) | |
992 | Session().commit() |
|
996 | Session().commit() | |
993 |
|
997 | |||
994 | # Trigger an event on change of keys. |
|
998 | # Trigger an event on change of keys. | |
995 | trigger(SshKeyFileChangeEvent(), self.request.registry) |
|
999 | trigger(SshKeyFileChangeEvent(), self.request.registry) | |
996 |
|
1000 | |||
997 | h.flash(_("Ssh Key successfully created"), category='success') |
|
1001 | h.flash(_("Ssh Key successfully created"), category='success') | |
998 |
|
1002 | |||
999 | except IntegrityError: |
|
1003 | except IntegrityError: | |
1000 | log.exception("Exception during ssh key saving") |
|
1004 | log.exception("Exception during ssh key saving") | |
1001 | err = 'Such key with fingerprint `{}` already exists, ' \ |
|
1005 | err = 'Such key with fingerprint `{}` already exists, ' \ | |
1002 | 'please use a different one'.format(fingerprint) |
|
1006 | 'please use a different one'.format(fingerprint) | |
1003 | h.flash(_('An error occurred during ssh key saving: {}').format(err), |
|
1007 | h.flash(_('An error occurred during ssh key saving: {}').format(err), | |
1004 | category='error') |
|
1008 | category='error') | |
1005 | except Exception as e: |
|
1009 | except Exception as e: | |
1006 | log.exception("Exception during ssh key saving") |
|
1010 | log.exception("Exception during ssh key saving") | |
1007 | h.flash(_('An error occurred during ssh key saving: {}').format(e), |
|
1011 | h.flash(_('An error occurred during ssh key saving: {}').format(e), | |
1008 | category='error') |
|
1012 | category='error') | |
1009 |
|
1013 | |||
1010 | return HTTPFound( |
|
1014 | return HTTPFound( | |
1011 | h.route_path('edit_user_ssh_keys', user_id=user_id)) |
|
1015 | h.route_path('edit_user_ssh_keys', user_id=user_id)) | |
1012 |
|
1016 | |||
1013 | @LoginRequired() |
|
1017 | @LoginRequired() | |
1014 | @HasPermissionAllDecorator('hg.admin') |
|
1018 | @HasPermissionAllDecorator('hg.admin') | |
1015 | @CSRFRequired() |
|
1019 | @CSRFRequired() | |
1016 | @view_config( |
|
1020 | @view_config( | |
1017 | route_name='edit_user_ssh_keys_delete', request_method='POST') |
|
1021 | route_name='edit_user_ssh_keys_delete', request_method='POST') | |
1018 | def ssh_keys_delete(self): |
|
1022 | def ssh_keys_delete(self): | |
1019 | _ = self.request.translate |
|
1023 | _ = self.request.translate | |
1020 | c = self.load_default_context() |
|
1024 | c = self.load_default_context() | |
1021 |
|
1025 | |||
1022 | user_id = self.db_user_id |
|
1026 | user_id = self.db_user_id | |
1023 | c.user = self.db_user |
|
1027 | c.user = self.db_user | |
1024 |
|
1028 | |||
1025 | user_data = c.user.get_api_data() |
|
1029 | user_data = c.user.get_api_data() | |
1026 |
|
1030 | |||
1027 | del_ssh_key = self.request.POST.get('del_ssh_key') |
|
1031 | del_ssh_key = self.request.POST.get('del_ssh_key') | |
1028 |
|
1032 | |||
1029 | if del_ssh_key: |
|
1033 | if del_ssh_key: | |
1030 | ssh_key = UserSshKeys.get_or_404(del_ssh_key) |
|
1034 | ssh_key = UserSshKeys.get_or_404(del_ssh_key) | |
1031 | ssh_key_data = ssh_key.get_api_data() |
|
1035 | ssh_key_data = ssh_key.get_api_data() | |
1032 |
|
1036 | |||
1033 | SshKeyModel().delete(del_ssh_key, c.user.user_id) |
|
1037 | SshKeyModel().delete(del_ssh_key, c.user.user_id) | |
1034 | audit_logger.store_web( |
|
1038 | audit_logger.store_web( | |
1035 | 'user.edit.ssh_key.delete', action_data={ |
|
1039 | 'user.edit.ssh_key.delete', action_data={ | |
1036 | 'data': {'ssh_key': ssh_key_data, 'user': user_data}}, |
|
1040 | 'data': {'ssh_key': ssh_key_data, 'user': user_data}}, | |
1037 | user=self._rhodecode_user,) |
|
1041 | user=self._rhodecode_user,) | |
1038 | Session().commit() |
|
1042 | Session().commit() | |
1039 | # Trigger an event on change of keys. |
|
1043 | # Trigger an event on change of keys. | |
1040 | trigger(SshKeyFileChangeEvent(), self.request.registry) |
|
1044 | trigger(SshKeyFileChangeEvent(), self.request.registry) | |
1041 | h.flash(_("Ssh key successfully deleted"), category='success') |
|
1045 | h.flash(_("Ssh key successfully deleted"), category='success') | |
1042 |
|
1046 | |||
1043 | return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id)) |
|
1047 | return HTTPFound(h.route_path('edit_user_ssh_keys', user_id=user_id)) | |
1044 |
|
1048 | |||
1045 | @LoginRequired() |
|
1049 | @LoginRequired() | |
1046 | @HasPermissionAllDecorator('hg.admin') |
|
1050 | @HasPermissionAllDecorator('hg.admin') | |
1047 | @view_config( |
|
1051 | @view_config( | |
1048 | route_name='edit_user_emails', request_method='GET', |
|
1052 | route_name='edit_user_emails', request_method='GET', | |
1049 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
1053 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
1050 | def emails(self): |
|
1054 | def emails(self): | |
1051 | _ = self.request.translate |
|
1055 | _ = self.request.translate | |
1052 | c = self.load_default_context() |
|
1056 | c = self.load_default_context() | |
1053 | c.user = self.db_user |
|
1057 | c.user = self.db_user | |
1054 |
|
1058 | |||
1055 | c.active = 'emails' |
|
1059 | c.active = 'emails' | |
1056 | c.user_email_map = UserEmailMap.query() \ |
|
1060 | c.user_email_map = UserEmailMap.query() \ | |
1057 | .filter(UserEmailMap.user == c.user).all() |
|
1061 | .filter(UserEmailMap.user == c.user).all() | |
1058 |
|
1062 | |||
1059 | return self._get_template_context(c) |
|
1063 | return self._get_template_context(c) | |
1060 |
|
1064 | |||
1061 | @LoginRequired() |
|
1065 | @LoginRequired() | |
1062 | @HasPermissionAllDecorator('hg.admin') |
|
1066 | @HasPermissionAllDecorator('hg.admin') | |
1063 | @CSRFRequired() |
|
1067 | @CSRFRequired() | |
1064 | @view_config( |
|
1068 | @view_config( | |
1065 | route_name='edit_user_emails_add', request_method='POST') |
|
1069 | route_name='edit_user_emails_add', request_method='POST') | |
1066 | def emails_add(self): |
|
1070 | def emails_add(self): | |
1067 | _ = self.request.translate |
|
1071 | _ = self.request.translate | |
1068 | c = self.load_default_context() |
|
1072 | c = self.load_default_context() | |
1069 |
|
1073 | |||
1070 | user_id = self.db_user_id |
|
1074 | user_id = self.db_user_id | |
1071 | c.user = self.db_user |
|
1075 | c.user = self.db_user | |
1072 |
|
1076 | |||
1073 | email = self.request.POST.get('new_email') |
|
1077 | email = self.request.POST.get('new_email') | |
1074 | user_data = c.user.get_api_data() |
|
1078 | user_data = c.user.get_api_data() | |
1075 | try: |
|
1079 | try: | |
1076 |
|
1080 | |||
1077 | form = UserExtraEmailForm(self.request.translate)() |
|
1081 | form = UserExtraEmailForm(self.request.translate)() | |
1078 | data = form.to_python({'email': email}) |
|
1082 | data = form.to_python({'email': email}) | |
1079 | email = data['email'] |
|
1083 | email = data['email'] | |
1080 |
|
1084 | |||
1081 | UserModel().add_extra_email(c.user.user_id, email) |
|
1085 | UserModel().add_extra_email(c.user.user_id, email) | |
1082 | audit_logger.store_web( |
|
1086 | audit_logger.store_web( | |
1083 | 'user.edit.email.add', |
|
1087 | 'user.edit.email.add', | |
1084 | action_data={'email': email, 'user': user_data}, |
|
1088 | action_data={'email': email, 'user': user_data}, | |
1085 | user=self._rhodecode_user) |
|
1089 | user=self._rhodecode_user) | |
1086 | Session().commit() |
|
1090 | Session().commit() | |
1087 | h.flash(_("Added new email address `%s` for user account") % email, |
|
1091 | h.flash(_("Added new email address `%s` for user account") % email, | |
1088 | category='success') |
|
1092 | category='success') | |
1089 | except formencode.Invalid as error: |
|
1093 | except formencode.Invalid as error: | |
1090 | h.flash(h.escape(error.error_dict['email']), category='error') |
|
1094 | h.flash(h.escape(error.error_dict['email']), category='error') | |
1091 | except IntegrityError: |
|
1095 | except IntegrityError: | |
1092 | log.warning("Email %s already exists", email) |
|
1096 | log.warning("Email %s already exists", email) | |
1093 | h.flash(_('Email `{}` is already registered for another user.').format(email), |
|
1097 | h.flash(_('Email `{}` is already registered for another user.').format(email), | |
1094 | category='error') |
|
1098 | category='error') | |
1095 | except Exception: |
|
1099 | except Exception: | |
1096 | log.exception("Exception during email saving") |
|
1100 | log.exception("Exception during email saving") | |
1097 | h.flash(_('An error occurred during email saving'), |
|
1101 | h.flash(_('An error occurred during email saving'), | |
1098 | category='error') |
|
1102 | category='error') | |
1099 | raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id)) |
|
1103 | raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id)) | |
1100 |
|
1104 | |||
1101 | @LoginRequired() |
|
1105 | @LoginRequired() | |
1102 | @HasPermissionAllDecorator('hg.admin') |
|
1106 | @HasPermissionAllDecorator('hg.admin') | |
1103 | @CSRFRequired() |
|
1107 | @CSRFRequired() | |
1104 | @view_config( |
|
1108 | @view_config( | |
1105 | route_name='edit_user_emails_delete', request_method='POST') |
|
1109 | route_name='edit_user_emails_delete', request_method='POST') | |
1106 | def emails_delete(self): |
|
1110 | def emails_delete(self): | |
1107 | _ = self.request.translate |
|
1111 | _ = self.request.translate | |
1108 | c = self.load_default_context() |
|
1112 | c = self.load_default_context() | |
1109 |
|
1113 | |||
1110 | user_id = self.db_user_id |
|
1114 | user_id = self.db_user_id | |
1111 | c.user = self.db_user |
|
1115 | c.user = self.db_user | |
1112 |
|
1116 | |||
1113 | email_id = self.request.POST.get('del_email_id') |
|
1117 | email_id = self.request.POST.get('del_email_id') | |
1114 | user_model = UserModel() |
|
1118 | user_model = UserModel() | |
1115 |
|
1119 | |||
1116 | email = UserEmailMap.query().get(email_id).email |
|
1120 | email = UserEmailMap.query().get(email_id).email | |
1117 | user_data = c.user.get_api_data() |
|
1121 | user_data = c.user.get_api_data() | |
1118 | user_model.delete_extra_email(c.user.user_id, email_id) |
|
1122 | user_model.delete_extra_email(c.user.user_id, email_id) | |
1119 | audit_logger.store_web( |
|
1123 | audit_logger.store_web( | |
1120 | 'user.edit.email.delete', |
|
1124 | 'user.edit.email.delete', | |
1121 | action_data={'email': email, 'user': user_data}, |
|
1125 | action_data={'email': email, 'user': user_data}, | |
1122 | user=self._rhodecode_user) |
|
1126 | user=self._rhodecode_user) | |
1123 | Session().commit() |
|
1127 | Session().commit() | |
1124 | h.flash(_("Removed email address from user account"), |
|
1128 | h.flash(_("Removed email address from user account"), | |
1125 | category='success') |
|
1129 | category='success') | |
1126 | raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id)) |
|
1130 | raise HTTPFound(h.route_path('edit_user_emails', user_id=user_id)) | |
1127 |
|
1131 | |||
1128 | @LoginRequired() |
|
1132 | @LoginRequired() | |
1129 | @HasPermissionAllDecorator('hg.admin') |
|
1133 | @HasPermissionAllDecorator('hg.admin') | |
1130 | @view_config( |
|
1134 | @view_config( | |
1131 | route_name='edit_user_ips', request_method='GET', |
|
1135 | route_name='edit_user_ips', request_method='GET', | |
1132 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
1136 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
1133 | def ips(self): |
|
1137 | def ips(self): | |
1134 | _ = self.request.translate |
|
1138 | _ = self.request.translate | |
1135 | c = self.load_default_context() |
|
1139 | c = self.load_default_context() | |
1136 | c.user = self.db_user |
|
1140 | c.user = self.db_user | |
1137 |
|
1141 | |||
1138 | c.active = 'ips' |
|
1142 | c.active = 'ips' | |
1139 | c.user_ip_map = UserIpMap.query() \ |
|
1143 | c.user_ip_map = UserIpMap.query() \ | |
1140 | .filter(UserIpMap.user == c.user).all() |
|
1144 | .filter(UserIpMap.user == c.user).all() | |
1141 |
|
1145 | |||
1142 | c.inherit_default_ips = c.user.inherit_default_permissions |
|
1146 | c.inherit_default_ips = c.user.inherit_default_permissions | |
1143 | c.default_user_ip_map = UserIpMap.query() \ |
|
1147 | c.default_user_ip_map = UserIpMap.query() \ | |
1144 | .filter(UserIpMap.user == User.get_default_user()).all() |
|
1148 | .filter(UserIpMap.user == User.get_default_user()).all() | |
1145 |
|
1149 | |||
1146 | return self._get_template_context(c) |
|
1150 | return self._get_template_context(c) | |
1147 |
|
1151 | |||
1148 | @LoginRequired() |
|
1152 | @LoginRequired() | |
1149 | @HasPermissionAllDecorator('hg.admin') |
|
1153 | @HasPermissionAllDecorator('hg.admin') | |
1150 | @CSRFRequired() |
|
1154 | @CSRFRequired() | |
1151 | @view_config( |
|
1155 | @view_config( | |
1152 | route_name='edit_user_ips_add', request_method='POST') |
|
1156 | route_name='edit_user_ips_add', request_method='POST') | |
1153 | # NOTE(marcink): this view is allowed for default users, as we can |
|
1157 | # NOTE(marcink): this view is allowed for default users, as we can | |
1154 | # edit their IP white list |
|
1158 | # edit their IP white list | |
1155 | def ips_add(self): |
|
1159 | def ips_add(self): | |
1156 | _ = self.request.translate |
|
1160 | _ = self.request.translate | |
1157 | c = self.load_default_context() |
|
1161 | c = self.load_default_context() | |
1158 |
|
1162 | |||
1159 | user_id = self.db_user_id |
|
1163 | user_id = self.db_user_id | |
1160 | c.user = self.db_user |
|
1164 | c.user = self.db_user | |
1161 |
|
1165 | |||
1162 | user_model = UserModel() |
|
1166 | user_model = UserModel() | |
1163 | desc = self.request.POST.get('description') |
|
1167 | desc = self.request.POST.get('description') | |
1164 | try: |
|
1168 | try: | |
1165 | ip_list = user_model.parse_ip_range( |
|
1169 | ip_list = user_model.parse_ip_range( | |
1166 | self.request.POST.get('new_ip')) |
|
1170 | self.request.POST.get('new_ip')) | |
1167 | except Exception as e: |
|
1171 | except Exception as e: | |
1168 | ip_list = [] |
|
1172 | ip_list = [] | |
1169 | log.exception("Exception during ip saving") |
|
1173 | log.exception("Exception during ip saving") | |
1170 | h.flash(_('An error occurred during ip saving:%s' % (e,)), |
|
1174 | h.flash(_('An error occurred during ip saving:%s' % (e,)), | |
1171 | category='error') |
|
1175 | category='error') | |
1172 | added = [] |
|
1176 | added = [] | |
1173 | user_data = c.user.get_api_data() |
|
1177 | user_data = c.user.get_api_data() | |
1174 | for ip in ip_list: |
|
1178 | for ip in ip_list: | |
1175 | try: |
|
1179 | try: | |
1176 | form = UserExtraIpForm(self.request.translate)() |
|
1180 | form = UserExtraIpForm(self.request.translate)() | |
1177 | data = form.to_python({'ip': ip}) |
|
1181 | data = form.to_python({'ip': ip}) | |
1178 | ip = data['ip'] |
|
1182 | ip = data['ip'] | |
1179 |
|
1183 | |||
1180 | user_model.add_extra_ip(c.user.user_id, ip, desc) |
|
1184 | user_model.add_extra_ip(c.user.user_id, ip, desc) | |
1181 | audit_logger.store_web( |
|
1185 | audit_logger.store_web( | |
1182 | 'user.edit.ip.add', |
|
1186 | 'user.edit.ip.add', | |
1183 | action_data={'ip': ip, 'user': user_data}, |
|
1187 | action_data={'ip': ip, 'user': user_data}, | |
1184 | user=self._rhodecode_user) |
|
1188 | user=self._rhodecode_user) | |
1185 | Session().commit() |
|
1189 | Session().commit() | |
1186 | added.append(ip) |
|
1190 | added.append(ip) | |
1187 | except formencode.Invalid as error: |
|
1191 | except formencode.Invalid as error: | |
1188 | msg = error.error_dict['ip'] |
|
1192 | msg = error.error_dict['ip'] | |
1189 | h.flash(msg, category='error') |
|
1193 | h.flash(msg, category='error') | |
1190 | except Exception: |
|
1194 | except Exception: | |
1191 | log.exception("Exception during ip saving") |
|
1195 | log.exception("Exception during ip saving") | |
1192 | h.flash(_('An error occurred during ip saving'), |
|
1196 | h.flash(_('An error occurred during ip saving'), | |
1193 | category='error') |
|
1197 | category='error') | |
1194 | if added: |
|
1198 | if added: | |
1195 | h.flash( |
|
1199 | h.flash( | |
1196 | _("Added ips %s to user whitelist") % (', '.join(ip_list), ), |
|
1200 | _("Added ips %s to user whitelist") % (', '.join(ip_list), ), | |
1197 | category='success') |
|
1201 | category='success') | |
1198 | if 'default_user' in self.request.POST: |
|
1202 | if 'default_user' in self.request.POST: | |
1199 | # case for editing global IP list we do it for 'DEFAULT' user |
|
1203 | # case for editing global IP list we do it for 'DEFAULT' user | |
1200 | raise HTTPFound(h.route_path('admin_permissions_ips')) |
|
1204 | raise HTTPFound(h.route_path('admin_permissions_ips')) | |
1201 | raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id)) |
|
1205 | raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id)) | |
1202 |
|
1206 | |||
1203 | @LoginRequired() |
|
1207 | @LoginRequired() | |
1204 | @HasPermissionAllDecorator('hg.admin') |
|
1208 | @HasPermissionAllDecorator('hg.admin') | |
1205 | @CSRFRequired() |
|
1209 | @CSRFRequired() | |
1206 | @view_config( |
|
1210 | @view_config( | |
1207 | route_name='edit_user_ips_delete', request_method='POST') |
|
1211 | route_name='edit_user_ips_delete', request_method='POST') | |
1208 | # NOTE(marcink): this view is allowed for default users, as we can |
|
1212 | # NOTE(marcink): this view is allowed for default users, as we can | |
1209 | # edit their IP white list |
|
1213 | # edit their IP white list | |
1210 | def ips_delete(self): |
|
1214 | def ips_delete(self): | |
1211 | _ = self.request.translate |
|
1215 | _ = self.request.translate | |
1212 | c = self.load_default_context() |
|
1216 | c = self.load_default_context() | |
1213 |
|
1217 | |||
1214 | user_id = self.db_user_id |
|
1218 | user_id = self.db_user_id | |
1215 | c.user = self.db_user |
|
1219 | c.user = self.db_user | |
1216 |
|
1220 | |||
1217 | ip_id = self.request.POST.get('del_ip_id') |
|
1221 | ip_id = self.request.POST.get('del_ip_id') | |
1218 | user_model = UserModel() |
|
1222 | user_model = UserModel() | |
1219 | user_data = c.user.get_api_data() |
|
1223 | user_data = c.user.get_api_data() | |
1220 | ip = UserIpMap.query().get(ip_id).ip_addr |
|
1224 | ip = UserIpMap.query().get(ip_id).ip_addr | |
1221 | user_model.delete_extra_ip(c.user.user_id, ip_id) |
|
1225 | user_model.delete_extra_ip(c.user.user_id, ip_id) | |
1222 | audit_logger.store_web( |
|
1226 | audit_logger.store_web( | |
1223 | 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data}, |
|
1227 | 'user.edit.ip.delete', action_data={'ip': ip, 'user': user_data}, | |
1224 | user=self._rhodecode_user) |
|
1228 | user=self._rhodecode_user) | |
1225 | Session().commit() |
|
1229 | Session().commit() | |
1226 | h.flash(_("Removed ip address from user whitelist"), category='success') |
|
1230 | h.flash(_("Removed ip address from user whitelist"), category='success') | |
1227 |
|
1231 | |||
1228 | if 'default_user' in self.request.POST: |
|
1232 | if 'default_user' in self.request.POST: | |
1229 | # case for editing global IP list we do it for 'DEFAULT' user |
|
1233 | # case for editing global IP list we do it for 'DEFAULT' user | |
1230 | raise HTTPFound(h.route_path('admin_permissions_ips')) |
|
1234 | raise HTTPFound(h.route_path('admin_permissions_ips')) | |
1231 | raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id)) |
|
1235 | raise HTTPFound(h.route_path('edit_user_ips', user_id=user_id)) | |
1232 |
|
1236 | |||
1233 | @LoginRequired() |
|
1237 | @LoginRequired() | |
1234 | @HasPermissionAllDecorator('hg.admin') |
|
1238 | @HasPermissionAllDecorator('hg.admin') | |
1235 | @view_config( |
|
1239 | @view_config( | |
1236 | route_name='edit_user_groups_management', request_method='GET', |
|
1240 | route_name='edit_user_groups_management', request_method='GET', | |
1237 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
1241 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
1238 | def groups_management(self): |
|
1242 | def groups_management(self): | |
1239 | c = self.load_default_context() |
|
1243 | c = self.load_default_context() | |
1240 | c.user = self.db_user |
|
1244 | c.user = self.db_user | |
1241 | c.data = c.user.group_member |
|
1245 | c.data = c.user.group_member | |
1242 |
|
1246 | |||
1243 | groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) |
|
1247 | groups = [UserGroupModel.get_user_groups_as_dict(group.users_group) | |
1244 | for group in c.user.group_member] |
|
1248 | for group in c.user.group_member] | |
1245 | c.groups = json.dumps(groups) |
|
1249 | c.groups = json.dumps(groups) | |
1246 | c.active = 'groups' |
|
1250 | c.active = 'groups' | |
1247 |
|
1251 | |||
1248 | return self._get_template_context(c) |
|
1252 | return self._get_template_context(c) | |
1249 |
|
1253 | |||
1250 | @LoginRequired() |
|
1254 | @LoginRequired() | |
1251 | @HasPermissionAllDecorator('hg.admin') |
|
1255 | @HasPermissionAllDecorator('hg.admin') | |
1252 | @CSRFRequired() |
|
1256 | @CSRFRequired() | |
1253 | @view_config( |
|
1257 | @view_config( | |
1254 | route_name='edit_user_groups_management_updates', request_method='POST') |
|
1258 | route_name='edit_user_groups_management_updates', request_method='POST') | |
1255 | def groups_management_updates(self): |
|
1259 | def groups_management_updates(self): | |
1256 | _ = self.request.translate |
|
1260 | _ = self.request.translate | |
1257 | c = self.load_default_context() |
|
1261 | c = self.load_default_context() | |
1258 |
|
1262 | |||
1259 | user_id = self.db_user_id |
|
1263 | user_id = self.db_user_id | |
1260 | c.user = self.db_user |
|
1264 | c.user = self.db_user | |
1261 |
|
1265 | |||
1262 | user_groups = set(self.request.POST.getall('users_group_id')) |
|
1266 | user_groups = set(self.request.POST.getall('users_group_id')) | |
1263 | user_groups_objects = [] |
|
1267 | user_groups_objects = [] | |
1264 |
|
1268 | |||
1265 | for ugid in user_groups: |
|
1269 | for ugid in user_groups: | |
1266 | user_groups_objects.append( |
|
1270 | user_groups_objects.append( | |
1267 | UserGroupModel().get_group(safe_int(ugid))) |
|
1271 | UserGroupModel().get_group(safe_int(ugid))) | |
1268 | user_group_model = UserGroupModel() |
|
1272 | user_group_model = UserGroupModel() | |
1269 | added_to_groups, removed_from_groups = \ |
|
1273 | added_to_groups, removed_from_groups = \ | |
1270 | user_group_model.change_groups(c.user, user_groups_objects) |
|
1274 | user_group_model.change_groups(c.user, user_groups_objects) | |
1271 |
|
1275 | |||
1272 | user_data = c.user.get_api_data() |
|
1276 | user_data = c.user.get_api_data() | |
1273 | for user_group_id in added_to_groups: |
|
1277 | for user_group_id in added_to_groups: | |
1274 | user_group = UserGroup.get(user_group_id) |
|
1278 | user_group = UserGroup.get(user_group_id) | |
1275 | old_values = user_group.get_api_data() |
|
1279 | old_values = user_group.get_api_data() | |
1276 | audit_logger.store_web( |
|
1280 | audit_logger.store_web( | |
1277 | 'user_group.edit.member.add', |
|
1281 | 'user_group.edit.member.add', | |
1278 | action_data={'user': user_data, 'old_data': old_values}, |
|
1282 | action_data={'user': user_data, 'old_data': old_values}, | |
1279 | user=self._rhodecode_user) |
|
1283 | user=self._rhodecode_user) | |
1280 |
|
1284 | |||
1281 | for user_group_id in removed_from_groups: |
|
1285 | for user_group_id in removed_from_groups: | |
1282 | user_group = UserGroup.get(user_group_id) |
|
1286 | user_group = UserGroup.get(user_group_id) | |
1283 | old_values = user_group.get_api_data() |
|
1287 | old_values = user_group.get_api_data() | |
1284 | audit_logger.store_web( |
|
1288 | audit_logger.store_web( | |
1285 | 'user_group.edit.member.delete', |
|
1289 | 'user_group.edit.member.delete', | |
1286 | action_data={'user': user_data, 'old_data': old_values}, |
|
1290 | action_data={'user': user_data, 'old_data': old_values}, | |
1287 | user=self._rhodecode_user) |
|
1291 | user=self._rhodecode_user) | |
1288 |
|
1292 | |||
1289 | Session().commit() |
|
1293 | Session().commit() | |
1290 | c.active = 'user_groups_management' |
|
1294 | c.active = 'user_groups_management' | |
1291 | h.flash(_("Groups successfully changed"), category='success') |
|
1295 | h.flash(_("Groups successfully changed"), category='success') | |
1292 |
|
1296 | |||
1293 | return HTTPFound(h.route_path( |
|
1297 | return HTTPFound(h.route_path( | |
1294 | 'edit_user_groups_management', user_id=user_id)) |
|
1298 | 'edit_user_groups_management', user_id=user_id)) | |
1295 |
|
1299 | |||
1296 | @LoginRequired() |
|
1300 | @LoginRequired() | |
1297 | @HasPermissionAllDecorator('hg.admin') |
|
1301 | @HasPermissionAllDecorator('hg.admin') | |
1298 | @view_config( |
|
1302 | @view_config( | |
1299 | route_name='edit_user_audit_logs', request_method='GET', |
|
1303 | route_name='edit_user_audit_logs', request_method='GET', | |
1300 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
1304 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
1301 | def user_audit_logs(self): |
|
1305 | def user_audit_logs(self): | |
1302 | _ = self.request.translate |
|
1306 | _ = self.request.translate | |
1303 | c = self.load_default_context() |
|
1307 | c = self.load_default_context() | |
1304 | c.user = self.db_user |
|
1308 | c.user = self.db_user | |
1305 |
|
1309 | |||
1306 | c.active = 'audit' |
|
1310 | c.active = 'audit' | |
1307 |
|
1311 | |||
1308 | p = safe_int(self.request.GET.get('page', 1), 1) |
|
1312 | p = safe_int(self.request.GET.get('page', 1), 1) | |
1309 |
|
1313 | |||
1310 | filter_term = self.request.GET.get('filter') |
|
1314 | filter_term = self.request.GET.get('filter') | |
1311 | user_log = UserModel().get_user_log(c.user, filter_term) |
|
1315 | user_log = UserModel().get_user_log(c.user, filter_term) | |
1312 |
|
1316 | |||
1313 | def url_generator(page_num): |
|
1317 | def url_generator(page_num): | |
1314 | query_params = { |
|
1318 | query_params = { | |
1315 | 'page': page_num |
|
1319 | 'page': page_num | |
1316 | } |
|
1320 | } | |
1317 | if filter_term: |
|
1321 | if filter_term: | |
1318 | query_params['filter'] = filter_term |
|
1322 | query_params['filter'] = filter_term | |
1319 | return self.request.current_route_path(_query=query_params) |
|
1323 | return self.request.current_route_path(_query=query_params) | |
1320 |
|
1324 | |||
1321 | c.audit_logs = SqlPage( |
|
1325 | c.audit_logs = SqlPage( | |
1322 | user_log, page=p, items_per_page=10, url_maker=url_generator) |
|
1326 | user_log, page=p, items_per_page=10, url_maker=url_generator) | |
1323 | c.filter_term = filter_term |
|
1327 | c.filter_term = filter_term | |
1324 | return self._get_template_context(c) |
|
1328 | return self._get_template_context(c) | |
1325 |
|
1329 | |||
1326 | @LoginRequired() |
|
1330 | @LoginRequired() | |
1327 | @HasPermissionAllDecorator('hg.admin') |
|
1331 | @HasPermissionAllDecorator('hg.admin') | |
1328 | @view_config( |
|
1332 | @view_config( | |
1329 | route_name='edit_user_audit_logs_download', request_method='GET', |
|
1333 | route_name='edit_user_audit_logs_download', request_method='GET', | |
1330 | renderer='string') |
|
1334 | renderer='string') | |
1331 | def user_audit_logs_download(self): |
|
1335 | def user_audit_logs_download(self): | |
1332 | _ = self.request.translate |
|
1336 | _ = self.request.translate | |
1333 | c = self.load_default_context() |
|
1337 | c = self.load_default_context() | |
1334 | c.user = self.db_user |
|
1338 | c.user = self.db_user | |
1335 |
|
1339 | |||
1336 | user_log = UserModel().get_user_log(c.user, filter_term=None) |
|
1340 | user_log = UserModel().get_user_log(c.user, filter_term=None) | |
1337 |
|
1341 | |||
1338 | audit_log_data = {} |
|
1342 | audit_log_data = {} | |
1339 | for entry in user_log: |
|
1343 | for entry in user_log: | |
1340 | audit_log_data[entry.user_log_id] = entry.get_dict() |
|
1344 | audit_log_data[entry.user_log_id] = entry.get_dict() | |
1341 |
|
1345 | |||
1342 | response = Response(json.dumps(audit_log_data, indent=4)) |
|
1346 | response = Response(json.dumps(audit_log_data, indent=4)) | |
1343 | response.content_disposition = str( |
|
1347 | response.content_disposition = str( | |
1344 | 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id)) |
|
1348 | 'attachment; filename=%s' % 'user_{}_audit_logs.json'.format(c.user.user_id)) | |
1345 | response.content_type = 'application/json' |
|
1349 | response.content_type = 'application/json' | |
1346 |
|
1350 | |||
1347 | return response |
|
1351 | return response | |
1348 |
|
1352 | |||
1349 | @LoginRequired() |
|
1353 | @LoginRequired() | |
1350 | @HasPermissionAllDecorator('hg.admin') |
|
1354 | @HasPermissionAllDecorator('hg.admin') | |
1351 | @view_config( |
|
1355 | @view_config( | |
1352 | route_name='edit_user_perms_summary', request_method='GET', |
|
1356 | route_name='edit_user_perms_summary', request_method='GET', | |
1353 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
1357 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
1354 | def user_perms_summary(self): |
|
1358 | def user_perms_summary(self): | |
1355 | _ = self.request.translate |
|
1359 | _ = self.request.translate | |
1356 | c = self.load_default_context() |
|
1360 | c = self.load_default_context() | |
1357 | c.user = self.db_user |
|
1361 | c.user = self.db_user | |
1358 |
|
1362 | |||
1359 | c.active = 'perms_summary' |
|
1363 | c.active = 'perms_summary' | |
1360 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) |
|
1364 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) | |
1361 |
|
1365 | |||
1362 | return self._get_template_context(c) |
|
1366 | return self._get_template_context(c) | |
1363 |
|
1367 | |||
1364 | @LoginRequired() |
|
1368 | @LoginRequired() | |
1365 | @HasPermissionAllDecorator('hg.admin') |
|
1369 | @HasPermissionAllDecorator('hg.admin') | |
1366 | @view_config( |
|
1370 | @view_config( | |
1367 | route_name='edit_user_perms_summary_json', request_method='GET', |
|
1371 | route_name='edit_user_perms_summary_json', request_method='GET', | |
1368 | renderer='json_ext') |
|
1372 | renderer='json_ext') | |
1369 | def user_perms_summary_json(self): |
|
1373 | def user_perms_summary_json(self): | |
1370 | self.load_default_context() |
|
1374 | self.load_default_context() | |
1371 | perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr) |
|
1375 | perm_user = self.db_user.AuthUser(ip_addr=self.request.remote_addr) | |
1372 |
|
1376 | |||
1373 | return perm_user.permissions |
|
1377 | return perm_user.permissions | |
1374 |
|
1378 | |||
1375 | @LoginRequired() |
|
1379 | @LoginRequired() | |
1376 | @HasPermissionAllDecorator('hg.admin') |
|
1380 | @HasPermissionAllDecorator('hg.admin') | |
1377 | @view_config( |
|
1381 | @view_config( | |
1378 | route_name='edit_user_caches', request_method='GET', |
|
1382 | route_name='edit_user_caches', request_method='GET', | |
1379 | renderer='rhodecode:templates/admin/users/user_edit.mako') |
|
1383 | renderer='rhodecode:templates/admin/users/user_edit.mako') | |
1380 | def user_caches(self): |
|
1384 | def user_caches(self): | |
1381 | _ = self.request.translate |
|
1385 | _ = self.request.translate | |
1382 | c = self.load_default_context() |
|
1386 | c = self.load_default_context() | |
1383 | c.user = self.db_user |
|
1387 | c.user = self.db_user | |
1384 |
|
1388 | |||
1385 | c.active = 'caches' |
|
1389 | c.active = 'caches' | |
1386 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) |
|
1390 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) | |
1387 |
|
1391 | |||
1388 | cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id) |
|
1392 | cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id) | |
1389 | c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid) |
|
1393 | c.region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid) | |
1390 | c.backend = c.region.backend |
|
1394 | c.backend = c.region.backend | |
1391 | c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid)) |
|
1395 | c.user_keys = sorted(c.region.backend.list_keys(prefix=cache_namespace_uid)) | |
1392 |
|
1396 | |||
1393 | return self._get_template_context(c) |
|
1397 | return self._get_template_context(c) | |
1394 |
|
1398 | |||
1395 | @LoginRequired() |
|
1399 | @LoginRequired() | |
1396 | @HasPermissionAllDecorator('hg.admin') |
|
1400 | @HasPermissionAllDecorator('hg.admin') | |
1397 | @CSRFRequired() |
|
1401 | @CSRFRequired() | |
1398 | @view_config( |
|
1402 | @view_config( | |
1399 | route_name='edit_user_caches_update', request_method='POST') |
|
1403 | route_name='edit_user_caches_update', request_method='POST') | |
1400 | def user_caches_update(self): |
|
1404 | def user_caches_update(self): | |
1401 | _ = self.request.translate |
|
1405 | _ = self.request.translate | |
1402 | c = self.load_default_context() |
|
1406 | c = self.load_default_context() | |
1403 | c.user = self.db_user |
|
1407 | c.user = self.db_user | |
1404 |
|
1408 | |||
1405 | c.active = 'caches' |
|
1409 | c.active = 'caches' | |
1406 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) |
|
1410 | c.perm_user = c.user.AuthUser(ip_addr=self.request.remote_addr) | |
1407 |
|
1411 | |||
1408 | cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id) |
|
1412 | cache_namespace_uid = 'cache_user_auth.{}'.format(self.db_user.user_id) | |
1409 | del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid) |
|
1413 | del_keys = rc_cache.clear_cache_namespace('cache_perms', cache_namespace_uid) | |
1410 |
|
1414 | |||
1411 | h.flash(_("Deleted {} cache keys").format(del_keys), category='success') |
|
1415 | h.flash(_("Deleted {} cache keys").format(del_keys), category='success') | |
1412 |
|
1416 | |||
1413 | return HTTPFound(h.route_path( |
|
1417 | return HTTPFound(h.route_path( | |
1414 | 'edit_user_caches', user_id=c.user.user_id)) |
|
1418 | 'edit_user_caches', user_id=c.user.user_id)) |
@@ -1,768 +1,766 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2010-2020 RhodeCode GmbH |
|
3 | # Copyright (C) 2010-2020 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 os |
|
21 | import os | |
22 | import sys |
|
22 | import sys | |
23 | import logging |
|
23 | import logging | |
24 | import collections |
|
24 | import collections | |
25 | import tempfile |
|
25 | import tempfile | |
26 | import time |
|
26 | import time | |
27 |
|
27 | |||
28 | from paste.gzipper import make_gzip_middleware |
|
28 | from paste.gzipper import make_gzip_middleware | |
29 | import pyramid.events |
|
29 | import pyramid.events | |
30 | from pyramid.wsgi import wsgiapp |
|
30 | from pyramid.wsgi import wsgiapp | |
31 | from pyramid.authorization import ACLAuthorizationPolicy |
|
31 | from pyramid.authorization import ACLAuthorizationPolicy | |
32 | from pyramid.config import Configurator |
|
32 | from pyramid.config import Configurator | |
33 | from pyramid.settings import asbool, aslist |
|
33 | from pyramid.settings import asbool, aslist | |
34 | from pyramid.httpexceptions import ( |
|
34 | from pyramid.httpexceptions import ( | |
35 | HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound) |
|
35 | HTTPException, HTTPError, HTTPInternalServerError, HTTPFound, HTTPNotFound) | |
36 | from pyramid.renderers import render_to_response |
|
36 | from pyramid.renderers import render_to_response | |
37 |
|
37 | |||
38 | from rhodecode.model import meta |
|
38 | from rhodecode.model import meta | |
39 | from rhodecode.config import patches |
|
39 | from rhodecode.config import patches | |
40 | from rhodecode.config import utils as config_utils |
|
40 | from rhodecode.config import utils as config_utils | |
41 | from rhodecode.config.environment import load_pyramid_environment |
|
41 | from rhodecode.config.environment import load_pyramid_environment | |
42 |
|
42 | |||
43 | import rhodecode.events |
|
43 | import rhodecode.events | |
44 | from rhodecode.lib.middleware.vcs import VCSMiddleware |
|
44 | from rhodecode.lib.middleware.vcs import VCSMiddleware | |
45 | from rhodecode.lib.request import Request |
|
45 | from rhodecode.lib.request import Request | |
46 | from rhodecode.lib.vcs import VCSCommunicationError |
|
46 | from rhodecode.lib.vcs import VCSCommunicationError | |
47 | from rhodecode.lib.exceptions import VCSServerUnavailable |
|
47 | from rhodecode.lib.exceptions import VCSServerUnavailable | |
48 | from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled |
|
48 | from rhodecode.lib.middleware.appenlight import wrap_in_appenlight_if_enabled | |
49 | from rhodecode.lib.middleware.https_fixup import HttpsFixup |
|
49 | from rhodecode.lib.middleware.https_fixup import HttpsFixup | |
50 | from rhodecode.lib.celerylib.loader import configure_celery |
|
50 | from rhodecode.lib.celerylib.loader import configure_celery | |
51 | from rhodecode.lib.plugins.utils import register_rhodecode_plugin |
|
51 | from rhodecode.lib.plugins.utils import register_rhodecode_plugin | |
52 | from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict |
|
52 | from rhodecode.lib.utils2 import aslist as rhodecode_aslist, AttributeDict | |
53 | from rhodecode.lib.exc_tracking import store_exception |
|
53 | from rhodecode.lib.exc_tracking import store_exception | |
54 | from rhodecode.subscribers import ( |
|
54 | from rhodecode.subscribers import ( | |
55 | scan_repositories_if_enabled, write_js_routes_if_enabled, |
|
55 | scan_repositories_if_enabled, write_js_routes_if_enabled, | |
56 |
write_metadata_if_needed, write_usage_data |
|
56 | write_metadata_if_needed, write_usage_data) | |
57 |
|
57 | |||
58 |
|
58 | |||
59 | log = logging.getLogger(__name__) |
|
59 | log = logging.getLogger(__name__) | |
60 |
|
60 | |||
61 |
|
61 | |||
62 | def is_http_error(response): |
|
62 | def is_http_error(response): | |
63 | # error which should have traceback |
|
63 | # error which should have traceback | |
64 | return response.status_code > 499 |
|
64 | return response.status_code > 499 | |
65 |
|
65 | |||
66 |
|
66 | |||
67 | def should_load_all(): |
|
67 | def should_load_all(): | |
68 | """ |
|
68 | """ | |
69 | Returns if all application components should be loaded. In some cases it's |
|
69 | Returns if all application components should be loaded. In some cases it's | |
70 | desired to skip apps loading for faster shell script execution |
|
70 | desired to skip apps loading for faster shell script execution | |
71 | """ |
|
71 | """ | |
72 | ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER') |
|
72 | ssh_cmd = os.environ.get('RC_CMD_SSH_WRAPPER') | |
73 | if ssh_cmd: |
|
73 | if ssh_cmd: | |
74 | return False |
|
74 | return False | |
75 |
|
75 | |||
76 | return True |
|
76 | return True | |
77 |
|
77 | |||
78 |
|
78 | |||
79 | def make_pyramid_app(global_config, **settings): |
|
79 | def make_pyramid_app(global_config, **settings): | |
80 | """ |
|
80 | """ | |
81 | Constructs the WSGI application based on Pyramid. |
|
81 | Constructs the WSGI application based on Pyramid. | |
82 |
|
82 | |||
83 | Specials: |
|
83 | Specials: | |
84 |
|
84 | |||
85 | * The application can also be integrated like a plugin via the call to |
|
85 | * The application can also be integrated like a plugin via the call to | |
86 | `includeme`. This is accompanied with the other utility functions which |
|
86 | `includeme`. This is accompanied with the other utility functions which | |
87 | are called. Changing this should be done with great care to not break |
|
87 | are called. Changing this should be done with great care to not break | |
88 | cases when these fragments are assembled from another place. |
|
88 | cases when these fragments are assembled from another place. | |
89 |
|
89 | |||
90 | """ |
|
90 | """ | |
91 |
|
91 | |||
92 | # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It |
|
92 | # Allows to use format style "{ENV_NAME}" placeholders in the configuration. It | |
93 | # will be replaced by the value of the environment variable "NAME" in this case. |
|
93 | # will be replaced by the value of the environment variable "NAME" in this case. | |
94 | start_time = time.time() |
|
94 | start_time = time.time() | |
95 |
|
95 | |||
96 | debug = asbool(global_config.get('debug')) |
|
96 | debug = asbool(global_config.get('debug')) | |
97 | if debug: |
|
97 | if debug: | |
98 | enable_debug() |
|
98 | enable_debug() | |
99 |
|
99 | |||
100 | environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()} |
|
100 | environ = {'ENV_{}'.format(key): value for key, value in os.environ.items()} | |
101 |
|
101 | |||
102 | global_config = _substitute_values(global_config, environ) |
|
102 | global_config = _substitute_values(global_config, environ) | |
103 | settings = _substitute_values(settings, environ) |
|
103 | settings = _substitute_values(settings, environ) | |
104 |
|
104 | |||
105 | sanitize_settings_and_apply_defaults(global_config, settings) |
|
105 | sanitize_settings_and_apply_defaults(global_config, settings) | |
106 |
|
106 | |||
107 | config = Configurator(settings=settings) |
|
107 | config = Configurator(settings=settings) | |
108 |
|
108 | |||
109 | # Apply compatibility patches |
|
109 | # Apply compatibility patches | |
110 | patches.inspect_getargspec() |
|
110 | patches.inspect_getargspec() | |
111 |
|
111 | |||
112 | load_pyramid_environment(global_config, settings) |
|
112 | load_pyramid_environment(global_config, settings) | |
113 |
|
113 | |||
114 | # Static file view comes first |
|
114 | # Static file view comes first | |
115 | includeme_first(config) |
|
115 | includeme_first(config) | |
116 |
|
116 | |||
117 | includeme(config) |
|
117 | includeme(config) | |
118 |
|
118 | |||
119 | pyramid_app = config.make_wsgi_app() |
|
119 | pyramid_app = config.make_wsgi_app() | |
120 | pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config) |
|
120 | pyramid_app = wrap_app_in_wsgi_middlewares(pyramid_app, config) | |
121 | pyramid_app.config = config |
|
121 | pyramid_app.config = config | |
122 |
|
122 | |||
123 | config.configure_celery(global_config['__file__']) |
|
123 | config.configure_celery(global_config['__file__']) | |
124 | # creating the app uses a connection - return it after we are done |
|
124 | # creating the app uses a connection - return it after we are done | |
125 | meta.Session.remove() |
|
125 | meta.Session.remove() | |
126 | total_time = time.time() - start_time |
|
126 | total_time = time.time() - start_time | |
127 | log.info('Pyramid app `%s` created and configured in %.2fs', |
|
127 | log.info('Pyramid app `%s` created and configured in %.2fs', | |
128 | pyramid_app.func_name, total_time) |
|
128 | pyramid_app.func_name, total_time) | |
129 |
|
129 | |||
130 | return pyramid_app |
|
130 | return pyramid_app | |
131 |
|
131 | |||
132 |
|
132 | |||
133 | def not_found_view(request): |
|
133 | def not_found_view(request): | |
134 | """ |
|
134 | """ | |
135 | This creates the view which should be registered as not-found-view to |
|
135 | This creates the view which should be registered as not-found-view to | |
136 | pyramid. |
|
136 | pyramid. | |
137 | """ |
|
137 | """ | |
138 |
|
138 | |||
139 | if not getattr(request, 'vcs_call', None): |
|
139 | if not getattr(request, 'vcs_call', None): | |
140 | # handle like regular case with our error_handler |
|
140 | # handle like regular case with our error_handler | |
141 | return error_handler(HTTPNotFound(), request) |
|
141 | return error_handler(HTTPNotFound(), request) | |
142 |
|
142 | |||
143 | # handle not found view as a vcs call |
|
143 | # handle not found view as a vcs call | |
144 | settings = request.registry.settings |
|
144 | settings = request.registry.settings | |
145 | ae_client = getattr(request, 'ae_client', None) |
|
145 | ae_client = getattr(request, 'ae_client', None) | |
146 | vcs_app = VCSMiddleware( |
|
146 | vcs_app = VCSMiddleware( | |
147 | HTTPNotFound(), request.registry, settings, |
|
147 | HTTPNotFound(), request.registry, settings, | |
148 | appenlight_client=ae_client) |
|
148 | appenlight_client=ae_client) | |
149 |
|
149 | |||
150 | return wsgiapp(vcs_app)(None, request) |
|
150 | return wsgiapp(vcs_app)(None, request) | |
151 |
|
151 | |||
152 |
|
152 | |||
153 | def error_handler(exception, request): |
|
153 | def error_handler(exception, request): | |
154 | import rhodecode |
|
154 | import rhodecode | |
155 | from rhodecode.lib import helpers |
|
155 | from rhodecode.lib import helpers | |
156 |
|
156 | |||
157 | rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode' |
|
157 | rhodecode_title = rhodecode.CONFIG.get('rhodecode_title') or 'RhodeCode' | |
158 |
|
158 | |||
159 | base_response = HTTPInternalServerError() |
|
159 | base_response = HTTPInternalServerError() | |
160 | # prefer original exception for the response since it may have headers set |
|
160 | # prefer original exception for the response since it may have headers set | |
161 | if isinstance(exception, HTTPException): |
|
161 | if isinstance(exception, HTTPException): | |
162 | base_response = exception |
|
162 | base_response = exception | |
163 | elif isinstance(exception, VCSCommunicationError): |
|
163 | elif isinstance(exception, VCSCommunicationError): | |
164 | base_response = VCSServerUnavailable() |
|
164 | base_response = VCSServerUnavailable() | |
165 |
|
165 | |||
166 | if is_http_error(base_response): |
|
166 | if is_http_error(base_response): | |
167 | log.exception( |
|
167 | log.exception( | |
168 | 'error occurred handling this request for path: %s', request.path) |
|
168 | 'error occurred handling this request for path: %s', request.path) | |
169 |
|
169 | |||
170 | error_explanation = base_response.explanation or str(base_response) |
|
170 | error_explanation = base_response.explanation or str(base_response) | |
171 | if base_response.status_code == 404: |
|
171 | if base_response.status_code == 404: | |
172 | error_explanation += " Optionally you don't have permission to access this page." |
|
172 | error_explanation += " Optionally you don't have permission to access this page." | |
173 | c = AttributeDict() |
|
173 | c = AttributeDict() | |
174 | c.error_message = base_response.status |
|
174 | c.error_message = base_response.status | |
175 | c.error_explanation = error_explanation |
|
175 | c.error_explanation = error_explanation | |
176 | c.visual = AttributeDict() |
|
176 | c.visual = AttributeDict() | |
177 |
|
177 | |||
178 | c.visual.rhodecode_support_url = ( |
|
178 | c.visual.rhodecode_support_url = ( | |
179 | request.registry.settings.get('rhodecode_support_url') or |
|
179 | request.registry.settings.get('rhodecode_support_url') or | |
180 | request.route_url('rhodecode_support') |
|
180 | request.route_url('rhodecode_support') | |
181 | ) |
|
181 | ) | |
182 | c.redirect_time = 0 |
|
182 | c.redirect_time = 0 | |
183 | c.rhodecode_name = rhodecode_title |
|
183 | c.rhodecode_name = rhodecode_title | |
184 | if not c.rhodecode_name: |
|
184 | if not c.rhodecode_name: | |
185 | c.rhodecode_name = 'Rhodecode' |
|
185 | c.rhodecode_name = 'Rhodecode' | |
186 |
|
186 | |||
187 | c.causes = [] |
|
187 | c.causes = [] | |
188 | if is_http_error(base_response): |
|
188 | if is_http_error(base_response): | |
189 | c.causes.append('Server is overloaded.') |
|
189 | c.causes.append('Server is overloaded.') | |
190 | c.causes.append('Server database connection is lost.') |
|
190 | c.causes.append('Server database connection is lost.') | |
191 | c.causes.append('Server expected unhandled error.') |
|
191 | c.causes.append('Server expected unhandled error.') | |
192 |
|
192 | |||
193 | if hasattr(base_response, 'causes'): |
|
193 | if hasattr(base_response, 'causes'): | |
194 | c.causes = base_response.causes |
|
194 | c.causes = base_response.causes | |
195 |
|
195 | |||
196 | c.messages = helpers.flash.pop_messages(request=request) |
|
196 | c.messages = helpers.flash.pop_messages(request=request) | |
197 |
|
197 | |||
198 | exc_info = sys.exc_info() |
|
198 | exc_info = sys.exc_info() | |
199 | c.exception_id = id(exc_info) |
|
199 | c.exception_id = id(exc_info) | |
200 | c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \ |
|
200 | c.show_exception_id = isinstance(base_response, VCSServerUnavailable) \ | |
201 | or base_response.status_code > 499 |
|
201 | or base_response.status_code > 499 | |
202 | c.exception_id_url = request.route_url( |
|
202 | c.exception_id_url = request.route_url( | |
203 | 'admin_settings_exception_tracker_show', exception_id=c.exception_id) |
|
203 | 'admin_settings_exception_tracker_show', exception_id=c.exception_id) | |
204 |
|
204 | |||
205 | if c.show_exception_id: |
|
205 | if c.show_exception_id: | |
206 | store_exception(c.exception_id, exc_info) |
|
206 | store_exception(c.exception_id, exc_info) | |
207 |
|
207 | |||
208 | response = render_to_response( |
|
208 | response = render_to_response( | |
209 | '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request, |
|
209 | '/errors/error_document.mako', {'c': c, 'h': helpers}, request=request, | |
210 | response=base_response) |
|
210 | response=base_response) | |
211 |
|
211 | |||
212 | return response |
|
212 | return response | |
213 |
|
213 | |||
214 |
|
214 | |||
215 | def includeme_first(config): |
|
215 | def includeme_first(config): | |
216 | # redirect automatic browser favicon.ico requests to correct place |
|
216 | # redirect automatic browser favicon.ico requests to correct place | |
217 | def favicon_redirect(context, request): |
|
217 | def favicon_redirect(context, request): | |
218 | return HTTPFound( |
|
218 | return HTTPFound( | |
219 | request.static_path('rhodecode:public/images/favicon.ico')) |
|
219 | request.static_path('rhodecode:public/images/favicon.ico')) | |
220 |
|
220 | |||
221 | config.add_view(favicon_redirect, route_name='favicon') |
|
221 | config.add_view(favicon_redirect, route_name='favicon') | |
222 | config.add_route('favicon', '/favicon.ico') |
|
222 | config.add_route('favicon', '/favicon.ico') | |
223 |
|
223 | |||
224 | def robots_redirect(context, request): |
|
224 | def robots_redirect(context, request): | |
225 | return HTTPFound( |
|
225 | return HTTPFound( | |
226 | request.static_path('rhodecode:public/robots.txt')) |
|
226 | request.static_path('rhodecode:public/robots.txt')) | |
227 |
|
227 | |||
228 | config.add_view(robots_redirect, route_name='robots') |
|
228 | config.add_view(robots_redirect, route_name='robots') | |
229 | config.add_route('robots', '/robots.txt') |
|
229 | config.add_route('robots', '/robots.txt') | |
230 |
|
230 | |||
231 | config.add_static_view( |
|
231 | config.add_static_view( | |
232 | '_static/deform', 'deform:static') |
|
232 | '_static/deform', 'deform:static') | |
233 | config.add_static_view( |
|
233 | config.add_static_view( | |
234 | '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24) |
|
234 | '_static/rhodecode', path='rhodecode:public', cache_max_age=3600 * 24) | |
235 |
|
235 | |||
236 |
|
236 | |||
237 | def includeme(config): |
|
237 | def includeme(config): | |
238 | log.debug('Initializing main includeme from %s', os.path.basename(__file__)) |
|
238 | log.debug('Initializing main includeme from %s', os.path.basename(__file__)) | |
239 | settings = config.registry.settings |
|
239 | settings = config.registry.settings | |
240 | config.set_request_factory(Request) |
|
240 | config.set_request_factory(Request) | |
241 |
|
241 | |||
242 | # plugin information |
|
242 | # plugin information | |
243 | config.registry.rhodecode_plugins = collections.OrderedDict() |
|
243 | config.registry.rhodecode_plugins = collections.OrderedDict() | |
244 |
|
244 | |||
245 | config.add_directive( |
|
245 | config.add_directive( | |
246 | 'register_rhodecode_plugin', register_rhodecode_plugin) |
|
246 | 'register_rhodecode_plugin', register_rhodecode_plugin) | |
247 |
|
247 | |||
248 | config.add_directive('configure_celery', configure_celery) |
|
248 | config.add_directive('configure_celery', configure_celery) | |
249 |
|
249 | |||
250 | if asbool(settings.get('appenlight', 'false')): |
|
250 | if asbool(settings.get('appenlight', 'false')): | |
251 | config.include('appenlight_client.ext.pyramid_tween') |
|
251 | config.include('appenlight_client.ext.pyramid_tween') | |
252 |
|
252 | |||
253 | load_all = should_load_all() |
|
253 | load_all = should_load_all() | |
254 |
|
254 | |||
255 | # Includes which are required. The application would fail without them. |
|
255 | # Includes which are required. The application would fail without them. | |
256 | config.include('pyramid_mako') |
|
256 | config.include('pyramid_mako') | |
257 | config.include('rhodecode.lib.rc_beaker') |
|
257 | config.include('rhodecode.lib.rc_beaker') | |
258 | config.include('rhodecode.lib.rc_cache') |
|
258 | config.include('rhodecode.lib.rc_cache') | |
259 |
|
259 | |||
260 | config.include('rhodecode.apps._base.navigation') |
|
260 | config.include('rhodecode.apps._base.navigation') | |
261 | config.include('rhodecode.apps._base.subscribers') |
|
261 | config.include('rhodecode.apps._base.subscribers') | |
262 | config.include('rhodecode.tweens') |
|
262 | config.include('rhodecode.tweens') | |
263 | config.include('rhodecode.authentication') |
|
263 | config.include('rhodecode.authentication') | |
264 |
|
264 | |||
265 | if load_all: |
|
265 | if load_all: | |
266 | config.include('rhodecode.integrations') |
|
266 | config.include('rhodecode.integrations') | |
267 |
|
267 | |||
268 | if load_all: |
|
268 | if load_all: | |
269 | # load CE authentication plugins |
|
269 | # load CE authentication plugins | |
270 | config.include('rhodecode.authentication.plugins.auth_crowd') |
|
270 | config.include('rhodecode.authentication.plugins.auth_crowd') | |
271 | config.include('rhodecode.authentication.plugins.auth_headers') |
|
271 | config.include('rhodecode.authentication.plugins.auth_headers') | |
272 | config.include('rhodecode.authentication.plugins.auth_jasig_cas') |
|
272 | config.include('rhodecode.authentication.plugins.auth_jasig_cas') | |
273 | config.include('rhodecode.authentication.plugins.auth_ldap') |
|
273 | config.include('rhodecode.authentication.plugins.auth_ldap') | |
274 | config.include('rhodecode.authentication.plugins.auth_pam') |
|
274 | config.include('rhodecode.authentication.plugins.auth_pam') | |
275 | config.include('rhodecode.authentication.plugins.auth_rhodecode') |
|
275 | config.include('rhodecode.authentication.plugins.auth_rhodecode') | |
276 | config.include('rhodecode.authentication.plugins.auth_token') |
|
276 | config.include('rhodecode.authentication.plugins.auth_token') | |
277 |
|
277 | |||
278 | # Auto discover authentication plugins and include their configuration. |
|
278 | # Auto discover authentication plugins and include their configuration. | |
279 | if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')): |
|
279 | if asbool(settings.get('auth_plugin.import_legacy_plugins', 'true')): | |
280 | from rhodecode.authentication import discover_legacy_plugins |
|
280 | from rhodecode.authentication import discover_legacy_plugins | |
281 | discover_legacy_plugins(config) |
|
281 | discover_legacy_plugins(config) | |
282 |
|
282 | |||
283 | # apps |
|
283 | # apps | |
284 | if load_all: |
|
284 | if load_all: | |
285 | config.include('rhodecode.apps._base') |
|
285 | config.include('rhodecode.apps._base') | |
286 | config.include('rhodecode.apps.hovercards') |
|
286 | config.include('rhodecode.apps.hovercards') | |
287 | config.include('rhodecode.apps.ops') |
|
287 | config.include('rhodecode.apps.ops') | |
288 | config.include('rhodecode.apps.admin') |
|
288 | config.include('rhodecode.apps.admin') | |
289 | config.include('rhodecode.apps.channelstream') |
|
289 | config.include('rhodecode.apps.channelstream') | |
290 | config.include('rhodecode.apps.file_store') |
|
290 | config.include('rhodecode.apps.file_store') | |
291 | config.include('rhodecode.apps.login') |
|
291 | config.include('rhodecode.apps.login') | |
292 | config.include('rhodecode.apps.home') |
|
292 | config.include('rhodecode.apps.home') | |
293 | config.include('rhodecode.apps.journal') |
|
293 | config.include('rhodecode.apps.journal') | |
294 | config.include('rhodecode.apps.repository') |
|
294 | config.include('rhodecode.apps.repository') | |
295 | config.include('rhodecode.apps.repo_group') |
|
295 | config.include('rhodecode.apps.repo_group') | |
296 | config.include('rhodecode.apps.user_group') |
|
296 | config.include('rhodecode.apps.user_group') | |
297 | config.include('rhodecode.apps.search') |
|
297 | config.include('rhodecode.apps.search') | |
298 | config.include('rhodecode.apps.user_profile') |
|
298 | config.include('rhodecode.apps.user_profile') | |
299 | config.include('rhodecode.apps.user_group_profile') |
|
299 | config.include('rhodecode.apps.user_group_profile') | |
300 | config.include('rhodecode.apps.my_account') |
|
300 | config.include('rhodecode.apps.my_account') | |
301 | config.include('rhodecode.apps.svn_support') |
|
301 | config.include('rhodecode.apps.svn_support') | |
302 | config.include('rhodecode.apps.ssh_support') |
|
302 | config.include('rhodecode.apps.ssh_support') | |
303 | config.include('rhodecode.apps.gist') |
|
303 | config.include('rhodecode.apps.gist') | |
304 | config.include('rhodecode.apps.debug_style') |
|
304 | config.include('rhodecode.apps.debug_style') | |
305 | config.include('rhodecode.api') |
|
305 | config.include('rhodecode.api') | |
306 |
|
306 | |||
307 | config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True) |
|
307 | config.add_route('rhodecode_support', 'https://rhodecode.com/help/', static=True) | |
308 | config.add_translation_dirs('rhodecode:i18n/') |
|
308 | config.add_translation_dirs('rhodecode:i18n/') | |
309 | settings['default_locale_name'] = settings.get('lang', 'en') |
|
309 | settings['default_locale_name'] = settings.get('lang', 'en') | |
310 |
|
310 | |||
311 | # Add subscribers. |
|
311 | # Add subscribers. | |
312 | if load_all: |
|
312 | if load_all: | |
313 | config.add_subscriber(inject_app_settings, |
|
|||
314 | pyramid.events.ApplicationCreated) |
|
|||
315 | config.add_subscriber(scan_repositories_if_enabled, |
|
313 | config.add_subscriber(scan_repositories_if_enabled, | |
316 | pyramid.events.ApplicationCreated) |
|
314 | pyramid.events.ApplicationCreated) | |
317 | config.add_subscriber(write_metadata_if_needed, |
|
315 | config.add_subscriber(write_metadata_if_needed, | |
318 | pyramid.events.ApplicationCreated) |
|
316 | pyramid.events.ApplicationCreated) | |
319 | config.add_subscriber(write_usage_data, |
|
317 | config.add_subscriber(write_usage_data, | |
320 | pyramid.events.ApplicationCreated) |
|
318 | pyramid.events.ApplicationCreated) | |
321 | config.add_subscriber(write_js_routes_if_enabled, |
|
319 | config.add_subscriber(write_js_routes_if_enabled, | |
322 | pyramid.events.ApplicationCreated) |
|
320 | pyramid.events.ApplicationCreated) | |
323 |
|
321 | |||
324 | # request custom methods |
|
322 | # request custom methods | |
325 | config.add_request_method( |
|
323 | config.add_request_method( | |
326 | 'rhodecode.lib.partial_renderer.get_partial_renderer', |
|
324 | 'rhodecode.lib.partial_renderer.get_partial_renderer', | |
327 | 'get_partial_renderer') |
|
325 | 'get_partial_renderer') | |
328 |
|
326 | |||
329 | config.add_request_method( |
|
327 | config.add_request_method( | |
330 | 'rhodecode.lib.request_counter.get_request_counter', |
|
328 | 'rhodecode.lib.request_counter.get_request_counter', | |
331 | 'request_count') |
|
329 | 'request_count') | |
332 |
|
330 | |||
333 | # Set the authorization policy. |
|
331 | # Set the authorization policy. | |
334 | authz_policy = ACLAuthorizationPolicy() |
|
332 | authz_policy = ACLAuthorizationPolicy() | |
335 | config.set_authorization_policy(authz_policy) |
|
333 | config.set_authorization_policy(authz_policy) | |
336 |
|
334 | |||
337 | # Set the default renderer for HTML templates to mako. |
|
335 | # Set the default renderer for HTML templates to mako. | |
338 | config.add_mako_renderer('.html') |
|
336 | config.add_mako_renderer('.html') | |
339 |
|
337 | |||
340 | config.add_renderer( |
|
338 | config.add_renderer( | |
341 | name='json_ext', |
|
339 | name='json_ext', | |
342 | factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json') |
|
340 | factory='rhodecode.lib.ext_json_renderer.pyramid_ext_json') | |
343 |
|
341 | |||
344 | config.add_renderer( |
|
342 | config.add_renderer( | |
345 | name='string_html', |
|
343 | name='string_html', | |
346 | factory='rhodecode.lib.string_renderer.html') |
|
344 | factory='rhodecode.lib.string_renderer.html') | |
347 |
|
345 | |||
348 | # include RhodeCode plugins |
|
346 | # include RhodeCode plugins | |
349 | includes = aslist(settings.get('rhodecode.includes', [])) |
|
347 | includes = aslist(settings.get('rhodecode.includes', [])) | |
350 | for inc in includes: |
|
348 | for inc in includes: | |
351 | config.include(inc) |
|
349 | config.include(inc) | |
352 |
|
350 | |||
353 | # custom not found view, if our pyramid app doesn't know how to handle |
|
351 | # custom not found view, if our pyramid app doesn't know how to handle | |
354 | # the request pass it to potential VCS handling ap |
|
352 | # the request pass it to potential VCS handling ap | |
355 | config.add_notfound_view(not_found_view) |
|
353 | config.add_notfound_view(not_found_view) | |
356 | if not settings.get('debugtoolbar.enabled', False): |
|
354 | if not settings.get('debugtoolbar.enabled', False): | |
357 | # disabled debugtoolbar handle all exceptions via the error_handlers |
|
355 | # disabled debugtoolbar handle all exceptions via the error_handlers | |
358 | config.add_view(error_handler, context=Exception) |
|
356 | config.add_view(error_handler, context=Exception) | |
359 |
|
357 | |||
360 | # all errors including 403/404/50X |
|
358 | # all errors including 403/404/50X | |
361 | config.add_view(error_handler, context=HTTPError) |
|
359 | config.add_view(error_handler, context=HTTPError) | |
362 |
|
360 | |||
363 |
|
361 | |||
364 | def wrap_app_in_wsgi_middlewares(pyramid_app, config): |
|
362 | def wrap_app_in_wsgi_middlewares(pyramid_app, config): | |
365 | """ |
|
363 | """ | |
366 | Apply outer WSGI middlewares around the application. |
|
364 | Apply outer WSGI middlewares around the application. | |
367 | """ |
|
365 | """ | |
368 | registry = config.registry |
|
366 | registry = config.registry | |
369 | settings = registry.settings |
|
367 | settings = registry.settings | |
370 |
|
368 | |||
371 | # enable https redirects based on HTTP_X_URL_SCHEME set by proxy |
|
369 | # enable https redirects based on HTTP_X_URL_SCHEME set by proxy | |
372 | pyramid_app = HttpsFixup(pyramid_app, settings) |
|
370 | pyramid_app = HttpsFixup(pyramid_app, settings) | |
373 |
|
371 | |||
374 | pyramid_app, _ae_client = wrap_in_appenlight_if_enabled( |
|
372 | pyramid_app, _ae_client = wrap_in_appenlight_if_enabled( | |
375 | pyramid_app, settings) |
|
373 | pyramid_app, settings) | |
376 | registry.ae_client = _ae_client |
|
374 | registry.ae_client = _ae_client | |
377 |
|
375 | |||
378 | if settings['gzip_responses']: |
|
376 | if settings['gzip_responses']: | |
379 | pyramid_app = make_gzip_middleware( |
|
377 | pyramid_app = make_gzip_middleware( | |
380 | pyramid_app, settings, compress_level=1) |
|
378 | pyramid_app, settings, compress_level=1) | |
381 |
|
379 | |||
382 | # this should be the outer most middleware in the wsgi stack since |
|
380 | # this should be the outer most middleware in the wsgi stack since | |
383 | # middleware like Routes make database calls |
|
381 | # middleware like Routes make database calls | |
384 | def pyramid_app_with_cleanup(environ, start_response): |
|
382 | def pyramid_app_with_cleanup(environ, start_response): | |
385 | try: |
|
383 | try: | |
386 | return pyramid_app(environ, start_response) |
|
384 | return pyramid_app(environ, start_response) | |
387 | finally: |
|
385 | finally: | |
388 | # Dispose current database session and rollback uncommitted |
|
386 | # Dispose current database session and rollback uncommitted | |
389 | # transactions. |
|
387 | # transactions. | |
390 | meta.Session.remove() |
|
388 | meta.Session.remove() | |
391 |
|
389 | |||
392 | # In a single threaded mode server, on non sqlite db we should have |
|
390 | # In a single threaded mode server, on non sqlite db we should have | |
393 | # '0 Current Checked out connections' at the end of a request, |
|
391 | # '0 Current Checked out connections' at the end of a request, | |
394 | # if not, then something, somewhere is leaving a connection open |
|
392 | # if not, then something, somewhere is leaving a connection open | |
395 | pool = meta.Base.metadata.bind.engine.pool |
|
393 | pool = meta.Base.metadata.bind.engine.pool | |
396 | log.debug('sa pool status: %s', pool.status()) |
|
394 | log.debug('sa pool status: %s', pool.status()) | |
397 | log.debug('Request processing finalized') |
|
395 | log.debug('Request processing finalized') | |
398 |
|
396 | |||
399 | return pyramid_app_with_cleanup |
|
397 | return pyramid_app_with_cleanup | |
400 |
|
398 | |||
401 |
|
399 | |||
402 | def sanitize_settings_and_apply_defaults(global_config, settings): |
|
400 | def sanitize_settings_and_apply_defaults(global_config, settings): | |
403 | """ |
|
401 | """ | |
404 | Applies settings defaults and does all type conversion. |
|
402 | Applies settings defaults and does all type conversion. | |
405 |
|
403 | |||
406 | We would move all settings parsing and preparation into this place, so that |
|
404 | We would move all settings parsing and preparation into this place, so that | |
407 | we have only one place left which deals with this part. The remaining parts |
|
405 | we have only one place left which deals with this part. The remaining parts | |
408 | of the application would start to rely fully on well prepared settings. |
|
406 | of the application would start to rely fully on well prepared settings. | |
409 |
|
407 | |||
410 | This piece would later be split up per topic to avoid a big fat monster |
|
408 | This piece would later be split up per topic to avoid a big fat monster | |
411 | function. |
|
409 | function. | |
412 | """ |
|
410 | """ | |
413 |
|
411 | |||
414 | settings.setdefault('rhodecode.edition', 'Community Edition') |
|
412 | settings.setdefault('rhodecode.edition', 'Community Edition') | |
415 | settings.setdefault('rhodecode.edition_id', 'CE') |
|
413 | settings.setdefault('rhodecode.edition_id', 'CE') | |
416 |
|
414 | |||
417 | if 'mako.default_filters' not in settings: |
|
415 | if 'mako.default_filters' not in settings: | |
418 | # set custom default filters if we don't have it defined |
|
416 | # set custom default filters if we don't have it defined | |
419 | settings['mako.imports'] = 'from rhodecode.lib.base import h_filter' |
|
417 | settings['mako.imports'] = 'from rhodecode.lib.base import h_filter' | |
420 | settings['mako.default_filters'] = 'h_filter' |
|
418 | settings['mako.default_filters'] = 'h_filter' | |
421 |
|
419 | |||
422 | if 'mako.directories' not in settings: |
|
420 | if 'mako.directories' not in settings: | |
423 | mako_directories = settings.setdefault('mako.directories', [ |
|
421 | mako_directories = settings.setdefault('mako.directories', [ | |
424 | # Base templates of the original application |
|
422 | # Base templates of the original application | |
425 | 'rhodecode:templates', |
|
423 | 'rhodecode:templates', | |
426 | ]) |
|
424 | ]) | |
427 | log.debug( |
|
425 | log.debug( | |
428 | "Using the following Mako template directories: %s", |
|
426 | "Using the following Mako template directories: %s", | |
429 | mako_directories) |
|
427 | mako_directories) | |
430 |
|
428 | |||
431 | # NOTE(marcink): fix redis requirement for schema of connection since 3.X |
|
429 | # NOTE(marcink): fix redis requirement for schema of connection since 3.X | |
432 | if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis': |
|
430 | if 'beaker.session.type' in settings and settings['beaker.session.type'] == 'ext:redis': | |
433 | raw_url = settings['beaker.session.url'] |
|
431 | raw_url = settings['beaker.session.url'] | |
434 | if not raw_url.startswith(('redis://', 'rediss://', 'unix://')): |
|
432 | if not raw_url.startswith(('redis://', 'rediss://', 'unix://')): | |
435 | settings['beaker.session.url'] = 'redis://' + raw_url |
|
433 | settings['beaker.session.url'] = 'redis://' + raw_url | |
436 |
|
434 | |||
437 | # Default includes, possible to change as a user |
|
435 | # Default includes, possible to change as a user | |
438 | pyramid_includes = settings.setdefault('pyramid.includes', []) |
|
436 | pyramid_includes = settings.setdefault('pyramid.includes', []) | |
439 | log.debug( |
|
437 | log.debug( | |
440 | "Using the following pyramid.includes: %s", |
|
438 | "Using the following pyramid.includes: %s", | |
441 | pyramid_includes) |
|
439 | pyramid_includes) | |
442 |
|
440 | |||
443 | # TODO: johbo: Re-think this, usually the call to config.include |
|
441 | # TODO: johbo: Re-think this, usually the call to config.include | |
444 | # should allow to pass in a prefix. |
|
442 | # should allow to pass in a prefix. | |
445 | settings.setdefault('rhodecode.api.url', '/_admin/api') |
|
443 | settings.setdefault('rhodecode.api.url', '/_admin/api') | |
446 | settings.setdefault('__file__', global_config.get('__file__')) |
|
444 | settings.setdefault('__file__', global_config.get('__file__')) | |
447 |
|
445 | |||
448 | # Sanitize generic settings. |
|
446 | # Sanitize generic settings. | |
449 | _list_setting(settings, 'default_encoding', 'UTF-8') |
|
447 | _list_setting(settings, 'default_encoding', 'UTF-8') | |
450 | _bool_setting(settings, 'is_test', 'false') |
|
448 | _bool_setting(settings, 'is_test', 'false') | |
451 | _bool_setting(settings, 'gzip_responses', 'false') |
|
449 | _bool_setting(settings, 'gzip_responses', 'false') | |
452 |
|
450 | |||
453 | # Call split out functions that sanitize settings for each topic. |
|
451 | # Call split out functions that sanitize settings for each topic. | |
454 | _sanitize_appenlight_settings(settings) |
|
452 | _sanitize_appenlight_settings(settings) | |
455 | _sanitize_vcs_settings(settings) |
|
453 | _sanitize_vcs_settings(settings) | |
456 | _sanitize_cache_settings(settings) |
|
454 | _sanitize_cache_settings(settings) | |
457 |
|
455 | |||
458 | # configure instance id |
|
456 | # configure instance id | |
459 | config_utils.set_instance_id(settings) |
|
457 | config_utils.set_instance_id(settings) | |
460 |
|
458 | |||
461 | return settings |
|
459 | return settings | |
462 |
|
460 | |||
463 |
|
461 | |||
464 | def enable_debug(): |
|
462 | def enable_debug(): | |
465 | """ |
|
463 | """ | |
466 | Helper to enable debug on running instance |
|
464 | Helper to enable debug on running instance | |
467 | :return: |
|
465 | :return: | |
468 | """ |
|
466 | """ | |
469 | import tempfile |
|
467 | import tempfile | |
470 | import textwrap |
|
468 | import textwrap | |
471 | import logging.config |
|
469 | import logging.config | |
472 |
|
470 | |||
473 | ini_template = textwrap.dedent(""" |
|
471 | ini_template = textwrap.dedent(""" | |
474 | ##################################### |
|
472 | ##################################### | |
475 | ### DEBUG LOGGING CONFIGURATION #### |
|
473 | ### DEBUG LOGGING CONFIGURATION #### | |
476 | ##################################### |
|
474 | ##################################### | |
477 | [loggers] |
|
475 | [loggers] | |
478 | keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper |
|
476 | keys = root, sqlalchemy, beaker, celery, rhodecode, ssh_wrapper | |
479 |
|
477 | |||
480 | [handlers] |
|
478 | [handlers] | |
481 | keys = console, console_sql |
|
479 | keys = console, console_sql | |
482 |
|
480 | |||
483 | [formatters] |
|
481 | [formatters] | |
484 | keys = generic, color_formatter, color_formatter_sql |
|
482 | keys = generic, color_formatter, color_formatter_sql | |
485 |
|
483 | |||
486 | ############# |
|
484 | ############# | |
487 | ## LOGGERS ## |
|
485 | ## LOGGERS ## | |
488 | ############# |
|
486 | ############# | |
489 | [logger_root] |
|
487 | [logger_root] | |
490 | level = NOTSET |
|
488 | level = NOTSET | |
491 | handlers = console |
|
489 | handlers = console | |
492 |
|
490 | |||
493 | [logger_sqlalchemy] |
|
491 | [logger_sqlalchemy] | |
494 | level = INFO |
|
492 | level = INFO | |
495 | handlers = console_sql |
|
493 | handlers = console_sql | |
496 | qualname = sqlalchemy.engine |
|
494 | qualname = sqlalchemy.engine | |
497 | propagate = 0 |
|
495 | propagate = 0 | |
498 |
|
496 | |||
499 | [logger_beaker] |
|
497 | [logger_beaker] | |
500 | level = DEBUG |
|
498 | level = DEBUG | |
501 | handlers = |
|
499 | handlers = | |
502 | qualname = beaker.container |
|
500 | qualname = beaker.container | |
503 | propagate = 1 |
|
501 | propagate = 1 | |
504 |
|
502 | |||
505 | [logger_rhodecode] |
|
503 | [logger_rhodecode] | |
506 | level = DEBUG |
|
504 | level = DEBUG | |
507 | handlers = |
|
505 | handlers = | |
508 | qualname = rhodecode |
|
506 | qualname = rhodecode | |
509 | propagate = 1 |
|
507 | propagate = 1 | |
510 |
|
508 | |||
511 | [logger_ssh_wrapper] |
|
509 | [logger_ssh_wrapper] | |
512 | level = DEBUG |
|
510 | level = DEBUG | |
513 | handlers = |
|
511 | handlers = | |
514 | qualname = ssh_wrapper |
|
512 | qualname = ssh_wrapper | |
515 | propagate = 1 |
|
513 | propagate = 1 | |
516 |
|
514 | |||
517 | [logger_celery] |
|
515 | [logger_celery] | |
518 | level = DEBUG |
|
516 | level = DEBUG | |
519 | handlers = |
|
517 | handlers = | |
520 | qualname = celery |
|
518 | qualname = celery | |
521 |
|
519 | |||
522 |
|
520 | |||
523 | ############## |
|
521 | ############## | |
524 | ## HANDLERS ## |
|
522 | ## HANDLERS ## | |
525 | ############## |
|
523 | ############## | |
526 |
|
524 | |||
527 | [handler_console] |
|
525 | [handler_console] | |
528 | class = StreamHandler |
|
526 | class = StreamHandler | |
529 | args = (sys.stderr, ) |
|
527 | args = (sys.stderr, ) | |
530 | level = DEBUG |
|
528 | level = DEBUG | |
531 | formatter = color_formatter |
|
529 | formatter = color_formatter | |
532 |
|
530 | |||
533 | [handler_console_sql] |
|
531 | [handler_console_sql] | |
534 | # "level = DEBUG" logs SQL queries and results. |
|
532 | # "level = DEBUG" logs SQL queries and results. | |
535 | # "level = INFO" logs SQL queries. |
|
533 | # "level = INFO" logs SQL queries. | |
536 | # "level = WARN" logs neither. (Recommended for production systems.) |
|
534 | # "level = WARN" logs neither. (Recommended for production systems.) | |
537 | class = StreamHandler |
|
535 | class = StreamHandler | |
538 | args = (sys.stderr, ) |
|
536 | args = (sys.stderr, ) | |
539 | level = WARN |
|
537 | level = WARN | |
540 | formatter = color_formatter_sql |
|
538 | formatter = color_formatter_sql | |
541 |
|
539 | |||
542 | ################ |
|
540 | ################ | |
543 | ## FORMATTERS ## |
|
541 | ## FORMATTERS ## | |
544 | ################ |
|
542 | ################ | |
545 |
|
543 | |||
546 | [formatter_generic] |
|
544 | [formatter_generic] | |
547 | class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter |
|
545 | class = rhodecode.lib.logging_formatter.ExceptionAwareFormatter | |
548 | format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s |
|
546 | format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s | |
549 | datefmt = %Y-%m-%d %H:%M:%S |
|
547 | datefmt = %Y-%m-%d %H:%M:%S | |
550 |
|
548 | |||
551 | [formatter_color_formatter] |
|
549 | [formatter_color_formatter] | |
552 | class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter |
|
550 | class = rhodecode.lib.logging_formatter.ColorRequestTrackingFormatter | |
553 | format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s |
|
551 | format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | %(req_id)s | |
554 | datefmt = %Y-%m-%d %H:%M:%S |
|
552 | datefmt = %Y-%m-%d %H:%M:%S | |
555 |
|
553 | |||
556 | [formatter_color_formatter_sql] |
|
554 | [formatter_color_formatter_sql] | |
557 | class = rhodecode.lib.logging_formatter.ColorFormatterSql |
|
555 | class = rhodecode.lib.logging_formatter.ColorFormatterSql | |
558 | format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s |
|
556 | format = %(asctime)s.%(msecs)03d [%(process)d] %(levelname)-5.5s [%(name)s] %(message)s | |
559 | datefmt = %Y-%m-%d %H:%M:%S |
|
557 | datefmt = %Y-%m-%d %H:%M:%S | |
560 | """) |
|
558 | """) | |
561 |
|
559 | |||
562 | with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini', |
|
560 | with tempfile.NamedTemporaryFile(prefix='rc_debug_logging_', suffix='.ini', | |
563 | delete=False) as f: |
|
561 | delete=False) as f: | |
564 | log.info('Saved Temporary DEBUG config at %s', f.name) |
|
562 | log.info('Saved Temporary DEBUG config at %s', f.name) | |
565 | f.write(ini_template) |
|
563 | f.write(ini_template) | |
566 |
|
564 | |||
567 | logging.config.fileConfig(f.name) |
|
565 | logging.config.fileConfig(f.name) | |
568 | log.debug('DEBUG MODE ON') |
|
566 | log.debug('DEBUG MODE ON') | |
569 | os.remove(f.name) |
|
567 | os.remove(f.name) | |
570 |
|
568 | |||
571 |
|
569 | |||
572 | def _sanitize_appenlight_settings(settings): |
|
570 | def _sanitize_appenlight_settings(settings): | |
573 | _bool_setting(settings, 'appenlight', 'false') |
|
571 | _bool_setting(settings, 'appenlight', 'false') | |
574 |
|
572 | |||
575 |
|
573 | |||
576 | def _sanitize_vcs_settings(settings): |
|
574 | def _sanitize_vcs_settings(settings): | |
577 | """ |
|
575 | """ | |
578 | Applies settings defaults and does type conversion for all VCS related |
|
576 | Applies settings defaults and does type conversion for all VCS related | |
579 | settings. |
|
577 | settings. | |
580 | """ |
|
578 | """ | |
581 | _string_setting(settings, 'vcs.svn.compatible_version', '') |
|
579 | _string_setting(settings, 'vcs.svn.compatible_version', '') | |
582 | _string_setting(settings, 'vcs.hooks.protocol', 'http') |
|
580 | _string_setting(settings, 'vcs.hooks.protocol', 'http') | |
583 | _string_setting(settings, 'vcs.hooks.host', '127.0.0.1') |
|
581 | _string_setting(settings, 'vcs.hooks.host', '127.0.0.1') | |
584 | _string_setting(settings, 'vcs.scm_app_implementation', 'http') |
|
582 | _string_setting(settings, 'vcs.scm_app_implementation', 'http') | |
585 | _string_setting(settings, 'vcs.server', '') |
|
583 | _string_setting(settings, 'vcs.server', '') | |
586 | _string_setting(settings, 'vcs.server.protocol', 'http') |
|
584 | _string_setting(settings, 'vcs.server.protocol', 'http') | |
587 | _bool_setting(settings, 'startup.import_repos', 'false') |
|
585 | _bool_setting(settings, 'startup.import_repos', 'false') | |
588 | _bool_setting(settings, 'vcs.hooks.direct_calls', 'false') |
|
586 | _bool_setting(settings, 'vcs.hooks.direct_calls', 'false') | |
589 | _bool_setting(settings, 'vcs.server.enable', 'true') |
|
587 | _bool_setting(settings, 'vcs.server.enable', 'true') | |
590 | _bool_setting(settings, 'vcs.start_server', 'false') |
|
588 | _bool_setting(settings, 'vcs.start_server', 'false') | |
591 | _list_setting(settings, 'vcs.backends', 'hg, git, svn') |
|
589 | _list_setting(settings, 'vcs.backends', 'hg, git, svn') | |
592 | _int_setting(settings, 'vcs.connection_timeout', 3600) |
|
590 | _int_setting(settings, 'vcs.connection_timeout', 3600) | |
593 |
|
591 | |||
594 | # Support legacy values of vcs.scm_app_implementation. Legacy |
|
592 | # Support legacy values of vcs.scm_app_implementation. Legacy | |
595 | # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or |
|
593 | # configurations may use 'rhodecode.lib.middleware.utils.scm_app_http', or | |
596 | # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'. |
|
594 | # disabled since 4.13 'vcsserver.scm_app' which is now mapped to 'http'. | |
597 | scm_app_impl = settings['vcs.scm_app_implementation'] |
|
595 | scm_app_impl = settings['vcs.scm_app_implementation'] | |
598 | if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']: |
|
596 | if scm_app_impl in ['rhodecode.lib.middleware.utils.scm_app_http', 'vcsserver.scm_app']: | |
599 | settings['vcs.scm_app_implementation'] = 'http' |
|
597 | settings['vcs.scm_app_implementation'] = 'http' | |
600 |
|
598 | |||
601 |
|
599 | |||
602 | def _sanitize_cache_settings(settings): |
|
600 | def _sanitize_cache_settings(settings): | |
603 | temp_store = tempfile.gettempdir() |
|
601 | temp_store = tempfile.gettempdir() | |
604 | default_cache_dir = os.path.join(temp_store, 'rc_cache') |
|
602 | default_cache_dir = os.path.join(temp_store, 'rc_cache') | |
605 |
|
603 | |||
606 | # save default, cache dir, and use it for all backends later. |
|
604 | # save default, cache dir, and use it for all backends later. | |
607 | default_cache_dir = _string_setting( |
|
605 | default_cache_dir = _string_setting( | |
608 | settings, |
|
606 | settings, | |
609 | 'cache_dir', |
|
607 | 'cache_dir', | |
610 | default_cache_dir, lower=False, default_when_empty=True) |
|
608 | default_cache_dir, lower=False, default_when_empty=True) | |
611 |
|
609 | |||
612 | # ensure we have our dir created |
|
610 | # ensure we have our dir created | |
613 | if not os.path.isdir(default_cache_dir): |
|
611 | if not os.path.isdir(default_cache_dir): | |
614 | os.makedirs(default_cache_dir, mode=0o755) |
|
612 | os.makedirs(default_cache_dir, mode=0o755) | |
615 |
|
613 | |||
616 | # exception store cache |
|
614 | # exception store cache | |
617 | _string_setting( |
|
615 | _string_setting( | |
618 | settings, |
|
616 | settings, | |
619 | 'exception_tracker.store_path', |
|
617 | 'exception_tracker.store_path', | |
620 | temp_store, lower=False, default_when_empty=True) |
|
618 | temp_store, lower=False, default_when_empty=True) | |
621 | _bool_setting( |
|
619 | _bool_setting( | |
622 | settings, |
|
620 | settings, | |
623 | 'exception_tracker.send_email', |
|
621 | 'exception_tracker.send_email', | |
624 | 'false') |
|
622 | 'false') | |
625 | _string_setting( |
|
623 | _string_setting( | |
626 | settings, |
|
624 | settings, | |
627 | 'exception_tracker.email_prefix', |
|
625 | 'exception_tracker.email_prefix', | |
628 | '[RHODECODE ERROR]', lower=False, default_when_empty=True) |
|
626 | '[RHODECODE ERROR]', lower=False, default_when_empty=True) | |
629 |
|
627 | |||
630 | # cache_perms |
|
628 | # cache_perms | |
631 | _string_setting( |
|
629 | _string_setting( | |
632 | settings, |
|
630 | settings, | |
633 | 'rc_cache.cache_perms.backend', |
|
631 | 'rc_cache.cache_perms.backend', | |
634 | 'dogpile.cache.rc.file_namespace', lower=False) |
|
632 | 'dogpile.cache.rc.file_namespace', lower=False) | |
635 | _int_setting( |
|
633 | _int_setting( | |
636 | settings, |
|
634 | settings, | |
637 | 'rc_cache.cache_perms.expiration_time', |
|
635 | 'rc_cache.cache_perms.expiration_time', | |
638 | 60) |
|
636 | 60) | |
639 | _string_setting( |
|
637 | _string_setting( | |
640 | settings, |
|
638 | settings, | |
641 | 'rc_cache.cache_perms.arguments.filename', |
|
639 | 'rc_cache.cache_perms.arguments.filename', | |
642 | os.path.join(default_cache_dir, 'rc_cache_1'), lower=False) |
|
640 | os.path.join(default_cache_dir, 'rc_cache_1'), lower=False) | |
643 |
|
641 | |||
644 | # cache_repo |
|
642 | # cache_repo | |
645 | _string_setting( |
|
643 | _string_setting( | |
646 | settings, |
|
644 | settings, | |
647 | 'rc_cache.cache_repo.backend', |
|
645 | 'rc_cache.cache_repo.backend', | |
648 | 'dogpile.cache.rc.file_namespace', lower=False) |
|
646 | 'dogpile.cache.rc.file_namespace', lower=False) | |
649 | _int_setting( |
|
647 | _int_setting( | |
650 | settings, |
|
648 | settings, | |
651 | 'rc_cache.cache_repo.expiration_time', |
|
649 | 'rc_cache.cache_repo.expiration_time', | |
652 | 60) |
|
650 | 60) | |
653 | _string_setting( |
|
651 | _string_setting( | |
654 | settings, |
|
652 | settings, | |
655 | 'rc_cache.cache_repo.arguments.filename', |
|
653 | 'rc_cache.cache_repo.arguments.filename', | |
656 | os.path.join(default_cache_dir, 'rc_cache_2'), lower=False) |
|
654 | os.path.join(default_cache_dir, 'rc_cache_2'), lower=False) | |
657 |
|
655 | |||
658 | # cache_license |
|
656 | # cache_license | |
659 | _string_setting( |
|
657 | _string_setting( | |
660 | settings, |
|
658 | settings, | |
661 | 'rc_cache.cache_license.backend', |
|
659 | 'rc_cache.cache_license.backend', | |
662 | 'dogpile.cache.rc.file_namespace', lower=False) |
|
660 | 'dogpile.cache.rc.file_namespace', lower=False) | |
663 | _int_setting( |
|
661 | _int_setting( | |
664 | settings, |
|
662 | settings, | |
665 | 'rc_cache.cache_license.expiration_time', |
|
663 | 'rc_cache.cache_license.expiration_time', | |
666 | 5*60) |
|
664 | 5*60) | |
667 | _string_setting( |
|
665 | _string_setting( | |
668 | settings, |
|
666 | settings, | |
669 | 'rc_cache.cache_license.arguments.filename', |
|
667 | 'rc_cache.cache_license.arguments.filename', | |
670 | os.path.join(default_cache_dir, 'rc_cache_3'), lower=False) |
|
668 | os.path.join(default_cache_dir, 'rc_cache_3'), lower=False) | |
671 |
|
669 | |||
672 | # cache_repo_longterm memory, 96H |
|
670 | # cache_repo_longterm memory, 96H | |
673 | _string_setting( |
|
671 | _string_setting( | |
674 | settings, |
|
672 | settings, | |
675 | 'rc_cache.cache_repo_longterm.backend', |
|
673 | 'rc_cache.cache_repo_longterm.backend', | |
676 | 'dogpile.cache.rc.memory_lru', lower=False) |
|
674 | 'dogpile.cache.rc.memory_lru', lower=False) | |
677 | _int_setting( |
|
675 | _int_setting( | |
678 | settings, |
|
676 | settings, | |
679 | 'rc_cache.cache_repo_longterm.expiration_time', |
|
677 | 'rc_cache.cache_repo_longterm.expiration_time', | |
680 | 345600) |
|
678 | 345600) | |
681 | _int_setting( |
|
679 | _int_setting( | |
682 | settings, |
|
680 | settings, | |
683 | 'rc_cache.cache_repo_longterm.max_size', |
|
681 | 'rc_cache.cache_repo_longterm.max_size', | |
684 | 10000) |
|
682 | 10000) | |
685 |
|
683 | |||
686 | # sql_cache_short |
|
684 | # sql_cache_short | |
687 | _string_setting( |
|
685 | _string_setting( | |
688 | settings, |
|
686 | settings, | |
689 | 'rc_cache.sql_cache_short.backend', |
|
687 | 'rc_cache.sql_cache_short.backend', | |
690 | 'dogpile.cache.rc.memory_lru', lower=False) |
|
688 | 'dogpile.cache.rc.memory_lru', lower=False) | |
691 | _int_setting( |
|
689 | _int_setting( | |
692 | settings, |
|
690 | settings, | |
693 | 'rc_cache.sql_cache_short.expiration_time', |
|
691 | 'rc_cache.sql_cache_short.expiration_time', | |
694 | 30) |
|
692 | 30) | |
695 | _int_setting( |
|
693 | _int_setting( | |
696 | settings, |
|
694 | settings, | |
697 | 'rc_cache.sql_cache_short.max_size', |
|
695 | 'rc_cache.sql_cache_short.max_size', | |
698 | 10000) |
|
696 | 10000) | |
699 |
|
697 | |||
700 |
|
698 | |||
701 | def _int_setting(settings, name, default): |
|
699 | def _int_setting(settings, name, default): | |
702 | settings[name] = int(settings.get(name, default)) |
|
700 | settings[name] = int(settings.get(name, default)) | |
703 | return settings[name] |
|
701 | return settings[name] | |
704 |
|
702 | |||
705 |
|
703 | |||
706 | def _bool_setting(settings, name, default): |
|
704 | def _bool_setting(settings, name, default): | |
707 | input_val = settings.get(name, default) |
|
705 | input_val = settings.get(name, default) | |
708 | if isinstance(input_val, unicode): |
|
706 | if isinstance(input_val, unicode): | |
709 | input_val = input_val.encode('utf8') |
|
707 | input_val = input_val.encode('utf8') | |
710 | settings[name] = asbool(input_val) |
|
708 | settings[name] = asbool(input_val) | |
711 | return settings[name] |
|
709 | return settings[name] | |
712 |
|
710 | |||
713 |
|
711 | |||
714 | def _list_setting(settings, name, default): |
|
712 | def _list_setting(settings, name, default): | |
715 | raw_value = settings.get(name, default) |
|
713 | raw_value = settings.get(name, default) | |
716 |
|
714 | |||
717 | old_separator = ',' |
|
715 | old_separator = ',' | |
718 | if old_separator in raw_value: |
|
716 | if old_separator in raw_value: | |
719 | # If we get a comma separated list, pass it to our own function. |
|
717 | # If we get a comma separated list, pass it to our own function. | |
720 | settings[name] = rhodecode_aslist(raw_value, sep=old_separator) |
|
718 | settings[name] = rhodecode_aslist(raw_value, sep=old_separator) | |
721 | else: |
|
719 | else: | |
722 | # Otherwise we assume it uses pyramids space/newline separation. |
|
720 | # Otherwise we assume it uses pyramids space/newline separation. | |
723 | settings[name] = aslist(raw_value) |
|
721 | settings[name] = aslist(raw_value) | |
724 | return settings[name] |
|
722 | return settings[name] | |
725 |
|
723 | |||
726 |
|
724 | |||
727 | def _string_setting(settings, name, default, lower=True, default_when_empty=False): |
|
725 | def _string_setting(settings, name, default, lower=True, default_when_empty=False): | |
728 | value = settings.get(name, default) |
|
726 | value = settings.get(name, default) | |
729 |
|
727 | |||
730 | if default_when_empty and not value: |
|
728 | if default_when_empty and not value: | |
731 | # use default value when value is empty |
|
729 | # use default value when value is empty | |
732 | value = default |
|
730 | value = default | |
733 |
|
731 | |||
734 | if lower: |
|
732 | if lower: | |
735 | value = value.lower() |
|
733 | value = value.lower() | |
736 | settings[name] = value |
|
734 | settings[name] = value | |
737 | return settings[name] |
|
735 | return settings[name] | |
738 |
|
736 | |||
739 |
|
737 | |||
740 | def _substitute_values(mapping, substitutions): |
|
738 | def _substitute_values(mapping, substitutions): | |
741 | result = {} |
|
739 | result = {} | |
742 |
|
740 | |||
743 | try: |
|
741 | try: | |
744 | for key, value in mapping.items(): |
|
742 | for key, value in mapping.items(): | |
745 | # initialize without substitution first |
|
743 | # initialize without substitution first | |
746 | result[key] = value |
|
744 | result[key] = value | |
747 |
|
745 | |||
748 | # Note: Cannot use regular replacements, since they would clash |
|
746 | # Note: Cannot use regular replacements, since they would clash | |
749 | # with the implementation of ConfigParser. Using "format" instead. |
|
747 | # with the implementation of ConfigParser. Using "format" instead. | |
750 | try: |
|
748 | try: | |
751 | result[key] = value.format(**substitutions) |
|
749 | result[key] = value.format(**substitutions) | |
752 | except KeyError as e: |
|
750 | except KeyError as e: | |
753 | env_var = '{}'.format(e.args[0]) |
|
751 | env_var = '{}'.format(e.args[0]) | |
754 |
|
752 | |||
755 | msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \ |
|
753 | msg = 'Failed to substitute: `{key}={{{var}}}` with environment entry. ' \ | |
756 | 'Make sure your environment has {var} set, or remove this ' \ |
|
754 | 'Make sure your environment has {var} set, or remove this ' \ | |
757 | 'variable from config file'.format(key=key, var=env_var) |
|
755 | 'variable from config file'.format(key=key, var=env_var) | |
758 |
|
756 | |||
759 | if env_var.startswith('ENV_'): |
|
757 | if env_var.startswith('ENV_'): | |
760 | raise ValueError(msg) |
|
758 | raise ValueError(msg) | |
761 | else: |
|
759 | else: | |
762 | log.warning(msg) |
|
760 | log.warning(msg) | |
763 |
|
761 | |||
764 | except ValueError as e: |
|
762 | except ValueError as e: | |
765 | log.warning('Failed to substitute ENV variable: %s', e) |
|
763 | log.warning('Failed to substitute ENV variable: %s', e) | |
766 | result = mapping |
|
764 | result = mapping | |
767 |
|
765 | |||
768 | return result |
|
766 | return result |
@@ -1,82 +1,86 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2015-2020 RhodeCode GmbH |
|
3 | # Copyright (C) 2015-2020 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 | from dogpile.cache import register_backend |
|
22 | from dogpile.cache import register_backend | |
23 |
|
23 | |||
24 | register_backend( |
|
24 | register_backend( | |
25 | "dogpile.cache.rc.memory_lru", "rhodecode.lib.rc_cache.backends", |
|
25 | "dogpile.cache.rc.memory_lru", "rhodecode.lib.rc_cache.backends", | |
26 | "LRUMemoryBackend") |
|
26 | "LRUMemoryBackend") | |
27 |
|
27 | |||
28 | register_backend( |
|
28 | register_backend( | |
29 | "dogpile.cache.rc.file_namespace", "rhodecode.lib.rc_cache.backends", |
|
29 | "dogpile.cache.rc.file_namespace", "rhodecode.lib.rc_cache.backends", | |
30 | "FileNamespaceBackend") |
|
30 | "FileNamespaceBackend") | |
31 |
|
31 | |||
32 | register_backend( |
|
32 | register_backend( | |
33 | "dogpile.cache.rc.redis", "rhodecode.lib.rc_cache.backends", |
|
33 | "dogpile.cache.rc.redis", "rhodecode.lib.rc_cache.backends", | |
34 | "RedisPickleBackend") |
|
34 | "RedisPickleBackend") | |
35 |
|
35 | |||
36 | register_backend( |
|
36 | register_backend( | |
37 | "dogpile.cache.rc.redis_msgpack", "rhodecode.lib.rc_cache.backends", |
|
37 | "dogpile.cache.rc.redis_msgpack", "rhodecode.lib.rc_cache.backends", | |
38 | "RedisMsgPackBackend") |
|
38 | "RedisMsgPackBackend") | |
39 |
|
39 | |||
40 |
|
40 | |||
41 | log = logging.getLogger(__name__) |
|
41 | log = logging.getLogger(__name__) | |
42 |
|
42 | |||
43 | from . import region_meta |
|
43 | from . import region_meta | |
44 | from .utils import ( |
|
44 | from .utils import ( | |
45 | get_default_cache_settings, backend_key_generator, get_or_create_region, |
|
45 | get_default_cache_settings, backend_key_generator, get_or_create_region, | |
46 | clear_cache_namespace, make_region, InvalidationContext, |
|
46 | clear_cache_namespace, make_region, InvalidationContext, | |
47 | FreshRegionCache, ActiveRegionCache) |
|
47 | FreshRegionCache, ActiveRegionCache) | |
48 |
|
48 | |||
49 |
|
49 | |||
50 | FILE_TREE_CACHE_VER = 'v4' |
|
50 | FILE_TREE_CACHE_VER = 'v4' | |
51 | LICENSE_CACHE_VER = 'v2' |
|
51 | LICENSE_CACHE_VER = 'v2' | |
52 |
|
52 | |||
53 |
|
53 | |||
54 | def configure_dogpile_cache(settings): |
|
54 | def configure_dogpile_cache(settings): | |
55 | cache_dir = settings.get('cache_dir') |
|
55 | cache_dir = settings.get('cache_dir') | |
56 | if cache_dir: |
|
56 | if cache_dir: | |
57 | region_meta.dogpile_config_defaults['cache_dir'] = cache_dir |
|
57 | region_meta.dogpile_config_defaults['cache_dir'] = cache_dir | |
58 |
|
58 | |||
59 | rc_cache_data = get_default_cache_settings(settings, prefixes=['rc_cache.']) |
|
59 | rc_cache_data = get_default_cache_settings(settings, prefixes=['rc_cache.']) | |
60 |
|
60 | |||
61 | # inspect available namespaces |
|
61 | # inspect available namespaces | |
62 | avail_regions = set() |
|
62 | avail_regions = set() | |
63 | for key in rc_cache_data.keys(): |
|
63 | for key in rc_cache_data.keys(): | |
64 | namespace_name = key.split('.', 1)[0] |
|
64 | namespace_name = key.split('.', 1)[0] | |
65 | avail_regions.add(namespace_name) |
|
65 | avail_regions.add(namespace_name) | |
66 | log.debug('dogpile: found following cache regions: %s', avail_regions) |
|
66 | log.debug('dogpile: found following cache regions: %s', avail_regions) | |
67 |
|
67 | |||
68 | # register them into namespace |
|
68 | # register them into namespace | |
69 | for region_name in avail_regions: |
|
69 | for region_name in avail_regions: | |
70 | new_region = make_region( |
|
70 | new_region = make_region( | |
71 | name=region_name, |
|
71 | name=region_name, | |
72 | function_key_generator=None |
|
72 | function_key_generator=None | |
73 | ) |
|
73 | ) | |
74 |
|
74 | |||
75 | new_region.configure_from_config(settings, 'rc_cache.{}.'.format(region_name)) |
|
75 | new_region.configure_from_config(settings, 'rc_cache.{}.'.format(region_name)) | |
76 | new_region.function_key_generator = backend_key_generator(new_region.actual_backend) |
|
76 | new_region.function_key_generator = backend_key_generator(new_region.actual_backend) | |
77 | log.debug('dogpile: registering a new region %s[%s]', region_name, new_region.__dict__) |
|
77 | if log.isEnabledFor(logging.DEBUG): | |
|
78 | region_args = dict(backend=new_region.actual_backend.__class__, | |||
|
79 | region_invalidator=new_region.region_invalidator.__class__) | |||
|
80 | log.debug('dogpile: registering a new region `%s` %s', region_name, region_args) | |||
|
81 | ||||
78 | region_meta.dogpile_cache_regions[region_name] = new_region |
|
82 | region_meta.dogpile_cache_regions[region_name] = new_region | |
79 |
|
83 | |||
80 |
|
84 | |||
81 | def includeme(config): |
|
85 | def includeme(config): | |
82 | configure_dogpile_cache(config.registry.settings) |
|
86 | configure_dogpile_cache(config.registry.settings) |
@@ -1,395 +1,389 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2010-2020 RhodeCode GmbH |
|
3 | # Copyright (C) 2010-2020 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 | import io |
|
20 | import io | |
21 | import math |
|
21 | import math | |
22 | import re |
|
22 | import re | |
23 | import os |
|
23 | import os | |
24 | import datetime |
|
24 | import datetime | |
25 | import logging |
|
25 | import logging | |
26 | import Queue |
|
26 | import Queue | |
27 | import subprocess32 |
|
27 | import subprocess32 | |
28 |
|
28 | |||
29 |
|
29 | |||
30 | from dateutil.parser import parse |
|
30 | from dateutil.parser import parse | |
31 | from pyramid.threadlocal import get_current_request |
|
31 | from pyramid.threadlocal import get_current_request | |
32 | from pyramid.interfaces import IRoutesMapper |
|
32 | from pyramid.interfaces import IRoutesMapper | |
33 | from pyramid.settings import asbool |
|
33 | from pyramid.settings import asbool | |
34 | from pyramid.path import AssetResolver |
|
34 | from pyramid.path import AssetResolver | |
35 | from threading import Thread |
|
35 | from threading import Thread | |
36 |
|
36 | |||
37 | from rhodecode.translation import _ as tsf |
|
37 | from rhodecode.translation import _ as tsf | |
38 | from rhodecode.config.jsroutes import generate_jsroutes_content |
|
38 | from rhodecode.config.jsroutes import generate_jsroutes_content | |
39 | from rhodecode.lib import auth |
|
39 | from rhodecode.lib import auth | |
40 | from rhodecode.lib.base import get_auth_user |
|
40 | from rhodecode.lib.base import get_auth_user | |
41 |
|
41 | |||
42 | import rhodecode |
|
42 | import rhodecode | |
43 |
|
43 | |||
44 |
|
44 | |||
45 | log = logging.getLogger(__name__) |
|
45 | log = logging.getLogger(__name__) | |
46 |
|
46 | |||
47 |
|
47 | |||
48 | def add_renderer_globals(event): |
|
48 | def add_renderer_globals(event): | |
49 | from rhodecode.lib import helpers |
|
49 | from rhodecode.lib import helpers | |
50 |
|
50 | |||
51 | # TODO: When executed in pyramid view context the request is not available |
|
51 | # TODO: When executed in pyramid view context the request is not available | |
52 | # in the event. Find a better solution to get the request. |
|
52 | # in the event. Find a better solution to get the request. | |
53 | request = event['request'] or get_current_request() |
|
53 | request = event['request'] or get_current_request() | |
54 |
|
54 | |||
55 | # Add Pyramid translation as '_' to context |
|
55 | # Add Pyramid translation as '_' to context | |
56 | event['_'] = request.translate |
|
56 | event['_'] = request.translate | |
57 | event['_ungettext'] = request.plularize |
|
57 | event['_ungettext'] = request.plularize | |
58 | event['h'] = helpers |
|
58 | event['h'] = helpers | |
59 |
|
59 | |||
60 |
|
60 | |||
61 | def add_localizer(event): |
|
61 | def add_localizer(event): | |
62 | request = event.request |
|
62 | request = event.request | |
63 | localizer = request.localizer |
|
63 | localizer = request.localizer | |
64 |
|
64 | |||
65 | def auto_translate(*args, **kwargs): |
|
65 | def auto_translate(*args, **kwargs): | |
66 | return localizer.translate(tsf(*args, **kwargs)) |
|
66 | return localizer.translate(tsf(*args, **kwargs)) | |
67 |
|
67 | |||
68 | request.translate = auto_translate |
|
68 | request.translate = auto_translate | |
69 | request.plularize = localizer.pluralize |
|
69 | request.plularize = localizer.pluralize | |
70 |
|
70 | |||
71 |
|
71 | |||
72 | def set_user_lang(event): |
|
72 | def set_user_lang(event): | |
73 | request = event.request |
|
73 | request = event.request | |
74 | cur_user = getattr(request, 'user', None) |
|
74 | cur_user = getattr(request, 'user', None) | |
75 |
|
75 | |||
76 | if cur_user: |
|
76 | if cur_user: | |
77 | user_lang = cur_user.get_instance().user_data.get('language') |
|
77 | user_lang = cur_user.get_instance().user_data.get('language') | |
78 | if user_lang: |
|
78 | if user_lang: | |
79 | log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang) |
|
79 | log.debug('lang: setting current user:%s language to: %s', cur_user, user_lang) | |
80 | event.request._LOCALE_ = user_lang |
|
80 | event.request._LOCALE_ = user_lang | |
81 |
|
81 | |||
82 |
|
82 | |||
83 | def add_request_user_context(event): |
|
83 | def add_request_user_context(event): | |
84 | """ |
|
84 | """ | |
85 | Adds auth user into request context |
|
85 | Adds auth user into request context | |
86 | """ |
|
86 | """ | |
87 | request = event.request |
|
87 | request = event.request | |
88 | # access req_id as soon as possible |
|
88 | # access req_id as soon as possible | |
89 | req_id = request.req_id |
|
89 | req_id = request.req_id | |
90 |
|
90 | |||
91 | if hasattr(request, 'vcs_call'): |
|
91 | if hasattr(request, 'vcs_call'): | |
92 | # skip vcs calls |
|
92 | # skip vcs calls | |
93 | return |
|
93 | return | |
94 |
|
94 | |||
95 | if hasattr(request, 'rpc_method'): |
|
95 | if hasattr(request, 'rpc_method'): | |
96 | # skip api calls |
|
96 | # skip api calls | |
97 | return |
|
97 | return | |
98 |
|
98 | |||
99 | auth_user, auth_token = get_auth_user(request) |
|
99 | auth_user, auth_token = get_auth_user(request) | |
100 | request.user = auth_user |
|
100 | request.user = auth_user | |
101 | request.user_auth_token = auth_token |
|
101 | request.user_auth_token = auth_token | |
102 | request.environ['rc_auth_user'] = auth_user |
|
102 | request.environ['rc_auth_user'] = auth_user | |
103 | request.environ['rc_auth_user_id'] = auth_user.user_id |
|
103 | request.environ['rc_auth_user_id'] = auth_user.user_id | |
104 | request.environ['rc_req_id'] = req_id |
|
104 | request.environ['rc_req_id'] = req_id | |
105 |
|
105 | |||
106 |
|
106 | |||
107 | def inject_app_settings(event): |
|
|||
108 | settings = event.app.registry.settings |
|
|||
109 | # inject info about available permissions |
|
|||
110 | auth.set_available_permissions(settings) |
|
|||
111 |
|
||||
112 |
|
||||
113 | def scan_repositories_if_enabled(event): |
|
107 | def scan_repositories_if_enabled(event): | |
114 | """ |
|
108 | """ | |
115 | This is subscribed to the `pyramid.events.ApplicationCreated` event. It |
|
109 | This is subscribed to the `pyramid.events.ApplicationCreated` event. It | |
116 | does a repository scan if enabled in the settings. |
|
110 | does a repository scan if enabled in the settings. | |
117 | """ |
|
111 | """ | |
118 | settings = event.app.registry.settings |
|
112 | settings = event.app.registry.settings | |
119 | vcs_server_enabled = settings['vcs.server.enable'] |
|
113 | vcs_server_enabled = settings['vcs.server.enable'] | |
120 | import_on_startup = settings['startup.import_repos'] |
|
114 | import_on_startup = settings['startup.import_repos'] | |
121 | if vcs_server_enabled and import_on_startup: |
|
115 | if vcs_server_enabled and import_on_startup: | |
122 | from rhodecode.model.scm import ScmModel |
|
116 | from rhodecode.model.scm import ScmModel | |
123 | from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path |
|
117 | from rhodecode.lib.utils import repo2db_mapper, get_rhodecode_base_path | |
124 | repositories = ScmModel().repo_scan(get_rhodecode_base_path()) |
|
118 | repositories = ScmModel().repo_scan(get_rhodecode_base_path()) | |
125 | repo2db_mapper(repositories, remove_obsolete=False) |
|
119 | repo2db_mapper(repositories, remove_obsolete=False) | |
126 |
|
120 | |||
127 |
|
121 | |||
128 | def write_metadata_if_needed(event): |
|
122 | def write_metadata_if_needed(event): | |
129 | """ |
|
123 | """ | |
130 | Writes upgrade metadata |
|
124 | Writes upgrade metadata | |
131 | """ |
|
125 | """ | |
132 | import rhodecode |
|
126 | import rhodecode | |
133 | from rhodecode.lib import system_info |
|
127 | from rhodecode.lib import system_info | |
134 | from rhodecode.lib import ext_json |
|
128 | from rhodecode.lib import ext_json | |
135 |
|
129 | |||
136 | fname = '.rcmetadata.json' |
|
130 | fname = '.rcmetadata.json' | |
137 | ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__')) |
|
131 | ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__')) | |
138 | metadata_destination = os.path.join(ini_loc, fname) |
|
132 | metadata_destination = os.path.join(ini_loc, fname) | |
139 |
|
133 | |||
140 | def get_update_age(): |
|
134 | def get_update_age(): | |
141 | now = datetime.datetime.utcnow() |
|
135 | now = datetime.datetime.utcnow() | |
142 |
|
136 | |||
143 | with open(metadata_destination, 'rb') as f: |
|
137 | with open(metadata_destination, 'rb') as f: | |
144 | data = ext_json.json.loads(f.read()) |
|
138 | data = ext_json.json.loads(f.read()) | |
145 | if 'created_on' in data: |
|
139 | if 'created_on' in data: | |
146 | update_date = parse(data['created_on']) |
|
140 | update_date = parse(data['created_on']) | |
147 | diff = now - update_date |
|
141 | diff = now - update_date | |
148 | return diff.total_seconds() / 60.0 |
|
142 | return diff.total_seconds() / 60.0 | |
149 |
|
143 | |||
150 | return 0 |
|
144 | return 0 | |
151 |
|
145 | |||
152 | def write(): |
|
146 | def write(): | |
153 | configuration = system_info.SysInfo( |
|
147 | configuration = system_info.SysInfo( | |
154 | system_info.rhodecode_config)()['value'] |
|
148 | system_info.rhodecode_config)()['value'] | |
155 | license_token = configuration['config']['license_token'] |
|
149 | license_token = configuration['config']['license_token'] | |
156 |
|
150 | |||
157 | setup = dict( |
|
151 | setup = dict( | |
158 | workers=configuration['config']['server:main'].get( |
|
152 | workers=configuration['config']['server:main'].get( | |
159 | 'workers', '?'), |
|
153 | 'workers', '?'), | |
160 | worker_type=configuration['config']['server:main'].get( |
|
154 | worker_type=configuration['config']['server:main'].get( | |
161 | 'worker_class', 'sync'), |
|
155 | 'worker_class', 'sync'), | |
162 | ) |
|
156 | ) | |
163 | dbinfo = system_info.SysInfo(system_info.database_info)()['value'] |
|
157 | dbinfo = system_info.SysInfo(system_info.database_info)()['value'] | |
164 | del dbinfo['url'] |
|
158 | del dbinfo['url'] | |
165 |
|
159 | |||
166 | metadata = dict( |
|
160 | metadata = dict( | |
167 | desc='upgrade metadata info', |
|
161 | desc='upgrade metadata info', | |
168 | license_token=license_token, |
|
162 | license_token=license_token, | |
169 | created_on=datetime.datetime.utcnow().isoformat(), |
|
163 | created_on=datetime.datetime.utcnow().isoformat(), | |
170 | usage=system_info.SysInfo(system_info.usage_info)()['value'], |
|
164 | usage=system_info.SysInfo(system_info.usage_info)()['value'], | |
171 | platform=system_info.SysInfo(system_info.platform_type)()['value'], |
|
165 | platform=system_info.SysInfo(system_info.platform_type)()['value'], | |
172 | database=dbinfo, |
|
166 | database=dbinfo, | |
173 | cpu=system_info.SysInfo(system_info.cpu)()['value'], |
|
167 | cpu=system_info.SysInfo(system_info.cpu)()['value'], | |
174 | memory=system_info.SysInfo(system_info.memory)()['value'], |
|
168 | memory=system_info.SysInfo(system_info.memory)()['value'], | |
175 | setup=setup |
|
169 | setup=setup | |
176 | ) |
|
170 | ) | |
177 |
|
171 | |||
178 | with open(metadata_destination, 'wb') as f: |
|
172 | with open(metadata_destination, 'wb') as f: | |
179 | f.write(ext_json.json.dumps(metadata)) |
|
173 | f.write(ext_json.json.dumps(metadata)) | |
180 |
|
174 | |||
181 | settings = event.app.registry.settings |
|
175 | settings = event.app.registry.settings | |
182 | if settings.get('metadata.skip'): |
|
176 | if settings.get('metadata.skip'): | |
183 | return |
|
177 | return | |
184 |
|
178 | |||
185 | # only write this every 24h, workers restart caused unwanted delays |
|
179 | # only write this every 24h, workers restart caused unwanted delays | |
186 | try: |
|
180 | try: | |
187 | age_in_min = get_update_age() |
|
181 | age_in_min = get_update_age() | |
188 | except Exception: |
|
182 | except Exception: | |
189 | age_in_min = 0 |
|
183 | age_in_min = 0 | |
190 |
|
184 | |||
191 | if age_in_min > 60 * 60 * 24: |
|
185 | if age_in_min > 60 * 60 * 24: | |
192 | return |
|
186 | return | |
193 |
|
187 | |||
194 | try: |
|
188 | try: | |
195 | write() |
|
189 | write() | |
196 | except Exception: |
|
190 | except Exception: | |
197 | pass |
|
191 | pass | |
198 |
|
192 | |||
199 |
|
193 | |||
200 | def write_usage_data(event): |
|
194 | def write_usage_data(event): | |
201 | import rhodecode |
|
195 | import rhodecode | |
202 | from rhodecode.lib import system_info |
|
196 | from rhodecode.lib import system_info | |
203 | from rhodecode.lib import ext_json |
|
197 | from rhodecode.lib import ext_json | |
204 |
|
198 | |||
205 | settings = event.app.registry.settings |
|
199 | settings = event.app.registry.settings | |
206 | instance_tag = settings.get('metadata.write_usage_tag') |
|
200 | instance_tag = settings.get('metadata.write_usage_tag') | |
207 | if not settings.get('metadata.write_usage'): |
|
201 | if not settings.get('metadata.write_usage'): | |
208 | return |
|
202 | return | |
209 |
|
203 | |||
210 | def get_update_age(dest_file): |
|
204 | def get_update_age(dest_file): | |
211 | now = datetime.datetime.utcnow() |
|
205 | now = datetime.datetime.utcnow() | |
212 |
|
206 | |||
213 | with open(dest_file, 'rb') as f: |
|
207 | with open(dest_file, 'rb') as f: | |
214 | data = ext_json.json.loads(f.read()) |
|
208 | data = ext_json.json.loads(f.read()) | |
215 | if 'created_on' in data: |
|
209 | if 'created_on' in data: | |
216 | update_date = parse(data['created_on']) |
|
210 | update_date = parse(data['created_on']) | |
217 | diff = now - update_date |
|
211 | diff = now - update_date | |
218 | return math.ceil(diff.total_seconds() / 60.0) |
|
212 | return math.ceil(diff.total_seconds() / 60.0) | |
219 |
|
213 | |||
220 | return 0 |
|
214 | return 0 | |
221 |
|
215 | |||
222 | utc_date = datetime.datetime.utcnow() |
|
216 | utc_date = datetime.datetime.utcnow() | |
223 | hour_quarter = int(math.ceil((utc_date.hour + utc_date.minute/60.0) / 6.)) |
|
217 | hour_quarter = int(math.ceil((utc_date.hour + utc_date.minute/60.0) / 6.)) | |
224 | fname = '.rc_usage_{date.year}{date.month:02d}{date.day:02d}_{hour}.json'.format( |
|
218 | fname = '.rc_usage_{date.year}{date.month:02d}{date.day:02d}_{hour}.json'.format( | |
225 | date=utc_date, hour=hour_quarter) |
|
219 | date=utc_date, hour=hour_quarter) | |
226 | ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__')) |
|
220 | ini_loc = os.path.dirname(rhodecode.CONFIG.get('__file__')) | |
227 |
|
221 | |||
228 | usage_dir = os.path.join(ini_loc, '.rcusage') |
|
222 | usage_dir = os.path.join(ini_loc, '.rcusage') | |
229 | if not os.path.isdir(usage_dir): |
|
223 | if not os.path.isdir(usage_dir): | |
230 | os.makedirs(usage_dir) |
|
224 | os.makedirs(usage_dir) | |
231 | usage_metadata_destination = os.path.join(usage_dir, fname) |
|
225 | usage_metadata_destination = os.path.join(usage_dir, fname) | |
232 |
|
226 | |||
233 | try: |
|
227 | try: | |
234 | age_in_min = get_update_age(usage_metadata_destination) |
|
228 | age_in_min = get_update_age(usage_metadata_destination) | |
235 | except Exception: |
|
229 | except Exception: | |
236 | age_in_min = 0 |
|
230 | age_in_min = 0 | |
237 |
|
231 | |||
238 | # write every 6th hour |
|
232 | # write every 6th hour | |
239 | if age_in_min and age_in_min < 60 * 6: |
|
233 | if age_in_min and age_in_min < 60 * 6: | |
240 | log.debug('Usage file created %s minutes ago, skipping (threashold: %s)...', |
|
234 | log.debug('Usage file created %s minutes ago, skipping (threashold: %s)...', | |
241 | age_in_min, 60 * 6) |
|
235 | age_in_min, 60 * 6) | |
242 | return |
|
236 | return | |
243 |
|
237 | |||
244 | def write(dest_file): |
|
238 | def write(dest_file): | |
245 | configuration = system_info.SysInfo(system_info.rhodecode_config)()['value'] |
|
239 | configuration = system_info.SysInfo(system_info.rhodecode_config)()['value'] | |
246 | license_token = configuration['config']['license_token'] |
|
240 | license_token = configuration['config']['license_token'] | |
247 |
|
241 | |||
248 | metadata = dict( |
|
242 | metadata = dict( | |
249 | desc='Usage data', |
|
243 | desc='Usage data', | |
250 | instance_tag=instance_tag, |
|
244 | instance_tag=instance_tag, | |
251 | license_token=license_token, |
|
245 | license_token=license_token, | |
252 | created_on=datetime.datetime.utcnow().isoformat(), |
|
246 | created_on=datetime.datetime.utcnow().isoformat(), | |
253 | usage=system_info.SysInfo(system_info.usage_info)()['value'], |
|
247 | usage=system_info.SysInfo(system_info.usage_info)()['value'], | |
254 | ) |
|
248 | ) | |
255 |
|
249 | |||
256 | with open(dest_file, 'wb') as f: |
|
250 | with open(dest_file, 'wb') as f: | |
257 | f.write(ext_json.json.dumps(metadata, indent=2, sort_keys=True)) |
|
251 | f.write(ext_json.json.dumps(metadata, indent=2, sort_keys=True)) | |
258 |
|
252 | |||
259 | try: |
|
253 | try: | |
260 | log.debug('Writing usage file at: %s', usage_metadata_destination) |
|
254 | log.debug('Writing usage file at: %s', usage_metadata_destination) | |
261 | write(usage_metadata_destination) |
|
255 | write(usage_metadata_destination) | |
262 | except Exception: |
|
256 | except Exception: | |
263 | pass |
|
257 | pass | |
264 |
|
258 | |||
265 |
|
259 | |||
266 | def write_js_routes_if_enabled(event): |
|
260 | def write_js_routes_if_enabled(event): | |
267 | registry = event.app.registry |
|
261 | registry = event.app.registry | |
268 |
|
262 | |||
269 | mapper = registry.queryUtility(IRoutesMapper) |
|
263 | mapper = registry.queryUtility(IRoutesMapper) | |
270 | _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)') |
|
264 | _argument_prog = re.compile('\{(.*?)\}|:\((.*)\)') | |
271 |
|
265 | |||
272 | def _extract_route_information(route): |
|
266 | def _extract_route_information(route): | |
273 | """ |
|
267 | """ | |
274 | Convert a route into tuple(name, path, args), eg: |
|
268 | Convert a route into tuple(name, path, args), eg: | |
275 | ('show_user', '/profile/%(username)s', ['username']) |
|
269 | ('show_user', '/profile/%(username)s', ['username']) | |
276 | """ |
|
270 | """ | |
277 |
|
271 | |||
278 | routepath = route.pattern |
|
272 | routepath = route.pattern | |
279 | pattern = route.pattern |
|
273 | pattern = route.pattern | |
280 |
|
274 | |||
281 | def replace(matchobj): |
|
275 | def replace(matchobj): | |
282 | if matchobj.group(1): |
|
276 | if matchobj.group(1): | |
283 | return "%%(%s)s" % matchobj.group(1).split(':')[0] |
|
277 | return "%%(%s)s" % matchobj.group(1).split(':')[0] | |
284 | else: |
|
278 | else: | |
285 | return "%%(%s)s" % matchobj.group(2) |
|
279 | return "%%(%s)s" % matchobj.group(2) | |
286 |
|
280 | |||
287 | routepath = _argument_prog.sub(replace, routepath) |
|
281 | routepath = _argument_prog.sub(replace, routepath) | |
288 |
|
282 | |||
289 | if not routepath.startswith('/'): |
|
283 | if not routepath.startswith('/'): | |
290 | routepath = '/'+routepath |
|
284 | routepath = '/'+routepath | |
291 |
|
285 | |||
292 | return ( |
|
286 | return ( | |
293 | route.name, |
|
287 | route.name, | |
294 | routepath, |
|
288 | routepath, | |
295 | [(arg[0].split(':')[0] if arg[0] != '' else arg[1]) |
|
289 | [(arg[0].split(':')[0] if arg[0] != '' else arg[1]) | |
296 | for arg in _argument_prog.findall(pattern)] |
|
290 | for arg in _argument_prog.findall(pattern)] | |
297 | ) |
|
291 | ) | |
298 |
|
292 | |||
299 | def get_routes(): |
|
293 | def get_routes(): | |
300 | # pyramid routes |
|
294 | # pyramid routes | |
301 | for route in mapper.get_routes(): |
|
295 | for route in mapper.get_routes(): | |
302 | if not route.name.startswith('__'): |
|
296 | if not route.name.startswith('__'): | |
303 | yield _extract_route_information(route) |
|
297 | yield _extract_route_information(route) | |
304 |
|
298 | |||
305 | if asbool(registry.settings.get('generate_js_files', 'false')): |
|
299 | if asbool(registry.settings.get('generate_js_files', 'false')): | |
306 | static_path = AssetResolver().resolve('rhodecode:public').abspath() |
|
300 | static_path = AssetResolver().resolve('rhodecode:public').abspath() | |
307 | jsroutes = get_routes() |
|
301 | jsroutes = get_routes() | |
308 | jsroutes_file_content = generate_jsroutes_content(jsroutes) |
|
302 | jsroutes_file_content = generate_jsroutes_content(jsroutes) | |
309 | jsroutes_file_path = os.path.join( |
|
303 | jsroutes_file_path = os.path.join( | |
310 | static_path, 'js', 'rhodecode', 'routes.js') |
|
304 | static_path, 'js', 'rhodecode', 'routes.js') | |
311 |
|
305 | |||
312 | try: |
|
306 | try: | |
313 | with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f: |
|
307 | with io.open(jsroutes_file_path, 'w', encoding='utf-8') as f: | |
314 | f.write(jsroutes_file_content) |
|
308 | f.write(jsroutes_file_content) | |
315 | except Exception: |
|
309 | except Exception: | |
316 | log.exception('Failed to write routes.js into %s', jsroutes_file_path) |
|
310 | log.exception('Failed to write routes.js into %s', jsroutes_file_path) | |
317 |
|
311 | |||
318 |
|
312 | |||
319 | class Subscriber(object): |
|
313 | class Subscriber(object): | |
320 | """ |
|
314 | """ | |
321 | Base class for subscribers to the pyramid event system. |
|
315 | Base class for subscribers to the pyramid event system. | |
322 | """ |
|
316 | """ | |
323 | def __call__(self, event): |
|
317 | def __call__(self, event): | |
324 | self.run(event) |
|
318 | self.run(event) | |
325 |
|
319 | |||
326 | def run(self, event): |
|
320 | def run(self, event): | |
327 | raise NotImplementedError('Subclass has to implement this.') |
|
321 | raise NotImplementedError('Subclass has to implement this.') | |
328 |
|
322 | |||
329 |
|
323 | |||
330 | class AsyncSubscriber(Subscriber): |
|
324 | class AsyncSubscriber(Subscriber): | |
331 | """ |
|
325 | """ | |
332 | Subscriber that handles the execution of events in a separate task to not |
|
326 | Subscriber that handles the execution of events in a separate task to not | |
333 | block the execution of the code which triggers the event. It puts the |
|
327 | block the execution of the code which triggers the event. It puts the | |
334 | received events into a queue from which the worker process takes them in |
|
328 | received events into a queue from which the worker process takes them in | |
335 | order. |
|
329 | order. | |
336 | """ |
|
330 | """ | |
337 | def __init__(self): |
|
331 | def __init__(self): | |
338 | self._stop = False |
|
332 | self._stop = False | |
339 | self._eventq = Queue.Queue() |
|
333 | self._eventq = Queue.Queue() | |
340 | self._worker = self.create_worker() |
|
334 | self._worker = self.create_worker() | |
341 | self._worker.start() |
|
335 | self._worker.start() | |
342 |
|
336 | |||
343 | def __call__(self, event): |
|
337 | def __call__(self, event): | |
344 | self._eventq.put(event) |
|
338 | self._eventq.put(event) | |
345 |
|
339 | |||
346 | def create_worker(self): |
|
340 | def create_worker(self): | |
347 | worker = Thread(target=self.do_work) |
|
341 | worker = Thread(target=self.do_work) | |
348 | worker.daemon = True |
|
342 | worker.daemon = True | |
349 | return worker |
|
343 | return worker | |
350 |
|
344 | |||
351 | def stop_worker(self): |
|
345 | def stop_worker(self): | |
352 | self._stop = False |
|
346 | self._stop = False | |
353 | self._eventq.put(None) |
|
347 | self._eventq.put(None) | |
354 | self._worker.join() |
|
348 | self._worker.join() | |
355 |
|
349 | |||
356 | def do_work(self): |
|
350 | def do_work(self): | |
357 | while not self._stop: |
|
351 | while not self._stop: | |
358 | event = self._eventq.get() |
|
352 | event = self._eventq.get() | |
359 | if event is not None: |
|
353 | if event is not None: | |
360 | self.run(event) |
|
354 | self.run(event) | |
361 |
|
355 | |||
362 |
|
356 | |||
363 | class AsyncSubprocessSubscriber(AsyncSubscriber): |
|
357 | class AsyncSubprocessSubscriber(AsyncSubscriber): | |
364 | """ |
|
358 | """ | |
365 | Subscriber that uses the subprocess32 module to execute a command if an |
|
359 | Subscriber that uses the subprocess32 module to execute a command if an | |
366 | event is received. Events are handled asynchronously. |
|
360 | event is received. Events are handled asynchronously. | |
367 | """ |
|
361 | """ | |
368 |
|
362 | |||
369 | def __init__(self, cmd, timeout=None): |
|
363 | def __init__(self, cmd, timeout=None): | |
370 | super(AsyncSubprocessSubscriber, self).__init__() |
|
364 | super(AsyncSubprocessSubscriber, self).__init__() | |
371 | self._cmd = cmd |
|
365 | self._cmd = cmd | |
372 | self._timeout = timeout |
|
366 | self._timeout = timeout | |
373 |
|
367 | |||
374 | def run(self, event): |
|
368 | def run(self, event): | |
375 | cmd = self._cmd |
|
369 | cmd = self._cmd | |
376 | timeout = self._timeout |
|
370 | timeout = self._timeout | |
377 | log.debug('Executing command %s.', cmd) |
|
371 | log.debug('Executing command %s.', cmd) | |
378 |
|
372 | |||
379 | try: |
|
373 | try: | |
380 | output = subprocess32.check_output( |
|
374 | output = subprocess32.check_output( | |
381 | cmd, timeout=timeout, stderr=subprocess32.STDOUT) |
|
375 | cmd, timeout=timeout, stderr=subprocess32.STDOUT) | |
382 | log.debug('Command finished %s', cmd) |
|
376 | log.debug('Command finished %s', cmd) | |
383 | if output: |
|
377 | if output: | |
384 | log.debug('Command output: %s', output) |
|
378 | log.debug('Command output: %s', output) | |
385 | except subprocess32.TimeoutExpired as e: |
|
379 | except subprocess32.TimeoutExpired as e: | |
386 | log.exception('Timeout while executing command.') |
|
380 | log.exception('Timeout while executing command.') | |
387 | if e.output: |
|
381 | if e.output: | |
388 | log.error('Command output: %s', e.output) |
|
382 | log.error('Command output: %s', e.output) | |
389 | except subprocess32.CalledProcessError as e: |
|
383 | except subprocess32.CalledProcessError as e: | |
390 | log.exception('Error while executing command.') |
|
384 | log.exception('Error while executing command.') | |
391 | if e.output: |
|
385 | if e.output: | |
392 | log.error('Command output: %s', e.output) |
|
386 | log.error('Command output: %s', e.output) | |
393 | except: |
|
387 | except: | |
394 | log.exception( |
|
388 | log.exception( | |
395 | 'Exception while executing command %s.', cmd) |
|
389 | 'Exception while executing command %s.', cmd) |
General Comments 0
You need to be logged in to leave comments.
Login now