##// END OF EJS Templates
my-account: owner/watched repos are now loaded only using DB queries....
marcink -
r4153:4c7da78c default
parent child Browse files
Show More
@@ -1,765 +1,800 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2019 RhodeCode GmbH
3 # Copyright (C) 2016-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22 import datetime
22 import datetime
23 import string
23 import string
24
24
25 import formencode
25 import formencode
26 import formencode.htmlfill
26 import formencode.htmlfill
27 import peppercorn
27 import peppercorn
28 from pyramid.httpexceptions import HTTPFound
28 from pyramid.httpexceptions import HTTPFound
29 from pyramid.view import view_config
29 from pyramid.view import view_config
30
30
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
31 from rhodecode.apps._base import BaseAppView, DataGridAppView
32 from rhodecode import forms
32 from rhodecode import forms
33 from rhodecode.lib import helpers as h
33 from rhodecode.lib import helpers as h
34 from rhodecode.lib import audit_logger
34 from rhodecode.lib import audit_logger
35 from rhodecode.lib.ext_json import json
35 from rhodecode.lib.ext_json import json
36 from rhodecode.lib.auth import LoginRequired, NotAnonymous, CSRFRequired, \
36 from rhodecode.lib.auth import (
37 HasRepoPermissionAny, HasRepoGroupPermissionAny
37 LoginRequired, NotAnonymous, CSRFRequired,
38 HasRepoPermissionAny, HasRepoGroupPermissionAny, AuthUser)
38 from rhodecode.lib.channelstream import (
39 from rhodecode.lib.channelstream import (
39 channelstream_request, ChannelstreamException)
40 channelstream_request, ChannelstreamException)
40 from rhodecode.lib.utils2 import safe_int, md5, str2bool
41 from rhodecode.lib.utils2 import safe_int, md5, str2bool
41 from rhodecode.model.auth_token import AuthTokenModel
42 from rhodecode.model.auth_token import AuthTokenModel
42 from rhodecode.model.comment import CommentsModel
43 from rhodecode.model.comment import CommentsModel
43 from rhodecode.model.db import (
44 from rhodecode.model.db import (
44 IntegrityError, joinedload,
45 IntegrityError, or_, in_filter_generator,
45 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 Repository, UserEmailMap, UserApiKeys, UserFollowing,
46 PullRequest, UserBookmark, RepoGroup)
47 PullRequest, UserBookmark, RepoGroup)
47 from rhodecode.model.meta import Session
48 from rhodecode.model.meta import Session
48 from rhodecode.model.pull_request import PullRequestModel
49 from rhodecode.model.pull_request import PullRequestModel
49 from rhodecode.model.scm import RepoList
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.repo import RepoModel
52 from rhodecode.model.user_group import UserGroupModel
51 from rhodecode.model.user_group import UserGroupModel
53 from rhodecode.model.validation_schema.schemas import user_schema
52 from rhodecode.model.validation_schema.schemas import user_schema
54
53
55 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
56
55
57
56
58 class MyAccountView(BaseAppView, DataGridAppView):
57 class MyAccountView(BaseAppView, DataGridAppView):
59 ALLOW_SCOPED_TOKENS = False
58 ALLOW_SCOPED_TOKENS = False
60 """
59 """
61 This view has alternative version inside EE, if modified please take a look
60 This view has alternative version inside EE, if modified please take a look
62 in there as well.
61 in there as well.
63 """
62 """
64
63
65 def load_default_context(self):
64 def load_default_context(self):
66 c = self._get_local_tmpl_context()
65 c = self._get_local_tmpl_context()
67 c.user = c.auth_user.get_instance()
66 c.user = c.auth_user.get_instance()
68 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
67 c.allow_scoped_tokens = self.ALLOW_SCOPED_TOKENS
69
68
70 return c
69 return c
71
70
72 @LoginRequired()
71 @LoginRequired()
73 @NotAnonymous()
72 @NotAnonymous()
74 @view_config(
73 @view_config(
75 route_name='my_account_profile', request_method='GET',
74 route_name='my_account_profile', request_method='GET',
76 renderer='rhodecode:templates/admin/my_account/my_account.mako')
75 renderer='rhodecode:templates/admin/my_account/my_account.mako')
77 def my_account_profile(self):
76 def my_account_profile(self):
78 c = self.load_default_context()
77 c = self.load_default_context()
79 c.active = 'profile'
78 c.active = 'profile'
80 return self._get_template_context(c)
79 return self._get_template_context(c)
81
80
82 @LoginRequired()
81 @LoginRequired()
83 @NotAnonymous()
82 @NotAnonymous()
84 @view_config(
83 @view_config(
85 route_name='my_account_password', request_method='GET',
84 route_name='my_account_password', request_method='GET',
86 renderer='rhodecode:templates/admin/my_account/my_account.mako')
85 renderer='rhodecode:templates/admin/my_account/my_account.mako')
87 def my_account_password(self):
86 def my_account_password(self):
88 c = self.load_default_context()
87 c = self.load_default_context()
89 c.active = 'password'
88 c.active = 'password'
90 c.extern_type = c.user.extern_type
89 c.extern_type = c.user.extern_type
91
90
92 schema = user_schema.ChangePasswordSchema().bind(
91 schema = user_schema.ChangePasswordSchema().bind(
93 username=c.user.username)
92 username=c.user.username)
94
93
95 form = forms.Form(
94 form = forms.Form(
96 schema,
95 schema,
97 action=h.route_path('my_account_password_update'),
96 action=h.route_path('my_account_password_update'),
98 buttons=(forms.buttons.save, forms.buttons.reset))
97 buttons=(forms.buttons.save, forms.buttons.reset))
99
98
100 c.form = form
99 c.form = form
101 return self._get_template_context(c)
100 return self._get_template_context(c)
102
101
103 @LoginRequired()
102 @LoginRequired()
104 @NotAnonymous()
103 @NotAnonymous()
105 @CSRFRequired()
104 @CSRFRequired()
106 @view_config(
105 @view_config(
107 route_name='my_account_password_update', request_method='POST',
106 route_name='my_account_password_update', request_method='POST',
108 renderer='rhodecode:templates/admin/my_account/my_account.mako')
107 renderer='rhodecode:templates/admin/my_account/my_account.mako')
109 def my_account_password_update(self):
108 def my_account_password_update(self):
110 _ = self.request.translate
109 _ = self.request.translate
111 c = self.load_default_context()
110 c = self.load_default_context()
112 c.active = 'password'
111 c.active = 'password'
113 c.extern_type = c.user.extern_type
112 c.extern_type = c.user.extern_type
114
113
115 schema = user_schema.ChangePasswordSchema().bind(
114 schema = user_schema.ChangePasswordSchema().bind(
116 username=c.user.username)
115 username=c.user.username)
117
116
118 form = forms.Form(
117 form = forms.Form(
119 schema, buttons=(forms.buttons.save, forms.buttons.reset))
118 schema, buttons=(forms.buttons.save, forms.buttons.reset))
120
119
121 if c.extern_type != 'rhodecode':
120 if c.extern_type != 'rhodecode':
122 raise HTTPFound(self.request.route_path('my_account_password'))
121 raise HTTPFound(self.request.route_path('my_account_password'))
123
122
124 controls = self.request.POST.items()
123 controls = self.request.POST.items()
125 try:
124 try:
126 valid_data = form.validate(controls)
125 valid_data = form.validate(controls)
127 UserModel().update_user(c.user.user_id, **valid_data)
126 UserModel().update_user(c.user.user_id, **valid_data)
128 c.user.update_userdata(force_password_change=False)
127 c.user.update_userdata(force_password_change=False)
129 Session().commit()
128 Session().commit()
130 except forms.ValidationFailure as e:
129 except forms.ValidationFailure as e:
131 c.form = e
130 c.form = e
132 return self._get_template_context(c)
131 return self._get_template_context(c)
133
132
134 except Exception:
133 except Exception:
135 log.exception("Exception updating password")
134 log.exception("Exception updating password")
136 h.flash(_('Error occurred during update of user password'),
135 h.flash(_('Error occurred during update of user password'),
137 category='error')
136 category='error')
138 else:
137 else:
139 instance = c.auth_user.get_instance()
138 instance = c.auth_user.get_instance()
140 self.session.setdefault('rhodecode_user', {}).update(
139 self.session.setdefault('rhodecode_user', {}).update(
141 {'password': md5(instance.password)})
140 {'password': md5(instance.password)})
142 self.session.save()
141 self.session.save()
143 h.flash(_("Successfully updated password"), category='success')
142 h.flash(_("Successfully updated password"), category='success')
144
143
145 raise HTTPFound(self.request.route_path('my_account_password'))
144 raise HTTPFound(self.request.route_path('my_account_password'))
146
145
147 @LoginRequired()
146 @LoginRequired()
148 @NotAnonymous()
147 @NotAnonymous()
149 @view_config(
148 @view_config(
150 route_name='my_account_auth_tokens', request_method='GET',
149 route_name='my_account_auth_tokens', request_method='GET',
151 renderer='rhodecode:templates/admin/my_account/my_account.mako')
150 renderer='rhodecode:templates/admin/my_account/my_account.mako')
152 def my_account_auth_tokens(self):
151 def my_account_auth_tokens(self):
153 _ = self.request.translate
152 _ = self.request.translate
154
153
155 c = self.load_default_context()
154 c = self.load_default_context()
156 c.active = 'auth_tokens'
155 c.active = 'auth_tokens'
157 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
156 c.lifetime_values = AuthTokenModel.get_lifetime_values(translator=_)
158 c.role_values = [
157 c.role_values = [
159 (x, AuthTokenModel.cls._get_role_name(x))
158 (x, AuthTokenModel.cls._get_role_name(x))
160 for x in AuthTokenModel.cls.ROLES]
159 for x in AuthTokenModel.cls.ROLES]
161 c.role_options = [(c.role_values, _("Role"))]
160 c.role_options = [(c.role_values, _("Role"))]
162 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
161 c.user_auth_tokens = AuthTokenModel().get_auth_tokens(
163 c.user.user_id, show_expired=True)
162 c.user.user_id, show_expired=True)
164 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
163 c.role_vcs = AuthTokenModel.cls.ROLE_VCS
165 return self._get_template_context(c)
164 return self._get_template_context(c)
166
165
167 def maybe_attach_token_scope(self, token):
166 def maybe_attach_token_scope(self, token):
168 # implemented in EE edition
167 # implemented in EE edition
169 pass
168 pass
170
169
171 @LoginRequired()
170 @LoginRequired()
172 @NotAnonymous()
171 @NotAnonymous()
173 @CSRFRequired()
172 @CSRFRequired()
174 @view_config(
173 @view_config(
175 route_name='my_account_auth_tokens_add', request_method='POST',)
174 route_name='my_account_auth_tokens_add', request_method='POST',)
176 def my_account_auth_tokens_add(self):
175 def my_account_auth_tokens_add(self):
177 _ = self.request.translate
176 _ = self.request.translate
178 c = self.load_default_context()
177 c = self.load_default_context()
179
178
180 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
179 lifetime = safe_int(self.request.POST.get('lifetime'), -1)
181 description = self.request.POST.get('description')
180 description = self.request.POST.get('description')
182 role = self.request.POST.get('role')
181 role = self.request.POST.get('role')
183
182
184 token = UserModel().add_auth_token(
183 token = UserModel().add_auth_token(
185 user=c.user.user_id,
184 user=c.user.user_id,
186 lifetime_minutes=lifetime, role=role, description=description,
185 lifetime_minutes=lifetime, role=role, description=description,
187 scope_callback=self.maybe_attach_token_scope)
186 scope_callback=self.maybe_attach_token_scope)
188 token_data = token.get_api_data()
187 token_data = token.get_api_data()
189
188
190 audit_logger.store_web(
189 audit_logger.store_web(
191 'user.edit.token.add', action_data={
190 'user.edit.token.add', action_data={
192 'data': {'token': token_data, 'user': 'self'}},
191 'data': {'token': token_data, 'user': 'self'}},
193 user=self._rhodecode_user, )
192 user=self._rhodecode_user, )
194 Session().commit()
193 Session().commit()
195
194
196 h.flash(_("Auth token successfully created"), category='success')
195 h.flash(_("Auth token successfully created"), category='success')
197 return HTTPFound(h.route_path('my_account_auth_tokens'))
196 return HTTPFound(h.route_path('my_account_auth_tokens'))
198
197
199 @LoginRequired()
198 @LoginRequired()
200 @NotAnonymous()
199 @NotAnonymous()
201 @CSRFRequired()
200 @CSRFRequired()
202 @view_config(
201 @view_config(
203 route_name='my_account_auth_tokens_delete', request_method='POST')
202 route_name='my_account_auth_tokens_delete', request_method='POST')
204 def my_account_auth_tokens_delete(self):
203 def my_account_auth_tokens_delete(self):
205 _ = self.request.translate
204 _ = self.request.translate
206 c = self.load_default_context()
205 c = self.load_default_context()
207
206
208 del_auth_token = self.request.POST.get('del_auth_token')
207 del_auth_token = self.request.POST.get('del_auth_token')
209
208
210 if del_auth_token:
209 if del_auth_token:
211 token = UserApiKeys.get_or_404(del_auth_token)
210 token = UserApiKeys.get_or_404(del_auth_token)
212 token_data = token.get_api_data()
211 token_data = token.get_api_data()
213
212
214 AuthTokenModel().delete(del_auth_token, c.user.user_id)
213 AuthTokenModel().delete(del_auth_token, c.user.user_id)
215 audit_logger.store_web(
214 audit_logger.store_web(
216 'user.edit.token.delete', action_data={
215 'user.edit.token.delete', action_data={
217 'data': {'token': token_data, 'user': 'self'}},
216 'data': {'token': token_data, 'user': 'self'}},
218 user=self._rhodecode_user,)
217 user=self._rhodecode_user,)
219 Session().commit()
218 Session().commit()
220 h.flash(_("Auth token successfully deleted"), category='success')
219 h.flash(_("Auth token successfully deleted"), category='success')
221
220
222 return HTTPFound(h.route_path('my_account_auth_tokens'))
221 return HTTPFound(h.route_path('my_account_auth_tokens'))
223
222
224 @LoginRequired()
223 @LoginRequired()
225 @NotAnonymous()
224 @NotAnonymous()
226 @view_config(
225 @view_config(
227 route_name='my_account_emails', request_method='GET',
226 route_name='my_account_emails', request_method='GET',
228 renderer='rhodecode:templates/admin/my_account/my_account.mako')
227 renderer='rhodecode:templates/admin/my_account/my_account.mako')
229 def my_account_emails(self):
228 def my_account_emails(self):
230 _ = self.request.translate
229 _ = self.request.translate
231
230
232 c = self.load_default_context()
231 c = self.load_default_context()
233 c.active = 'emails'
232 c.active = 'emails'
234
233
235 c.user_email_map = UserEmailMap.query()\
234 c.user_email_map = UserEmailMap.query()\
236 .filter(UserEmailMap.user == c.user).all()
235 .filter(UserEmailMap.user == c.user).all()
237
236
238 schema = user_schema.AddEmailSchema().bind(
237 schema = user_schema.AddEmailSchema().bind(
239 username=c.user.username, user_emails=c.user.emails)
238 username=c.user.username, user_emails=c.user.emails)
240
239
241 form = forms.RcForm(schema,
240 form = forms.RcForm(schema,
242 action=h.route_path('my_account_emails_add'),
241 action=h.route_path('my_account_emails_add'),
243 buttons=(forms.buttons.save, forms.buttons.reset))
242 buttons=(forms.buttons.save, forms.buttons.reset))
244
243
245 c.form = form
244 c.form = form
246 return self._get_template_context(c)
245 return self._get_template_context(c)
247
246
248 @LoginRequired()
247 @LoginRequired()
249 @NotAnonymous()
248 @NotAnonymous()
250 @CSRFRequired()
249 @CSRFRequired()
251 @view_config(
250 @view_config(
252 route_name='my_account_emails_add', request_method='POST',
251 route_name='my_account_emails_add', request_method='POST',
253 renderer='rhodecode:templates/admin/my_account/my_account.mako')
252 renderer='rhodecode:templates/admin/my_account/my_account.mako')
254 def my_account_emails_add(self):
253 def my_account_emails_add(self):
255 _ = self.request.translate
254 _ = self.request.translate
256 c = self.load_default_context()
255 c = self.load_default_context()
257 c.active = 'emails'
256 c.active = 'emails'
258
257
259 schema = user_schema.AddEmailSchema().bind(
258 schema = user_schema.AddEmailSchema().bind(
260 username=c.user.username, user_emails=c.user.emails)
259 username=c.user.username, user_emails=c.user.emails)
261
260
262 form = forms.RcForm(
261 form = forms.RcForm(
263 schema, action=h.route_path('my_account_emails_add'),
262 schema, action=h.route_path('my_account_emails_add'),
264 buttons=(forms.buttons.save, forms.buttons.reset))
263 buttons=(forms.buttons.save, forms.buttons.reset))
265
264
266 controls = self.request.POST.items()
265 controls = self.request.POST.items()
267 try:
266 try:
268 valid_data = form.validate(controls)
267 valid_data = form.validate(controls)
269 UserModel().add_extra_email(c.user.user_id, valid_data['email'])
268 UserModel().add_extra_email(c.user.user_id, valid_data['email'])
270 audit_logger.store_web(
269 audit_logger.store_web(
271 'user.edit.email.add', action_data={
270 'user.edit.email.add', action_data={
272 'data': {'email': valid_data['email'], 'user': 'self'}},
271 'data': {'email': valid_data['email'], 'user': 'self'}},
273 user=self._rhodecode_user,)
272 user=self._rhodecode_user,)
274 Session().commit()
273 Session().commit()
275 except formencode.Invalid as error:
274 except formencode.Invalid as error:
276 h.flash(h.escape(error.error_dict['email']), category='error')
275 h.flash(h.escape(error.error_dict['email']), category='error')
277 except forms.ValidationFailure as e:
276 except forms.ValidationFailure as e:
278 c.user_email_map = UserEmailMap.query() \
277 c.user_email_map = UserEmailMap.query() \
279 .filter(UserEmailMap.user == c.user).all()
278 .filter(UserEmailMap.user == c.user).all()
280 c.form = e
279 c.form = e
281 return self._get_template_context(c)
280 return self._get_template_context(c)
282 except Exception:
281 except Exception:
283 log.exception("Exception adding email")
282 log.exception("Exception adding email")
284 h.flash(_('Error occurred during adding email'),
283 h.flash(_('Error occurred during adding email'),
285 category='error')
284 category='error')
286 else:
285 else:
287 h.flash(_("Successfully added email"), category='success')
286 h.flash(_("Successfully added email"), category='success')
288
287
289 raise HTTPFound(self.request.route_path('my_account_emails'))
288 raise HTTPFound(self.request.route_path('my_account_emails'))
290
289
291 @LoginRequired()
290 @LoginRequired()
292 @NotAnonymous()
291 @NotAnonymous()
293 @CSRFRequired()
292 @CSRFRequired()
294 @view_config(
293 @view_config(
295 route_name='my_account_emails_delete', request_method='POST')
294 route_name='my_account_emails_delete', request_method='POST')
296 def my_account_emails_delete(self):
295 def my_account_emails_delete(self):
297 _ = self.request.translate
296 _ = self.request.translate
298 c = self.load_default_context()
297 c = self.load_default_context()
299
298
300 del_email_id = self.request.POST.get('del_email_id')
299 del_email_id = self.request.POST.get('del_email_id')
301 if del_email_id:
300 if del_email_id:
302 email = UserEmailMap.get_or_404(del_email_id).email
301 email = UserEmailMap.get_or_404(del_email_id).email
303 UserModel().delete_extra_email(c.user.user_id, del_email_id)
302 UserModel().delete_extra_email(c.user.user_id, del_email_id)
304 audit_logger.store_web(
303 audit_logger.store_web(
305 'user.edit.email.delete', action_data={
304 'user.edit.email.delete', action_data={
306 'data': {'email': email, 'user': 'self'}},
305 'data': {'email': email, 'user': 'self'}},
307 user=self._rhodecode_user,)
306 user=self._rhodecode_user,)
308 Session().commit()
307 Session().commit()
309 h.flash(_("Email successfully deleted"),
308 h.flash(_("Email successfully deleted"),
310 category='success')
309 category='success')
311 return HTTPFound(h.route_path('my_account_emails'))
310 return HTTPFound(h.route_path('my_account_emails'))
312
311
313 @LoginRequired()
312 @LoginRequired()
314 @NotAnonymous()
313 @NotAnonymous()
315 @CSRFRequired()
314 @CSRFRequired()
316 @view_config(
315 @view_config(
317 route_name='my_account_notifications_test_channelstream',
316 route_name='my_account_notifications_test_channelstream',
318 request_method='POST', renderer='json_ext')
317 request_method='POST', renderer='json_ext')
319 def my_account_notifications_test_channelstream(self):
318 def my_account_notifications_test_channelstream(self):
320 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
319 message = 'Test message sent via Channelstream by user: {}, on {}'.format(
321 self._rhodecode_user.username, datetime.datetime.now())
320 self._rhodecode_user.username, datetime.datetime.now())
322 payload = {
321 payload = {
323 # 'channel': 'broadcast',
322 # 'channel': 'broadcast',
324 'type': 'message',
323 'type': 'message',
325 'timestamp': datetime.datetime.utcnow(),
324 'timestamp': datetime.datetime.utcnow(),
326 'user': 'system',
325 'user': 'system',
327 'pm_users': [self._rhodecode_user.username],
326 'pm_users': [self._rhodecode_user.username],
328 'message': {
327 'message': {
329 'message': message,
328 'message': message,
330 'level': 'info',
329 'level': 'info',
331 'topic': '/notifications'
330 'topic': '/notifications'
332 }
331 }
333 }
332 }
334
333
335 registry = self.request.registry
334 registry = self.request.registry
336 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
335 rhodecode_plugins = getattr(registry, 'rhodecode_plugins', {})
337 channelstream_config = rhodecode_plugins.get('channelstream', {})
336 channelstream_config = rhodecode_plugins.get('channelstream', {})
338
337
339 try:
338 try:
340 channelstream_request(channelstream_config, [payload], '/message')
339 channelstream_request(channelstream_config, [payload], '/message')
341 except ChannelstreamException as e:
340 except ChannelstreamException as e:
342 log.exception('Failed to send channelstream data')
341 log.exception('Failed to send channelstream data')
343 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
342 return {"response": 'ERROR: {}'.format(e.__class__.__name__)}
344 return {"response": 'Channelstream data sent. '
343 return {"response": 'Channelstream data sent. '
345 'You should see a new live message now.'}
344 'You should see a new live message now.'}
346
345
347 def _load_my_repos_data(self, watched=False):
346 def _load_my_repos_data(self, watched=False):
347
348 allowed_ids = [-1] + self._rhodecode_user.repo_acl_ids_from_stack(AuthUser.repo_read_perms)
349
348 if watched:
350 if watched:
349 admin = False
351 # repos user watch
350 follows_repos = Session().query(UserFollowing)\
352 repo_list = Session().query(
351 .filter(UserFollowing.user_id == self._rhodecode_user.user_id)\
353 Repository
352 .options(joinedload(UserFollowing.follows_repository))\
354 ) \
355 .join(
356 (UserFollowing, UserFollowing.follows_repo_id == Repository.repo_id)
357 ) \
358 .filter(
359 UserFollowing.user_id == self._rhodecode_user.user_id
360 ) \
361 .filter(or_(
362 # generate multiple IN to fix limitation problems
363 *in_filter_generator(Repository.repo_id, allowed_ids))
364 ) \
365 .order_by(Repository.repo_name) \
353 .all()
366 .all()
354 repo_list = [x.follows_repository for x in follows_repos]
367
355 else:
368 else:
356 admin = True
369 # repos user is owner of
357 repo_list = Repository.get_all_repos(
370 repo_list = Session().query(
358 user_id=self._rhodecode_user.user_id)
371 Repository
359 repo_list = RepoList(repo_list, perm_set=[
372 ) \
360 'repository.read', 'repository.write', 'repository.admin'],
373 .filter(
361 extra_kwargs=dict(user=self._rhodecode_user))
374 Repository.user_id == self._rhodecode_user.user_id
375 ) \
376 .filter(or_(
377 # generate multiple IN to fix limitation problems
378 *in_filter_generator(Repository.repo_id, allowed_ids))
379 ) \
380 .order_by(Repository.repo_name) \
381 .all()
362
382
363 repos_data = RepoModel().get_repos_as_dict(
383 _render = self.request.get_partial_renderer(
364 repo_list=repo_list, admin=admin, short_name=False)
384 'rhodecode:templates/data_table/_dt_elements.mako')
385
386 def repo_lnk(name, rtype, rstate, private, archived, fork_of):
387 return _render('repo_name', name, rtype, rstate, private, archived, fork_of,
388 short_name=False, admin=False)
389
390 repos_data = []
391 for repo in repo_list:
392 row = {
393 "name": repo_lnk(repo.repo_name, repo.repo_type, repo.repo_state,
394 repo.private, repo.archived, repo.fork),
395 "name_raw": repo.repo_name.lower(),
396 }
397
398 repos_data.append(row)
399
365 # json used to render the grid
400 # json used to render the grid
366 return json.dumps(repos_data)
401 return json.dumps(repos_data)
367
402
368 @LoginRequired()
403 @LoginRequired()
369 @NotAnonymous()
404 @NotAnonymous()
370 @view_config(
405 @view_config(
371 route_name='my_account_repos', request_method='GET',
406 route_name='my_account_repos', request_method='GET',
372 renderer='rhodecode:templates/admin/my_account/my_account.mako')
407 renderer='rhodecode:templates/admin/my_account/my_account.mako')
373 def my_account_repos(self):
408 def my_account_repos(self):
374 c = self.load_default_context()
409 c = self.load_default_context()
375 c.active = 'repos'
410 c.active = 'repos'
376
411
377 # json used to render the grid
412 # json used to render the grid
378 c.data = self._load_my_repos_data()
413 c.data = self._load_my_repos_data()
379 return self._get_template_context(c)
414 return self._get_template_context(c)
380
415
381 @LoginRequired()
416 @LoginRequired()
382 @NotAnonymous()
417 @NotAnonymous()
383 @view_config(
418 @view_config(
384 route_name='my_account_watched', request_method='GET',
419 route_name='my_account_watched', request_method='GET',
385 renderer='rhodecode:templates/admin/my_account/my_account.mako')
420 renderer='rhodecode:templates/admin/my_account/my_account.mako')
386 def my_account_watched(self):
421 def my_account_watched(self):
387 c = self.load_default_context()
422 c = self.load_default_context()
388 c.active = 'watched'
423 c.active = 'watched'
389
424
390 # json used to render the grid
425 # json used to render the grid
391 c.data = self._load_my_repos_data(watched=True)
426 c.data = self._load_my_repos_data(watched=True)
392 return self._get_template_context(c)
427 return self._get_template_context(c)
393
428
394 @LoginRequired()
429 @LoginRequired()
395 @NotAnonymous()
430 @NotAnonymous()
396 @view_config(
431 @view_config(
397 route_name='my_account_bookmarks', request_method='GET',
432 route_name='my_account_bookmarks', request_method='GET',
398 renderer='rhodecode:templates/admin/my_account/my_account.mako')
433 renderer='rhodecode:templates/admin/my_account/my_account.mako')
399 def my_account_bookmarks(self):
434 def my_account_bookmarks(self):
400 c = self.load_default_context()
435 c = self.load_default_context()
401 c.active = 'bookmarks'
436 c.active = 'bookmarks'
402 c.bookmark_items = UserBookmark.get_bookmarks_for_user(
437 c.bookmark_items = UserBookmark.get_bookmarks_for_user(
403 self._rhodecode_db_user.user_id, cache=False)
438 self._rhodecode_db_user.user_id, cache=False)
404 return self._get_template_context(c)
439 return self._get_template_context(c)
405
440
406 def _process_bookmark_entry(self, entry, user_id):
441 def _process_bookmark_entry(self, entry, user_id):
407 position = safe_int(entry.get('position'))
442 position = safe_int(entry.get('position'))
408 cur_position = safe_int(entry.get('cur_position'))
443 cur_position = safe_int(entry.get('cur_position'))
409 if position is None:
444 if position is None:
410 return
445 return
411
446
412 # check if this is an existing entry
447 # check if this is an existing entry
413 is_new = False
448 is_new = False
414 db_entry = UserBookmark().get_by_position_for_user(cur_position, user_id)
449 db_entry = UserBookmark().get_by_position_for_user(cur_position, user_id)
415
450
416 if db_entry and str2bool(entry.get('remove')):
451 if db_entry and str2bool(entry.get('remove')):
417 log.debug('Marked bookmark %s for deletion', db_entry)
452 log.debug('Marked bookmark %s for deletion', db_entry)
418 Session().delete(db_entry)
453 Session().delete(db_entry)
419 return
454 return
420
455
421 if not db_entry:
456 if not db_entry:
422 # new
457 # new
423 db_entry = UserBookmark()
458 db_entry = UserBookmark()
424 is_new = True
459 is_new = True
425
460
426 should_save = False
461 should_save = False
427 default_redirect_url = ''
462 default_redirect_url = ''
428
463
429 # save repo
464 # save repo
430 if entry.get('bookmark_repo') and safe_int(entry.get('bookmark_repo')):
465 if entry.get('bookmark_repo') and safe_int(entry.get('bookmark_repo')):
431 repo = Repository.get(entry['bookmark_repo'])
466 repo = Repository.get(entry['bookmark_repo'])
432 perm_check = HasRepoPermissionAny(
467 perm_check = HasRepoPermissionAny(
433 'repository.read', 'repository.write', 'repository.admin')
468 'repository.read', 'repository.write', 'repository.admin')
434 if repo and perm_check(repo_name=repo.repo_name):
469 if repo and perm_check(repo_name=repo.repo_name):
435 db_entry.repository = repo
470 db_entry.repository = repo
436 should_save = True
471 should_save = True
437 default_redirect_url = '${repo_url}'
472 default_redirect_url = '${repo_url}'
438 # save repo group
473 # save repo group
439 elif entry.get('bookmark_repo_group') and safe_int(entry.get('bookmark_repo_group')):
474 elif entry.get('bookmark_repo_group') and safe_int(entry.get('bookmark_repo_group')):
440 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
475 repo_group = RepoGroup.get(entry['bookmark_repo_group'])
441 perm_check = HasRepoGroupPermissionAny(
476 perm_check = HasRepoGroupPermissionAny(
442 'group.read', 'group.write', 'group.admin')
477 'group.read', 'group.write', 'group.admin')
443
478
444 if repo_group and perm_check(group_name=repo_group.group_name):
479 if repo_group and perm_check(group_name=repo_group.group_name):
445 db_entry.repository_group = repo_group
480 db_entry.repository_group = repo_group
446 should_save = True
481 should_save = True
447 default_redirect_url = '${repo_group_url}'
482 default_redirect_url = '${repo_group_url}'
448 # save generic info
483 # save generic info
449 elif entry.get('title') and entry.get('redirect_url'):
484 elif entry.get('title') and entry.get('redirect_url'):
450 should_save = True
485 should_save = True
451
486
452 if should_save:
487 if should_save:
453 # mark user and position
488 # mark user and position
454 db_entry.user_id = user_id
489 db_entry.user_id = user_id
455 db_entry.position = position
490 db_entry.position = position
456 db_entry.title = entry.get('title')
491 db_entry.title = entry.get('title')
457 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
492 db_entry.redirect_url = entry.get('redirect_url') or default_redirect_url
458 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
493 log.debug('Saving bookmark %s, new:%s', db_entry, is_new)
459
494
460 Session().add(db_entry)
495 Session().add(db_entry)
461
496
462 @LoginRequired()
497 @LoginRequired()
463 @NotAnonymous()
498 @NotAnonymous()
464 @CSRFRequired()
499 @CSRFRequired()
465 @view_config(
500 @view_config(
466 route_name='my_account_bookmarks_update', request_method='POST')
501 route_name='my_account_bookmarks_update', request_method='POST')
467 def my_account_bookmarks_update(self):
502 def my_account_bookmarks_update(self):
468 _ = self.request.translate
503 _ = self.request.translate
469 c = self.load_default_context()
504 c = self.load_default_context()
470 c.active = 'bookmarks'
505 c.active = 'bookmarks'
471
506
472 controls = peppercorn.parse(self.request.POST.items())
507 controls = peppercorn.parse(self.request.POST.items())
473 user_id = c.user.user_id
508 user_id = c.user.user_id
474
509
475 # validate positions
510 # validate positions
476 positions = {}
511 positions = {}
477 for entry in controls.get('bookmarks', []):
512 for entry in controls.get('bookmarks', []):
478 position = safe_int(entry['position'])
513 position = safe_int(entry['position'])
479 if position is None:
514 if position is None:
480 continue
515 continue
481
516
482 if position in positions:
517 if position in positions:
483 h.flash(_("Position {} is defined twice. "
518 h.flash(_("Position {} is defined twice. "
484 "Please correct this error.").format(position), category='error')
519 "Please correct this error.").format(position), category='error')
485 return HTTPFound(h.route_path('my_account_bookmarks'))
520 return HTTPFound(h.route_path('my_account_bookmarks'))
486
521
487 entry['position'] = position
522 entry['position'] = position
488 entry['cur_position'] = safe_int(entry.get('cur_position'))
523 entry['cur_position'] = safe_int(entry.get('cur_position'))
489 positions[position] = entry
524 positions[position] = entry
490
525
491 try:
526 try:
492 for entry in positions.values():
527 for entry in positions.values():
493 self._process_bookmark_entry(entry, user_id)
528 self._process_bookmark_entry(entry, user_id)
494
529
495 Session().commit()
530 Session().commit()
496 h.flash(_("Update Bookmarks"), category='success')
531 h.flash(_("Update Bookmarks"), category='success')
497 except IntegrityError:
532 except IntegrityError:
498 h.flash(_("Failed to update bookmarks. "
533 h.flash(_("Failed to update bookmarks. "
499 "Make sure an unique position is used."), category='error')
534 "Make sure an unique position is used."), category='error')
500
535
501 return HTTPFound(h.route_path('my_account_bookmarks'))
536 return HTTPFound(h.route_path('my_account_bookmarks'))
502
537
503 @LoginRequired()
538 @LoginRequired()
504 @NotAnonymous()
539 @NotAnonymous()
505 @view_config(
540 @view_config(
506 route_name='my_account_goto_bookmark', request_method='GET',
541 route_name='my_account_goto_bookmark', request_method='GET',
507 renderer='rhodecode:templates/admin/my_account/my_account.mako')
542 renderer='rhodecode:templates/admin/my_account/my_account.mako')
508 def my_account_goto_bookmark(self):
543 def my_account_goto_bookmark(self):
509
544
510 bookmark_id = self.request.matchdict['bookmark_id']
545 bookmark_id = self.request.matchdict['bookmark_id']
511 user_bookmark = UserBookmark().query()\
546 user_bookmark = UserBookmark().query()\
512 .filter(UserBookmark.user_id == self.request.user.user_id) \
547 .filter(UserBookmark.user_id == self.request.user.user_id) \
513 .filter(UserBookmark.position == bookmark_id).scalar()
548 .filter(UserBookmark.position == bookmark_id).scalar()
514
549
515 redirect_url = h.route_path('my_account_bookmarks')
550 redirect_url = h.route_path('my_account_bookmarks')
516 if not user_bookmark:
551 if not user_bookmark:
517 raise HTTPFound(redirect_url)
552 raise HTTPFound(redirect_url)
518
553
519 # repository set
554 # repository set
520 if user_bookmark.repository:
555 if user_bookmark.repository:
521 repo_name = user_bookmark.repository.repo_name
556 repo_name = user_bookmark.repository.repo_name
522 base_redirect_url = h.route_path(
557 base_redirect_url = h.route_path(
523 'repo_summary', repo_name=repo_name)
558 'repo_summary', repo_name=repo_name)
524 if user_bookmark.redirect_url and \
559 if user_bookmark.redirect_url and \
525 '${repo_url}' in user_bookmark.redirect_url:
560 '${repo_url}' in user_bookmark.redirect_url:
526 redirect_url = string.Template(user_bookmark.redirect_url)\
561 redirect_url = string.Template(user_bookmark.redirect_url)\
527 .safe_substitute({'repo_url': base_redirect_url})
562 .safe_substitute({'repo_url': base_redirect_url})
528 else:
563 else:
529 redirect_url = base_redirect_url
564 redirect_url = base_redirect_url
530 # repository group set
565 # repository group set
531 elif user_bookmark.repository_group:
566 elif user_bookmark.repository_group:
532 repo_group_name = user_bookmark.repository_group.group_name
567 repo_group_name = user_bookmark.repository_group.group_name
533 base_redirect_url = h.route_path(
568 base_redirect_url = h.route_path(
534 'repo_group_home', repo_group_name=repo_group_name)
569 'repo_group_home', repo_group_name=repo_group_name)
535 if user_bookmark.redirect_url and \
570 if user_bookmark.redirect_url and \
536 '${repo_group_url}' in user_bookmark.redirect_url:
571 '${repo_group_url}' in user_bookmark.redirect_url:
537 redirect_url = string.Template(user_bookmark.redirect_url)\
572 redirect_url = string.Template(user_bookmark.redirect_url)\
538 .safe_substitute({'repo_group_url': base_redirect_url})
573 .safe_substitute({'repo_group_url': base_redirect_url})
539 else:
574 else:
540 redirect_url = base_redirect_url
575 redirect_url = base_redirect_url
541 # custom URL set
576 # custom URL set
542 elif user_bookmark.redirect_url:
577 elif user_bookmark.redirect_url:
543 server_url = h.route_url('home').rstrip('/')
578 server_url = h.route_url('home').rstrip('/')
544 redirect_url = string.Template(user_bookmark.redirect_url) \
579 redirect_url = string.Template(user_bookmark.redirect_url) \
545 .safe_substitute({'server_url': server_url})
580 .safe_substitute({'server_url': server_url})
546
581
547 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
582 log.debug('Redirecting bookmark %s to %s', user_bookmark, redirect_url)
548 raise HTTPFound(redirect_url)
583 raise HTTPFound(redirect_url)
549
584
550 @LoginRequired()
585 @LoginRequired()
551 @NotAnonymous()
586 @NotAnonymous()
552 @view_config(
587 @view_config(
553 route_name='my_account_perms', request_method='GET',
588 route_name='my_account_perms', request_method='GET',
554 renderer='rhodecode:templates/admin/my_account/my_account.mako')
589 renderer='rhodecode:templates/admin/my_account/my_account.mako')
555 def my_account_perms(self):
590 def my_account_perms(self):
556 c = self.load_default_context()
591 c = self.load_default_context()
557 c.active = 'perms'
592 c.active = 'perms'
558
593
559 c.perm_user = c.auth_user
594 c.perm_user = c.auth_user
560 return self._get_template_context(c)
595 return self._get_template_context(c)
561
596
562 @LoginRequired()
597 @LoginRequired()
563 @NotAnonymous()
598 @NotAnonymous()
564 @view_config(
599 @view_config(
565 route_name='my_account_notifications', request_method='GET',
600 route_name='my_account_notifications', request_method='GET',
566 renderer='rhodecode:templates/admin/my_account/my_account.mako')
601 renderer='rhodecode:templates/admin/my_account/my_account.mako')
567 def my_notifications(self):
602 def my_notifications(self):
568 c = self.load_default_context()
603 c = self.load_default_context()
569 c.active = 'notifications'
604 c.active = 'notifications'
570
605
571 return self._get_template_context(c)
606 return self._get_template_context(c)
572
607
573 @LoginRequired()
608 @LoginRequired()
574 @NotAnonymous()
609 @NotAnonymous()
575 @CSRFRequired()
610 @CSRFRequired()
576 @view_config(
611 @view_config(
577 route_name='my_account_notifications_toggle_visibility',
612 route_name='my_account_notifications_toggle_visibility',
578 request_method='POST', renderer='json_ext')
613 request_method='POST', renderer='json_ext')
579 def my_notifications_toggle_visibility(self):
614 def my_notifications_toggle_visibility(self):
580 user = self._rhodecode_db_user
615 user = self._rhodecode_db_user
581 new_status = not user.user_data.get('notification_status', True)
616 new_status = not user.user_data.get('notification_status', True)
582 user.update_userdata(notification_status=new_status)
617 user.update_userdata(notification_status=new_status)
583 Session().commit()
618 Session().commit()
584 return user.user_data['notification_status']
619 return user.user_data['notification_status']
585
620
586 @LoginRequired()
621 @LoginRequired()
587 @NotAnonymous()
622 @NotAnonymous()
588 @view_config(
623 @view_config(
589 route_name='my_account_edit',
624 route_name='my_account_edit',
590 request_method='GET',
625 request_method='GET',
591 renderer='rhodecode:templates/admin/my_account/my_account.mako')
626 renderer='rhodecode:templates/admin/my_account/my_account.mako')
592 def my_account_edit(self):
627 def my_account_edit(self):
593 c = self.load_default_context()
628 c = self.load_default_context()
594 c.active = 'profile_edit'
629 c.active = 'profile_edit'
595 c.extern_type = c.user.extern_type
630 c.extern_type = c.user.extern_type
596 c.extern_name = c.user.extern_name
631 c.extern_name = c.user.extern_name
597
632
598 schema = user_schema.UserProfileSchema().bind(
633 schema = user_schema.UserProfileSchema().bind(
599 username=c.user.username, user_emails=c.user.emails)
634 username=c.user.username, user_emails=c.user.emails)
600 appstruct = {
635 appstruct = {
601 'username': c.user.username,
636 'username': c.user.username,
602 'email': c.user.email,
637 'email': c.user.email,
603 'firstname': c.user.firstname,
638 'firstname': c.user.firstname,
604 'lastname': c.user.lastname,
639 'lastname': c.user.lastname,
605 'description': c.user.description,
640 'description': c.user.description,
606 }
641 }
607 c.form = forms.RcForm(
642 c.form = forms.RcForm(
608 schema, appstruct=appstruct,
643 schema, appstruct=appstruct,
609 action=h.route_path('my_account_update'),
644 action=h.route_path('my_account_update'),
610 buttons=(forms.buttons.save, forms.buttons.reset))
645 buttons=(forms.buttons.save, forms.buttons.reset))
611
646
612 return self._get_template_context(c)
647 return self._get_template_context(c)
613
648
614 @LoginRequired()
649 @LoginRequired()
615 @NotAnonymous()
650 @NotAnonymous()
616 @CSRFRequired()
651 @CSRFRequired()
617 @view_config(
652 @view_config(
618 route_name='my_account_update',
653 route_name='my_account_update',
619 request_method='POST',
654 request_method='POST',
620 renderer='rhodecode:templates/admin/my_account/my_account.mako')
655 renderer='rhodecode:templates/admin/my_account/my_account.mako')
621 def my_account_update(self):
656 def my_account_update(self):
622 _ = self.request.translate
657 _ = self.request.translate
623 c = self.load_default_context()
658 c = self.load_default_context()
624 c.active = 'profile_edit'
659 c.active = 'profile_edit'
625 c.perm_user = c.auth_user
660 c.perm_user = c.auth_user
626 c.extern_type = c.user.extern_type
661 c.extern_type = c.user.extern_type
627 c.extern_name = c.user.extern_name
662 c.extern_name = c.user.extern_name
628
663
629 schema = user_schema.UserProfileSchema().bind(
664 schema = user_schema.UserProfileSchema().bind(
630 username=c.user.username, user_emails=c.user.emails)
665 username=c.user.username, user_emails=c.user.emails)
631 form = forms.RcForm(
666 form = forms.RcForm(
632 schema, buttons=(forms.buttons.save, forms.buttons.reset))
667 schema, buttons=(forms.buttons.save, forms.buttons.reset))
633
668
634 controls = self.request.POST.items()
669 controls = self.request.POST.items()
635 try:
670 try:
636 valid_data = form.validate(controls)
671 valid_data = form.validate(controls)
637 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
672 skip_attrs = ['admin', 'active', 'extern_type', 'extern_name',
638 'new_password', 'password_confirmation']
673 'new_password', 'password_confirmation']
639 if c.extern_type != "rhodecode":
674 if c.extern_type != "rhodecode":
640 # forbid updating username for external accounts
675 # forbid updating username for external accounts
641 skip_attrs.append('username')
676 skip_attrs.append('username')
642 old_email = c.user.email
677 old_email = c.user.email
643 UserModel().update_user(
678 UserModel().update_user(
644 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
679 self._rhodecode_user.user_id, skip_attrs=skip_attrs,
645 **valid_data)
680 **valid_data)
646 if old_email != valid_data['email']:
681 if old_email != valid_data['email']:
647 old = UserEmailMap.query() \
682 old = UserEmailMap.query() \
648 .filter(UserEmailMap.user == c.user).filter(UserEmailMap.email == valid_data['email']).first()
683 .filter(UserEmailMap.user == c.user).filter(UserEmailMap.email == valid_data['email']).first()
649 old.email = old_email
684 old.email = old_email
650 h.flash(_('Your account was updated successfully'), category='success')
685 h.flash(_('Your account was updated successfully'), category='success')
651 Session().commit()
686 Session().commit()
652 except forms.ValidationFailure as e:
687 except forms.ValidationFailure as e:
653 c.form = e
688 c.form = e
654 return self._get_template_context(c)
689 return self._get_template_context(c)
655 except Exception:
690 except Exception:
656 log.exception("Exception updating user")
691 log.exception("Exception updating user")
657 h.flash(_('Error occurred during update of user'),
692 h.flash(_('Error occurred during update of user'),
658 category='error')
693 category='error')
659 raise HTTPFound(h.route_path('my_account_profile'))
694 raise HTTPFound(h.route_path('my_account_profile'))
660
695
661 def _get_pull_requests_list(self, statuses):
696 def _get_pull_requests_list(self, statuses):
662 draw, start, limit = self._extract_chunk(self.request)
697 draw, start, limit = self._extract_chunk(self.request)
663 search_q, order_by, order_dir = self._extract_ordering(self.request)
698 search_q, order_by, order_dir = self._extract_ordering(self.request)
664 _render = self.request.get_partial_renderer(
699 _render = self.request.get_partial_renderer(
665 'rhodecode:templates/data_table/_dt_elements.mako')
700 'rhodecode:templates/data_table/_dt_elements.mako')
666
701
667 pull_requests = PullRequestModel().get_im_participating_in(
702 pull_requests = PullRequestModel().get_im_participating_in(
668 user_id=self._rhodecode_user.user_id,
703 user_id=self._rhodecode_user.user_id,
669 statuses=statuses,
704 statuses=statuses,
670 offset=start, length=limit, order_by=order_by,
705 offset=start, length=limit, order_by=order_by,
671 order_dir=order_dir)
706 order_dir=order_dir)
672
707
673 pull_requests_total_count = PullRequestModel().count_im_participating_in(
708 pull_requests_total_count = PullRequestModel().count_im_participating_in(
674 user_id=self._rhodecode_user.user_id, statuses=statuses)
709 user_id=self._rhodecode_user.user_id, statuses=statuses)
675
710
676 data = []
711 data = []
677 comments_model = CommentsModel()
712 comments_model = CommentsModel()
678 for pr in pull_requests:
713 for pr in pull_requests:
679 repo_id = pr.target_repo_id
714 repo_id = pr.target_repo_id
680 comments = comments_model.get_all_comments(
715 comments = comments_model.get_all_comments(
681 repo_id, pull_request=pr)
716 repo_id, pull_request=pr)
682 owned = pr.user_id == self._rhodecode_user.user_id
717 owned = pr.user_id == self._rhodecode_user.user_id
683
718
684 data.append({
719 data.append({
685 'target_repo': _render('pullrequest_target_repo',
720 'target_repo': _render('pullrequest_target_repo',
686 pr.target_repo.repo_name),
721 pr.target_repo.repo_name),
687 'name': _render('pullrequest_name',
722 'name': _render('pullrequest_name',
688 pr.pull_request_id, pr.pull_request_state,
723 pr.pull_request_id, pr.pull_request_state,
689 pr.work_in_progress, pr.target_repo.repo_name,
724 pr.work_in_progress, pr.target_repo.repo_name,
690 short=True),
725 short=True),
691 'name_raw': pr.pull_request_id,
726 'name_raw': pr.pull_request_id,
692 'status': _render('pullrequest_status',
727 'status': _render('pullrequest_status',
693 pr.calculated_review_status()),
728 pr.calculated_review_status()),
694 'title': _render('pullrequest_title', pr.title, pr.description),
729 'title': _render('pullrequest_title', pr.title, pr.description),
695 'description': h.escape(pr.description),
730 'description': h.escape(pr.description),
696 'updated_on': _render('pullrequest_updated_on',
731 'updated_on': _render('pullrequest_updated_on',
697 h.datetime_to_time(pr.updated_on)),
732 h.datetime_to_time(pr.updated_on)),
698 'updated_on_raw': h.datetime_to_time(pr.updated_on),
733 'updated_on_raw': h.datetime_to_time(pr.updated_on),
699 'created_on': _render('pullrequest_updated_on',
734 'created_on': _render('pullrequest_updated_on',
700 h.datetime_to_time(pr.created_on)),
735 h.datetime_to_time(pr.created_on)),
701 'created_on_raw': h.datetime_to_time(pr.created_on),
736 'created_on_raw': h.datetime_to_time(pr.created_on),
702 'state': pr.pull_request_state,
737 'state': pr.pull_request_state,
703 'author': _render('pullrequest_author',
738 'author': _render('pullrequest_author',
704 pr.author.full_contact, ),
739 pr.author.full_contact, ),
705 'author_raw': pr.author.full_name,
740 'author_raw': pr.author.full_name,
706 'comments': _render('pullrequest_comments', len(comments)),
741 'comments': _render('pullrequest_comments', len(comments)),
707 'comments_raw': len(comments),
742 'comments_raw': len(comments),
708 'closed': pr.is_closed(),
743 'closed': pr.is_closed(),
709 'owned': owned
744 'owned': owned
710 })
745 })
711
746
712 # json used to render the grid
747 # json used to render the grid
713 data = ({
748 data = ({
714 'draw': draw,
749 'draw': draw,
715 'data': data,
750 'data': data,
716 'recordsTotal': pull_requests_total_count,
751 'recordsTotal': pull_requests_total_count,
717 'recordsFiltered': pull_requests_total_count,
752 'recordsFiltered': pull_requests_total_count,
718 })
753 })
719 return data
754 return data
720
755
721 @LoginRequired()
756 @LoginRequired()
722 @NotAnonymous()
757 @NotAnonymous()
723 @view_config(
758 @view_config(
724 route_name='my_account_pullrequests',
759 route_name='my_account_pullrequests',
725 request_method='GET',
760 request_method='GET',
726 renderer='rhodecode:templates/admin/my_account/my_account.mako')
761 renderer='rhodecode:templates/admin/my_account/my_account.mako')
727 def my_account_pullrequests(self):
762 def my_account_pullrequests(self):
728 c = self.load_default_context()
763 c = self.load_default_context()
729 c.active = 'pullrequests'
764 c.active = 'pullrequests'
730 req_get = self.request.GET
765 req_get = self.request.GET
731
766
732 c.closed = str2bool(req_get.get('pr_show_closed'))
767 c.closed = str2bool(req_get.get('pr_show_closed'))
733
768
734 return self._get_template_context(c)
769 return self._get_template_context(c)
735
770
736 @LoginRequired()
771 @LoginRequired()
737 @NotAnonymous()
772 @NotAnonymous()
738 @view_config(
773 @view_config(
739 route_name='my_account_pullrequests_data',
774 route_name='my_account_pullrequests_data',
740 request_method='GET', renderer='json_ext')
775 request_method='GET', renderer='json_ext')
741 def my_account_pullrequests_data(self):
776 def my_account_pullrequests_data(self):
742 self.load_default_context()
777 self.load_default_context()
743 req_get = self.request.GET
778 req_get = self.request.GET
744 closed = str2bool(req_get.get('closed'))
779 closed = str2bool(req_get.get('closed'))
745
780
746 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
781 statuses = [PullRequest.STATUS_NEW, PullRequest.STATUS_OPEN]
747 if closed:
782 if closed:
748 statuses += [PullRequest.STATUS_CLOSED]
783 statuses += [PullRequest.STATUS_CLOSED]
749
784
750 data = self._get_pull_requests_list(statuses=statuses)
785 data = self._get_pull_requests_list(statuses=statuses)
751 return data
786 return data
752
787
753 @LoginRequired()
788 @LoginRequired()
754 @NotAnonymous()
789 @NotAnonymous()
755 @view_config(
790 @view_config(
756 route_name='my_account_user_group_membership',
791 route_name='my_account_user_group_membership',
757 request_method='GET',
792 request_method='GET',
758 renderer='rhodecode:templates/admin/my_account/my_account.mako')
793 renderer='rhodecode:templates/admin/my_account/my_account.mako')
759 def my_account_user_group_membership(self):
794 def my_account_user_group_membership(self):
760 c = self.load_default_context()
795 c = self.load_default_context()
761 c.active = 'user_group_membership'
796 c.active = 'user_group_membership'
762 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
797 groups = [UserGroupModel.get_user_groups_as_dict(group.users_group)
763 for group in self._rhodecode_db_user.group_member]
798 for group in self._rhodecode_db_user.group_member]
764 c.user_groups = json.dumps(groups)
799 c.user_groups = json.dumps(groups)
765 return self._get_template_context(c)
800 return self._get_template_context(c)
@@ -1,2410 +1,2412 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2019 RhodeCode GmbH
3 # Copyright (C) 2010-2019 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 authentication and permission libraries
22 authentication and permission libraries
23 """
23 """
24
24
25 import os
25 import os
26 import time
26 import time
27 import inspect
27 import inspect
28 import collections
28 import collections
29 import fnmatch
29 import fnmatch
30 import hashlib
30 import hashlib
31 import itertools
31 import itertools
32 import logging
32 import logging
33 import random
33 import random
34 import traceback
34 import traceback
35 from functools import wraps
35 from functools import wraps
36
36
37 import ipaddress
37 import ipaddress
38
38
39 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
39 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
40 from sqlalchemy.orm.exc import ObjectDeletedError
40 from sqlalchemy.orm.exc import ObjectDeletedError
41 from sqlalchemy.orm import joinedload
41 from sqlalchemy.orm import joinedload
42 from zope.cachedescriptors.property import Lazy as LazyProperty
42 from zope.cachedescriptors.property import Lazy as LazyProperty
43
43
44 import rhodecode
44 import rhodecode
45 from rhodecode.model import meta
45 from rhodecode.model import meta
46 from rhodecode.model.meta import Session
46 from rhodecode.model.meta import Session
47 from rhodecode.model.user import UserModel
47 from rhodecode.model.user import UserModel
48 from rhodecode.model.db import (
48 from rhodecode.model.db import (
49 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
49 User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
50 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
50 UserIpMap, UserApiKeys, RepoGroup, UserGroup)
51 from rhodecode.lib import rc_cache
51 from rhodecode.lib import rc_cache
52 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
52 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
53 from rhodecode.lib.utils import (
53 from rhodecode.lib.utils import (
54 get_repo_slug, get_repo_group_slug, get_user_group_slug)
54 get_repo_slug, get_repo_group_slug, get_user_group_slug)
55 from rhodecode.lib.caching_query import FromCache
55 from rhodecode.lib.caching_query import FromCache
56
56
57
57
58 if rhodecode.is_unix:
58 if rhodecode.is_unix:
59 import bcrypt
59 import bcrypt
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63 csrf_token_key = "csrf_token"
63 csrf_token_key = "csrf_token"
64
64
65
65
66 class PasswordGenerator(object):
66 class PasswordGenerator(object):
67 """
67 """
68 This is a simple class for generating password from different sets of
68 This is a simple class for generating password from different sets of
69 characters
69 characters
70 usage::
70 usage::
71 passwd_gen = PasswordGenerator()
71 passwd_gen = PasswordGenerator()
72 #print 8-letter password containing only big and small letters
72 #print 8-letter password containing only big and small letters
73 of alphabet
73 of alphabet
74 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
74 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
75 """
75 """
76 ALPHABETS_NUM = r'''1234567890'''
76 ALPHABETS_NUM = r'''1234567890'''
77 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
77 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
78 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
78 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
79 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
79 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
80 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
80 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
81 + ALPHABETS_NUM + ALPHABETS_SPECIAL
81 + ALPHABETS_NUM + ALPHABETS_SPECIAL
82 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
82 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
83 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
83 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
84 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
84 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
85 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
85 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
86
86
87 def __init__(self, passwd=''):
87 def __init__(self, passwd=''):
88 self.passwd = passwd
88 self.passwd = passwd
89
89
90 def gen_password(self, length, type_=None):
90 def gen_password(self, length, type_=None):
91 if type_ is None:
91 if type_ is None:
92 type_ = self.ALPHABETS_FULL
92 type_ = self.ALPHABETS_FULL
93 self.passwd = ''.join([random.choice(type_) for _ in range(length)])
93 self.passwd = ''.join([random.choice(type_) for _ in range(length)])
94 return self.passwd
94 return self.passwd
95
95
96
96
97 class _RhodeCodeCryptoBase(object):
97 class _RhodeCodeCryptoBase(object):
98 ENC_PREF = None
98 ENC_PREF = None
99
99
100 def hash_create(self, str_):
100 def hash_create(self, str_):
101 """
101 """
102 hash the string using
102 hash the string using
103
103
104 :param str_: password to hash
104 :param str_: password to hash
105 """
105 """
106 raise NotImplementedError
106 raise NotImplementedError
107
107
108 def hash_check_with_upgrade(self, password, hashed):
108 def hash_check_with_upgrade(self, password, hashed):
109 """
109 """
110 Returns tuple in which first element is boolean that states that
110 Returns tuple in which first element is boolean that states that
111 given password matches it's hashed version, and the second is new hash
111 given password matches it's hashed version, and the second is new hash
112 of the password, in case this password should be migrated to new
112 of the password, in case this password should be migrated to new
113 cipher.
113 cipher.
114 """
114 """
115 checked_hash = self.hash_check(password, hashed)
115 checked_hash = self.hash_check(password, hashed)
116 return checked_hash, None
116 return checked_hash, None
117
117
118 def hash_check(self, password, hashed):
118 def hash_check(self, password, hashed):
119 """
119 """
120 Checks matching password with it's hashed value.
120 Checks matching password with it's hashed value.
121
121
122 :param password: password
122 :param password: password
123 :param hashed: password in hashed form
123 :param hashed: password in hashed form
124 """
124 """
125 raise NotImplementedError
125 raise NotImplementedError
126
126
127 def _assert_bytes(self, value):
127 def _assert_bytes(self, value):
128 """
128 """
129 Passing in an `unicode` object can lead to hard to detect issues
129 Passing in an `unicode` object can lead to hard to detect issues
130 if passwords contain non-ascii characters. Doing a type check
130 if passwords contain non-ascii characters. Doing a type check
131 during runtime, so that such mistakes are detected early on.
131 during runtime, so that such mistakes are detected early on.
132 """
132 """
133 if not isinstance(value, str):
133 if not isinstance(value, str):
134 raise TypeError(
134 raise TypeError(
135 "Bytestring required as input, got %r." % (value, ))
135 "Bytestring required as input, got %r." % (value, ))
136
136
137
137
138 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
138 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
139 ENC_PREF = ('$2a$10', '$2b$10')
139 ENC_PREF = ('$2a$10', '$2b$10')
140
140
141 def hash_create(self, str_):
141 def hash_create(self, str_):
142 self._assert_bytes(str_)
142 self._assert_bytes(str_)
143 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
143 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
144
144
145 def hash_check_with_upgrade(self, password, hashed):
145 def hash_check_with_upgrade(self, password, hashed):
146 """
146 """
147 Returns tuple in which first element is boolean that states that
147 Returns tuple in which first element is boolean that states that
148 given password matches it's hashed version, and the second is new hash
148 given password matches it's hashed version, and the second is new hash
149 of the password, in case this password should be migrated to new
149 of the password, in case this password should be migrated to new
150 cipher.
150 cipher.
151
151
152 This implements special upgrade logic which works like that:
152 This implements special upgrade logic which works like that:
153 - check if the given password == bcrypted hash, if yes then we
153 - check if the given password == bcrypted hash, if yes then we
154 properly used password and it was already in bcrypt. Proceed
154 properly used password and it was already in bcrypt. Proceed
155 without any changes
155 without any changes
156 - if bcrypt hash check is not working try with sha256. If hash compare
156 - if bcrypt hash check is not working try with sha256. If hash compare
157 is ok, it means we using correct but old hashed password. indicate
157 is ok, it means we using correct but old hashed password. indicate
158 hash change and proceed
158 hash change and proceed
159 """
159 """
160
160
161 new_hash = None
161 new_hash = None
162
162
163 # regular pw check
163 # regular pw check
164 password_match_bcrypt = self.hash_check(password, hashed)
164 password_match_bcrypt = self.hash_check(password, hashed)
165
165
166 # now we want to know if the password was maybe from sha256
166 # now we want to know if the password was maybe from sha256
167 # basically calling _RhodeCodeCryptoSha256().hash_check()
167 # basically calling _RhodeCodeCryptoSha256().hash_check()
168 if not password_match_bcrypt:
168 if not password_match_bcrypt:
169 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
169 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
170 new_hash = self.hash_create(password) # make new bcrypt hash
170 new_hash = self.hash_create(password) # make new bcrypt hash
171 password_match_bcrypt = True
171 password_match_bcrypt = True
172
172
173 return password_match_bcrypt, new_hash
173 return password_match_bcrypt, new_hash
174
174
175 def hash_check(self, password, hashed):
175 def hash_check(self, password, hashed):
176 """
176 """
177 Checks matching password with it's hashed value.
177 Checks matching password with it's hashed value.
178
178
179 :param password: password
179 :param password: password
180 :param hashed: password in hashed form
180 :param hashed: password in hashed form
181 """
181 """
182 self._assert_bytes(password)
182 self._assert_bytes(password)
183 try:
183 try:
184 return bcrypt.hashpw(password, hashed) == hashed
184 return bcrypt.hashpw(password, hashed) == hashed
185 except ValueError as e:
185 except ValueError as e:
186 # we're having a invalid salt here probably, we should not crash
186 # we're having a invalid salt here probably, we should not crash
187 # just return with False as it would be a wrong password.
187 # just return with False as it would be a wrong password.
188 log.debug('Failed to check password hash using bcrypt %s',
188 log.debug('Failed to check password hash using bcrypt %s',
189 safe_str(e))
189 safe_str(e))
190
190
191 return False
191 return False
192
192
193
193
194 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
194 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
195 ENC_PREF = '_'
195 ENC_PREF = '_'
196
196
197 def hash_create(self, str_):
197 def hash_create(self, str_):
198 self._assert_bytes(str_)
198 self._assert_bytes(str_)
199 return hashlib.sha256(str_).hexdigest()
199 return hashlib.sha256(str_).hexdigest()
200
200
201 def hash_check(self, password, hashed):
201 def hash_check(self, password, hashed):
202 """
202 """
203 Checks matching password with it's hashed value.
203 Checks matching password with it's hashed value.
204
204
205 :param password: password
205 :param password: password
206 :param hashed: password in hashed form
206 :param hashed: password in hashed form
207 """
207 """
208 self._assert_bytes(password)
208 self._assert_bytes(password)
209 return hashlib.sha256(password).hexdigest() == hashed
209 return hashlib.sha256(password).hexdigest() == hashed
210
210
211
211
212 class _RhodeCodeCryptoTest(_RhodeCodeCryptoBase):
212 class _RhodeCodeCryptoTest(_RhodeCodeCryptoBase):
213 ENC_PREF = '_'
213 ENC_PREF = '_'
214
214
215 def hash_create(self, str_):
215 def hash_create(self, str_):
216 self._assert_bytes(str_)
216 self._assert_bytes(str_)
217 return sha1(str_)
217 return sha1(str_)
218
218
219 def hash_check(self, password, hashed):
219 def hash_check(self, password, hashed):
220 """
220 """
221 Checks matching password with it's hashed value.
221 Checks matching password with it's hashed value.
222
222
223 :param password: password
223 :param password: password
224 :param hashed: password in hashed form
224 :param hashed: password in hashed form
225 """
225 """
226 self._assert_bytes(password)
226 self._assert_bytes(password)
227 return sha1(password) == hashed
227 return sha1(password) == hashed
228
228
229
229
230 def crypto_backend():
230 def crypto_backend():
231 """
231 """
232 Return the matching crypto backend.
232 Return the matching crypto backend.
233
233
234 Selection is based on if we run tests or not, we pick sha1-test backend to run
234 Selection is based on if we run tests or not, we pick sha1-test backend to run
235 tests faster since BCRYPT is expensive to calculate
235 tests faster since BCRYPT is expensive to calculate
236 """
236 """
237 if rhodecode.is_test:
237 if rhodecode.is_test:
238 RhodeCodeCrypto = _RhodeCodeCryptoTest()
238 RhodeCodeCrypto = _RhodeCodeCryptoTest()
239 else:
239 else:
240 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
240 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
241
241
242 return RhodeCodeCrypto
242 return RhodeCodeCrypto
243
243
244
244
245 def get_crypt_password(password):
245 def get_crypt_password(password):
246 """
246 """
247 Create the hash of `password` with the active crypto backend.
247 Create the hash of `password` with the active crypto backend.
248
248
249 :param password: The cleartext password.
249 :param password: The cleartext password.
250 :type password: unicode
250 :type password: unicode
251 """
251 """
252 password = safe_str(password)
252 password = safe_str(password)
253 return crypto_backend().hash_create(password)
253 return crypto_backend().hash_create(password)
254
254
255
255
256 def check_password(password, hashed):
256 def check_password(password, hashed):
257 """
257 """
258 Check if the value in `password` matches the hash in `hashed`.
258 Check if the value in `password` matches the hash in `hashed`.
259
259
260 :param password: The cleartext password.
260 :param password: The cleartext password.
261 :type password: unicode
261 :type password: unicode
262
262
263 :param hashed: The expected hashed version of the password.
263 :param hashed: The expected hashed version of the password.
264 :type hashed: The hash has to be passed in in text representation.
264 :type hashed: The hash has to be passed in in text representation.
265 """
265 """
266 password = safe_str(password)
266 password = safe_str(password)
267 return crypto_backend().hash_check(password, hashed)
267 return crypto_backend().hash_check(password, hashed)
268
268
269
269
270 def generate_auth_token(data, salt=None):
270 def generate_auth_token(data, salt=None):
271 """
271 """
272 Generates API KEY from given string
272 Generates API KEY from given string
273 """
273 """
274
274
275 if salt is None:
275 if salt is None:
276 salt = os.urandom(16)
276 salt = os.urandom(16)
277 return hashlib.sha1(safe_str(data) + salt).hexdigest()
277 return hashlib.sha1(safe_str(data) + salt).hexdigest()
278
278
279
279
280 def get_came_from(request):
280 def get_came_from(request):
281 """
281 """
282 get query_string+path from request sanitized after removing auth_token
282 get query_string+path from request sanitized after removing auth_token
283 """
283 """
284 _req = request
284 _req = request
285
285
286 path = _req.path
286 path = _req.path
287 if 'auth_token' in _req.GET:
287 if 'auth_token' in _req.GET:
288 # sanitize the request and remove auth_token for redirection
288 # sanitize the request and remove auth_token for redirection
289 _req.GET.pop('auth_token')
289 _req.GET.pop('auth_token')
290 qs = _req.query_string
290 qs = _req.query_string
291 if qs:
291 if qs:
292 path += '?' + qs
292 path += '?' + qs
293
293
294 return path
294 return path
295
295
296
296
297 class CookieStoreWrapper(object):
297 class CookieStoreWrapper(object):
298
298
299 def __init__(self, cookie_store):
299 def __init__(self, cookie_store):
300 self.cookie_store = cookie_store
300 self.cookie_store = cookie_store
301
301
302 def __repr__(self):
302 def __repr__(self):
303 return 'CookieStore<%s>' % (self.cookie_store)
303 return 'CookieStore<%s>' % (self.cookie_store)
304
304
305 def get(self, key, other=None):
305 def get(self, key, other=None):
306 if isinstance(self.cookie_store, dict):
306 if isinstance(self.cookie_store, dict):
307 return self.cookie_store.get(key, other)
307 return self.cookie_store.get(key, other)
308 elif isinstance(self.cookie_store, AuthUser):
308 elif isinstance(self.cookie_store, AuthUser):
309 return self.cookie_store.__dict__.get(key, other)
309 return self.cookie_store.__dict__.get(key, other)
310
310
311
311
312 def _cached_perms_data(user_id, scope, user_is_admin,
312 def _cached_perms_data(user_id, scope, user_is_admin,
313 user_inherit_default_permissions, explicit, algo,
313 user_inherit_default_permissions, explicit, algo,
314 calculate_super_admin):
314 calculate_super_admin):
315
315
316 permissions = PermissionCalculator(
316 permissions = PermissionCalculator(
317 user_id, scope, user_is_admin, user_inherit_default_permissions,
317 user_id, scope, user_is_admin, user_inherit_default_permissions,
318 explicit, algo, calculate_super_admin)
318 explicit, algo, calculate_super_admin)
319 return permissions.calculate()
319 return permissions.calculate()
320
320
321
321
322 class PermOrigin(object):
322 class PermOrigin(object):
323 SUPER_ADMIN = 'superadmin'
323 SUPER_ADMIN = 'superadmin'
324 ARCHIVED = 'archived'
324 ARCHIVED = 'archived'
325
325
326 REPO_USER = 'user:%s'
326 REPO_USER = 'user:%s'
327 REPO_USERGROUP = 'usergroup:%s'
327 REPO_USERGROUP = 'usergroup:%s'
328 REPO_OWNER = 'repo.owner'
328 REPO_OWNER = 'repo.owner'
329 REPO_DEFAULT = 'repo.default'
329 REPO_DEFAULT = 'repo.default'
330 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
330 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
331 REPO_PRIVATE = 'repo.private'
331 REPO_PRIVATE = 'repo.private'
332
332
333 REPOGROUP_USER = 'user:%s'
333 REPOGROUP_USER = 'user:%s'
334 REPOGROUP_USERGROUP = 'usergroup:%s'
334 REPOGROUP_USERGROUP = 'usergroup:%s'
335 REPOGROUP_OWNER = 'group.owner'
335 REPOGROUP_OWNER = 'group.owner'
336 REPOGROUP_DEFAULT = 'group.default'
336 REPOGROUP_DEFAULT = 'group.default'
337 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
337 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
338
338
339 USERGROUP_USER = 'user:%s'
339 USERGROUP_USER = 'user:%s'
340 USERGROUP_USERGROUP = 'usergroup:%s'
340 USERGROUP_USERGROUP = 'usergroup:%s'
341 USERGROUP_OWNER = 'usergroup.owner'
341 USERGROUP_OWNER = 'usergroup.owner'
342 USERGROUP_DEFAULT = 'usergroup.default'
342 USERGROUP_DEFAULT = 'usergroup.default'
343 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
343 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
344
344
345
345
346 class PermOriginDict(dict):
346 class PermOriginDict(dict):
347 """
347 """
348 A special dict used for tracking permissions along with their origins.
348 A special dict used for tracking permissions along with their origins.
349
349
350 `__setitem__` has been overridden to expect a tuple(perm, origin)
350 `__setitem__` has been overridden to expect a tuple(perm, origin)
351 `__getitem__` will return only the perm
351 `__getitem__` will return only the perm
352 `.perm_origin_stack` will return the stack of (perm, origin) set per key
352 `.perm_origin_stack` will return the stack of (perm, origin) set per key
353
353
354 >>> perms = PermOriginDict()
354 >>> perms = PermOriginDict()
355 >>> perms['resource'] = 'read', 'default', 1
355 >>> perms['resource'] = 'read', 'default', 1
356 >>> perms['resource']
356 >>> perms['resource']
357 'read'
357 'read'
358 >>> perms['resource'] = 'write', 'admin', 2
358 >>> perms['resource'] = 'write', 'admin', 2
359 >>> perms['resource']
359 >>> perms['resource']
360 'write'
360 'write'
361 >>> perms.perm_origin_stack
361 >>> perms.perm_origin_stack
362 {'resource': [('read', 'default', 1), ('write', 'admin', 2)]}
362 {'resource': [('read', 'default', 1), ('write', 'admin', 2)]}
363 """
363 """
364
364
365 def __init__(self, *args, **kw):
365 def __init__(self, *args, **kw):
366 dict.__init__(self, *args, **kw)
366 dict.__init__(self, *args, **kw)
367 self.perm_origin_stack = collections.OrderedDict()
367 self.perm_origin_stack = collections.OrderedDict()
368
368
369 def __setitem__(self, key, (perm, origin, obj_id)):
369 def __setitem__(self, key, (perm, origin, obj_id)):
370 self.perm_origin_stack.setdefault(key, []).append(
370 self.perm_origin_stack.setdefault(key, []).append(
371 (perm, origin, obj_id))
371 (perm, origin, obj_id))
372 dict.__setitem__(self, key, perm)
372 dict.__setitem__(self, key, perm)
373
373
374
374
375 class BranchPermOriginDict(PermOriginDict):
375 class BranchPermOriginDict(PermOriginDict):
376 """
376 """
377 Dedicated branch permissions dict, with tracking of patterns and origins.
377 Dedicated branch permissions dict, with tracking of patterns and origins.
378
378
379 >>> perms = BranchPermOriginDict()
379 >>> perms = BranchPermOriginDict()
380 >>> perms['resource'] = '*pattern', 'read', 'default'
380 >>> perms['resource'] = '*pattern', 'read', 'default'
381 >>> perms['resource']
381 >>> perms['resource']
382 {'*pattern': 'read'}
382 {'*pattern': 'read'}
383 >>> perms['resource'] = '*pattern', 'write', 'admin'
383 >>> perms['resource'] = '*pattern', 'write', 'admin'
384 >>> perms['resource']
384 >>> perms['resource']
385 {'*pattern': 'write'}
385 {'*pattern': 'write'}
386 >>> perms.perm_origin_stack
386 >>> perms.perm_origin_stack
387 {'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
387 {'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
388 """
388 """
389 def __setitem__(self, key, (pattern, perm, origin)):
389 def __setitem__(self, key, (pattern, perm, origin)):
390
390
391 self.perm_origin_stack.setdefault(key, {}) \
391 self.perm_origin_stack.setdefault(key, {}) \
392 .setdefault(pattern, []).append((perm, origin))
392 .setdefault(pattern, []).append((perm, origin))
393
393
394 if key in self:
394 if key in self:
395 self[key].__setitem__(pattern, perm)
395 self[key].__setitem__(pattern, perm)
396 else:
396 else:
397 patterns = collections.OrderedDict()
397 patterns = collections.OrderedDict()
398 patterns[pattern] = perm
398 patterns[pattern] = perm
399 dict.__setitem__(self, key, patterns)
399 dict.__setitem__(self, key, patterns)
400
400
401
401
402 class PermissionCalculator(object):
402 class PermissionCalculator(object):
403
403
404 def __init__(
404 def __init__(
405 self, user_id, scope, user_is_admin,
405 self, user_id, scope, user_is_admin,
406 user_inherit_default_permissions, explicit, algo,
406 user_inherit_default_permissions, explicit, algo,
407 calculate_super_admin_as_user=False):
407 calculate_super_admin_as_user=False):
408
408
409 self.user_id = user_id
409 self.user_id = user_id
410 self.user_is_admin = user_is_admin
410 self.user_is_admin = user_is_admin
411 self.inherit_default_permissions = user_inherit_default_permissions
411 self.inherit_default_permissions = user_inherit_default_permissions
412 self.explicit = explicit
412 self.explicit = explicit
413 self.algo = algo
413 self.algo = algo
414 self.calculate_super_admin_as_user = calculate_super_admin_as_user
414 self.calculate_super_admin_as_user = calculate_super_admin_as_user
415
415
416 scope = scope or {}
416 scope = scope or {}
417 self.scope_repo_id = scope.get('repo_id')
417 self.scope_repo_id = scope.get('repo_id')
418 self.scope_repo_group_id = scope.get('repo_group_id')
418 self.scope_repo_group_id = scope.get('repo_group_id')
419 self.scope_user_group_id = scope.get('user_group_id')
419 self.scope_user_group_id = scope.get('user_group_id')
420
420
421 self.default_user_id = User.get_default_user(cache=True).user_id
421 self.default_user_id = User.get_default_user(cache=True).user_id
422
422
423 self.permissions_repositories = PermOriginDict()
423 self.permissions_repositories = PermOriginDict()
424 self.permissions_repository_groups = PermOriginDict()
424 self.permissions_repository_groups = PermOriginDict()
425 self.permissions_user_groups = PermOriginDict()
425 self.permissions_user_groups = PermOriginDict()
426 self.permissions_repository_branches = BranchPermOriginDict()
426 self.permissions_repository_branches = BranchPermOriginDict()
427 self.permissions_global = set()
427 self.permissions_global = set()
428
428
429 self.default_repo_perms = Permission.get_default_repo_perms(
429 self.default_repo_perms = Permission.get_default_repo_perms(
430 self.default_user_id, self.scope_repo_id)
430 self.default_user_id, self.scope_repo_id)
431 self.default_repo_groups_perms = Permission.get_default_group_perms(
431 self.default_repo_groups_perms = Permission.get_default_group_perms(
432 self.default_user_id, self.scope_repo_group_id)
432 self.default_user_id, self.scope_repo_group_id)
433 self.default_user_group_perms = \
433 self.default_user_group_perms = \
434 Permission.get_default_user_group_perms(
434 Permission.get_default_user_group_perms(
435 self.default_user_id, self.scope_user_group_id)
435 self.default_user_id, self.scope_user_group_id)
436
436
437 # default branch perms
437 # default branch perms
438 self.default_branch_repo_perms = \
438 self.default_branch_repo_perms = \
439 Permission.get_default_repo_branch_perms(
439 Permission.get_default_repo_branch_perms(
440 self.default_user_id, self.scope_repo_id)
440 self.default_user_id, self.scope_repo_id)
441
441
442 def calculate(self):
442 def calculate(self):
443 if self.user_is_admin and not self.calculate_super_admin_as_user:
443 if self.user_is_admin and not self.calculate_super_admin_as_user:
444 return self._calculate_admin_permissions()
444 return self._calculate_admin_permissions()
445
445
446 self._calculate_global_default_permissions()
446 self._calculate_global_default_permissions()
447 self._calculate_global_permissions()
447 self._calculate_global_permissions()
448 self._calculate_default_permissions()
448 self._calculate_default_permissions()
449 self._calculate_repository_permissions()
449 self._calculate_repository_permissions()
450 self._calculate_repository_branch_permissions()
450 self._calculate_repository_branch_permissions()
451 self._calculate_repository_group_permissions()
451 self._calculate_repository_group_permissions()
452 self._calculate_user_group_permissions()
452 self._calculate_user_group_permissions()
453 return self._permission_structure()
453 return self._permission_structure()
454
454
455 def _calculate_admin_permissions(self):
455 def _calculate_admin_permissions(self):
456 """
456 """
457 admin user have all default rights for repositories
457 admin user have all default rights for repositories
458 and groups set to admin
458 and groups set to admin
459 """
459 """
460 self.permissions_global.add('hg.admin')
460 self.permissions_global.add('hg.admin')
461 self.permissions_global.add('hg.create.write_on_repogroup.true')
461 self.permissions_global.add('hg.create.write_on_repogroup.true')
462
462
463 # repositories
463 # repositories
464 for perm in self.default_repo_perms:
464 for perm in self.default_repo_perms:
465 r_k = perm.UserRepoToPerm.repository.repo_name
465 r_k = perm.UserRepoToPerm.repository.repo_name
466 obj_id = perm.UserRepoToPerm.repository.repo_id
466 obj_id = perm.UserRepoToPerm.repository.repo_id
467 archived = perm.UserRepoToPerm.repository.archived
467 archived = perm.UserRepoToPerm.repository.archived
468 p = 'repository.admin'
468 p = 'repository.admin'
469 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN, obj_id
469 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN, obj_id
470 # special case for archived repositories, which we block still even for
470 # special case for archived repositories, which we block still even for
471 # super admins
471 # super admins
472 if archived:
472 if archived:
473 p = 'repository.read'
473 p = 'repository.read'
474 self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED, obj_id
474 self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED, obj_id
475
475
476 # repository groups
476 # repository groups
477 for perm in self.default_repo_groups_perms:
477 for perm in self.default_repo_groups_perms:
478 rg_k = perm.UserRepoGroupToPerm.group.group_name
478 rg_k = perm.UserRepoGroupToPerm.group.group_name
479 obj_id = perm.UserRepoGroupToPerm.group.group_id
479 obj_id = perm.UserRepoGroupToPerm.group.group_id
480 p = 'group.admin'
480 p = 'group.admin'
481 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN, obj_id
481 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN, obj_id
482
482
483 # user groups
483 # user groups
484 for perm in self.default_user_group_perms:
484 for perm in self.default_user_group_perms:
485 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
485 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
486 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
486 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
487 p = 'usergroup.admin'
487 p = 'usergroup.admin'
488 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN, obj_id
488 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN, obj_id
489
489
490 # branch permissions
490 # branch permissions
491 # since super-admin also can have custom rule permissions
491 # since super-admin also can have custom rule permissions
492 # we *always* need to calculate those inherited from default, and also explicit
492 # we *always* need to calculate those inherited from default, and also explicit
493 self._calculate_default_permissions_repository_branches(
493 self._calculate_default_permissions_repository_branches(
494 user_inherit_object_permissions=False)
494 user_inherit_object_permissions=False)
495 self._calculate_repository_branch_permissions()
495 self._calculate_repository_branch_permissions()
496
496
497 return self._permission_structure()
497 return self._permission_structure()
498
498
499 def _calculate_global_default_permissions(self):
499 def _calculate_global_default_permissions(self):
500 """
500 """
501 global permissions taken from the default user
501 global permissions taken from the default user
502 """
502 """
503 default_global_perms = UserToPerm.query()\
503 default_global_perms = UserToPerm.query()\
504 .filter(UserToPerm.user_id == self.default_user_id)\
504 .filter(UserToPerm.user_id == self.default_user_id)\
505 .options(joinedload(UserToPerm.permission))
505 .options(joinedload(UserToPerm.permission))
506
506
507 for perm in default_global_perms:
507 for perm in default_global_perms:
508 self.permissions_global.add(perm.permission.permission_name)
508 self.permissions_global.add(perm.permission.permission_name)
509
509
510 if self.user_is_admin:
510 if self.user_is_admin:
511 self.permissions_global.add('hg.admin')
511 self.permissions_global.add('hg.admin')
512 self.permissions_global.add('hg.create.write_on_repogroup.true')
512 self.permissions_global.add('hg.create.write_on_repogroup.true')
513
513
514 def _calculate_global_permissions(self):
514 def _calculate_global_permissions(self):
515 """
515 """
516 Set global system permissions with user permissions or permissions
516 Set global system permissions with user permissions or permissions
517 taken from the user groups of the current user.
517 taken from the user groups of the current user.
518
518
519 The permissions include repo creating, repo group creating, forking
519 The permissions include repo creating, repo group creating, forking
520 etc.
520 etc.
521 """
521 """
522
522
523 # now we read the defined permissions and overwrite what we have set
523 # now we read the defined permissions and overwrite what we have set
524 # before those can be configured from groups or users explicitly.
524 # before those can be configured from groups or users explicitly.
525
525
526 # In case we want to extend this list we should make sure
526 # In case we want to extend this list we should make sure
527 # this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
527 # this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
528 _configurable = frozenset([
528 _configurable = frozenset([
529 'hg.fork.none', 'hg.fork.repository',
529 'hg.fork.none', 'hg.fork.repository',
530 'hg.create.none', 'hg.create.repository',
530 'hg.create.none', 'hg.create.repository',
531 'hg.usergroup.create.false', 'hg.usergroup.create.true',
531 'hg.usergroup.create.false', 'hg.usergroup.create.true',
532 'hg.repogroup.create.false', 'hg.repogroup.create.true',
532 'hg.repogroup.create.false', 'hg.repogroup.create.true',
533 'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
533 'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
534 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
534 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
535 ])
535 ])
536
536
537 # USER GROUPS comes first user group global permissions
537 # USER GROUPS comes first user group global permissions
538 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
538 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
539 .options(joinedload(UserGroupToPerm.permission))\
539 .options(joinedload(UserGroupToPerm.permission))\
540 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
540 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
541 UserGroupMember.users_group_id))\
541 UserGroupMember.users_group_id))\
542 .filter(UserGroupMember.user_id == self.user_id)\
542 .filter(UserGroupMember.user_id == self.user_id)\
543 .order_by(UserGroupToPerm.users_group_id)\
543 .order_by(UserGroupToPerm.users_group_id)\
544 .all()
544 .all()
545
545
546 # need to group here by groups since user can be in more than
546 # need to group here by groups since user can be in more than
547 # one group, so we get all groups
547 # one group, so we get all groups
548 _explicit_grouped_perms = [
548 _explicit_grouped_perms = [
549 [x, list(y)] for x, y in
549 [x, list(y)] for x, y in
550 itertools.groupby(user_perms_from_users_groups,
550 itertools.groupby(user_perms_from_users_groups,
551 lambda _x: _x.users_group)]
551 lambda _x: _x.users_group)]
552
552
553 for gr, perms in _explicit_grouped_perms:
553 for gr, perms in _explicit_grouped_perms:
554 # since user can be in multiple groups iterate over them and
554 # since user can be in multiple groups iterate over them and
555 # select the lowest permissions first (more explicit)
555 # select the lowest permissions first (more explicit)
556 # TODO(marcink): do this^^
556 # TODO(marcink): do this^^
557
557
558 # group doesn't inherit default permissions so we actually set them
558 # group doesn't inherit default permissions so we actually set them
559 if not gr.inherit_default_permissions:
559 if not gr.inherit_default_permissions:
560 # NEED TO IGNORE all previously set configurable permissions
560 # NEED TO IGNORE all previously set configurable permissions
561 # and replace them with explicitly set from this user
561 # and replace them with explicitly set from this user
562 # group permissions
562 # group permissions
563 self.permissions_global = self.permissions_global.difference(
563 self.permissions_global = self.permissions_global.difference(
564 _configurable)
564 _configurable)
565 for perm in perms:
565 for perm in perms:
566 self.permissions_global.add(perm.permission.permission_name)
566 self.permissions_global.add(perm.permission.permission_name)
567
567
568 # user explicit global permissions
568 # user explicit global permissions
569 user_perms = Session().query(UserToPerm)\
569 user_perms = Session().query(UserToPerm)\
570 .options(joinedload(UserToPerm.permission))\
570 .options(joinedload(UserToPerm.permission))\
571 .filter(UserToPerm.user_id == self.user_id).all()
571 .filter(UserToPerm.user_id == self.user_id).all()
572
572
573 if not self.inherit_default_permissions:
573 if not self.inherit_default_permissions:
574 # NEED TO IGNORE all configurable permissions and
574 # NEED TO IGNORE all configurable permissions and
575 # replace them with explicitly set from this user permissions
575 # replace them with explicitly set from this user permissions
576 self.permissions_global = self.permissions_global.difference(
576 self.permissions_global = self.permissions_global.difference(
577 _configurable)
577 _configurable)
578 for perm in user_perms:
578 for perm in user_perms:
579 self.permissions_global.add(perm.permission.permission_name)
579 self.permissions_global.add(perm.permission.permission_name)
580
580
581 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
581 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
582 for perm in self.default_repo_perms:
582 for perm in self.default_repo_perms:
583 r_k = perm.UserRepoToPerm.repository.repo_name
583 r_k = perm.UserRepoToPerm.repository.repo_name
584 obj_id = perm.UserRepoToPerm.repository.repo_id
584 obj_id = perm.UserRepoToPerm.repository.repo_id
585 archived = perm.UserRepoToPerm.repository.archived
585 archived = perm.UserRepoToPerm.repository.archived
586 p = perm.Permission.permission_name
586 p = perm.Permission.permission_name
587 o = PermOrigin.REPO_DEFAULT
587 o = PermOrigin.REPO_DEFAULT
588 self.permissions_repositories[r_k] = p, o, obj_id
588 self.permissions_repositories[r_k] = p, o, obj_id
589
589
590 # if we decide this user isn't inheriting permissions from
590 # if we decide this user isn't inheriting permissions from
591 # default user we set him to .none so only explicit
591 # default user we set him to .none so only explicit
592 # permissions work
592 # permissions work
593 if not user_inherit_object_permissions:
593 if not user_inherit_object_permissions:
594 p = 'repository.none'
594 p = 'repository.none'
595 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
595 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
596 self.permissions_repositories[r_k] = p, o, obj_id
596 self.permissions_repositories[r_k] = p, o, obj_id
597
597
598 if perm.Repository.private and not (
598 if perm.Repository.private and not (
599 perm.Repository.user_id == self.user_id):
599 perm.Repository.user_id == self.user_id):
600 # disable defaults for private repos,
600 # disable defaults for private repos,
601 p = 'repository.none'
601 p = 'repository.none'
602 o = PermOrigin.REPO_PRIVATE
602 o = PermOrigin.REPO_PRIVATE
603 self.permissions_repositories[r_k] = p, o, obj_id
603 self.permissions_repositories[r_k] = p, o, obj_id
604
604
605 elif perm.Repository.user_id == self.user_id:
605 elif perm.Repository.user_id == self.user_id:
606 # set admin if owner
606 # set admin if owner
607 p = 'repository.admin'
607 p = 'repository.admin'
608 o = PermOrigin.REPO_OWNER
608 o = PermOrigin.REPO_OWNER
609 self.permissions_repositories[r_k] = p, o, obj_id
609 self.permissions_repositories[r_k] = p, o, obj_id
610
610
611 if self.user_is_admin:
611 if self.user_is_admin:
612 p = 'repository.admin'
612 p = 'repository.admin'
613 o = PermOrigin.SUPER_ADMIN
613 o = PermOrigin.SUPER_ADMIN
614 self.permissions_repositories[r_k] = p, o, obj_id
614 self.permissions_repositories[r_k] = p, o, obj_id
615
615
616 # finally in case of archived repositories, we downgrade higher
616 # finally in case of archived repositories, we downgrade higher
617 # permissions to read
617 # permissions to read
618 if archived:
618 if archived:
619 current_perm = self.permissions_repositories[r_k]
619 current_perm = self.permissions_repositories[r_k]
620 if current_perm in ['repository.write', 'repository.admin']:
620 if current_perm in ['repository.write', 'repository.admin']:
621 p = 'repository.read'
621 p = 'repository.read'
622 o = PermOrigin.ARCHIVED
622 o = PermOrigin.ARCHIVED
623 self.permissions_repositories[r_k] = p, o, obj_id
623 self.permissions_repositories[r_k] = p, o, obj_id
624
624
625 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
625 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
626 for perm in self.default_branch_repo_perms:
626 for perm in self.default_branch_repo_perms:
627
627
628 r_k = perm.UserRepoToPerm.repository.repo_name
628 r_k = perm.UserRepoToPerm.repository.repo_name
629 p = perm.Permission.permission_name
629 p = perm.Permission.permission_name
630 pattern = perm.UserToRepoBranchPermission.branch_pattern
630 pattern = perm.UserToRepoBranchPermission.branch_pattern
631 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
631 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
632
632
633 if not self.explicit:
633 if not self.explicit:
634 cur_perm = self.permissions_repository_branches.get(r_k)
634 cur_perm = self.permissions_repository_branches.get(r_k)
635 if cur_perm:
635 if cur_perm:
636 cur_perm = cur_perm[pattern]
636 cur_perm = cur_perm[pattern]
637 cur_perm = cur_perm or 'branch.none'
637 cur_perm = cur_perm or 'branch.none'
638
638
639 p = self._choose_permission(p, cur_perm)
639 p = self._choose_permission(p, cur_perm)
640
640
641 # NOTE(marcink): register all pattern/perm instances in this
641 # NOTE(marcink): register all pattern/perm instances in this
642 # special dict that aggregates entries
642 # special dict that aggregates entries
643 self.permissions_repository_branches[r_k] = pattern, p, o
643 self.permissions_repository_branches[r_k] = pattern, p, o
644
644
645 def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions):
645 def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions):
646 for perm in self.default_repo_groups_perms:
646 for perm in self.default_repo_groups_perms:
647 rg_k = perm.UserRepoGroupToPerm.group.group_name
647 rg_k = perm.UserRepoGroupToPerm.group.group_name
648 obj_id = perm.UserRepoGroupToPerm.group.group_id
648 obj_id = perm.UserRepoGroupToPerm.group.group_id
649 p = perm.Permission.permission_name
649 p = perm.Permission.permission_name
650 o = PermOrigin.REPOGROUP_DEFAULT
650 o = PermOrigin.REPOGROUP_DEFAULT
651 self.permissions_repository_groups[rg_k] = p, o, obj_id
651 self.permissions_repository_groups[rg_k] = p, o, obj_id
652
652
653 # if we decide this user isn't inheriting permissions from default
653 # if we decide this user isn't inheriting permissions from default
654 # user we set him to .none so only explicit permissions work
654 # user we set him to .none so only explicit permissions work
655 if not user_inherit_object_permissions:
655 if not user_inherit_object_permissions:
656 p = 'group.none'
656 p = 'group.none'
657 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
657 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
658 self.permissions_repository_groups[rg_k] = p, o, obj_id
658 self.permissions_repository_groups[rg_k] = p, o, obj_id
659
659
660 if perm.RepoGroup.user_id == self.user_id:
660 if perm.RepoGroup.user_id == self.user_id:
661 # set admin if owner
661 # set admin if owner
662 p = 'group.admin'
662 p = 'group.admin'
663 o = PermOrigin.REPOGROUP_OWNER
663 o = PermOrigin.REPOGROUP_OWNER
664 self.permissions_repository_groups[rg_k] = p, o, obj_id
664 self.permissions_repository_groups[rg_k] = p, o, obj_id
665
665
666 if self.user_is_admin:
666 if self.user_is_admin:
667 p = 'group.admin'
667 p = 'group.admin'
668 o = PermOrigin.SUPER_ADMIN
668 o = PermOrigin.SUPER_ADMIN
669 self.permissions_repository_groups[rg_k] = p, o, obj_id
669 self.permissions_repository_groups[rg_k] = p, o, obj_id
670
670
671 def _calculate_default_permissions_user_groups(self, user_inherit_object_permissions):
671 def _calculate_default_permissions_user_groups(self, user_inherit_object_permissions):
672 for perm in self.default_user_group_perms:
672 for perm in self.default_user_group_perms:
673 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
673 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
674 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
674 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
675 p = perm.Permission.permission_name
675 p = perm.Permission.permission_name
676 o = PermOrigin.USERGROUP_DEFAULT
676 o = PermOrigin.USERGROUP_DEFAULT
677 self.permissions_user_groups[u_k] = p, o, obj_id
677 self.permissions_user_groups[u_k] = p, o, obj_id
678
678
679 # if we decide this user isn't inheriting permissions from default
679 # if we decide this user isn't inheriting permissions from default
680 # user we set him to .none so only explicit permissions work
680 # user we set him to .none so only explicit permissions work
681 if not user_inherit_object_permissions:
681 if not user_inherit_object_permissions:
682 p = 'usergroup.none'
682 p = 'usergroup.none'
683 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
683 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
684 self.permissions_user_groups[u_k] = p, o, obj_id
684 self.permissions_user_groups[u_k] = p, o, obj_id
685
685
686 if perm.UserGroup.user_id == self.user_id:
686 if perm.UserGroup.user_id == self.user_id:
687 # set admin if owner
687 # set admin if owner
688 p = 'usergroup.admin'
688 p = 'usergroup.admin'
689 o = PermOrigin.USERGROUP_OWNER
689 o = PermOrigin.USERGROUP_OWNER
690 self.permissions_user_groups[u_k] = p, o, obj_id
690 self.permissions_user_groups[u_k] = p, o, obj_id
691
691
692 if self.user_is_admin:
692 if self.user_is_admin:
693 p = 'usergroup.admin'
693 p = 'usergroup.admin'
694 o = PermOrigin.SUPER_ADMIN
694 o = PermOrigin.SUPER_ADMIN
695 self.permissions_user_groups[u_k] = p, o, obj_id
695 self.permissions_user_groups[u_k] = p, o, obj_id
696
696
697 def _calculate_default_permissions(self):
697 def _calculate_default_permissions(self):
698 """
698 """
699 Set default user permissions for repositories, repository branches,
699 Set default user permissions for repositories, repository branches,
700 repository groups, user groups taken from the default user.
700 repository groups, user groups taken from the default user.
701
701
702 Calculate inheritance of object permissions based on what we have now
702 Calculate inheritance of object permissions based on what we have now
703 in GLOBAL permissions. We check if .false is in GLOBAL since this is
703 in GLOBAL permissions. We check if .false is in GLOBAL since this is
704 explicitly set. Inherit is the opposite of .false being there.
704 explicitly set. Inherit is the opposite of .false being there.
705
705
706 .. note::
706 .. note::
707
707
708 the syntax is little bit odd but what we need to check here is
708 the syntax is little bit odd but what we need to check here is
709 the opposite of .false permission being in the list so even for
709 the opposite of .false permission being in the list so even for
710 inconsistent state when both .true/.false is there
710 inconsistent state when both .true/.false is there
711 .false is more important
711 .false is more important
712
712
713 """
713 """
714 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
714 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
715 in self.permissions_global)
715 in self.permissions_global)
716
716
717 # default permissions inherited from `default` user permissions
717 # default permissions inherited from `default` user permissions
718 self._calculate_default_permissions_repositories(
718 self._calculate_default_permissions_repositories(
719 user_inherit_object_permissions)
719 user_inherit_object_permissions)
720
720
721 self._calculate_default_permissions_repository_branches(
721 self._calculate_default_permissions_repository_branches(
722 user_inherit_object_permissions)
722 user_inherit_object_permissions)
723
723
724 self._calculate_default_permissions_repository_groups(
724 self._calculate_default_permissions_repository_groups(
725 user_inherit_object_permissions)
725 user_inherit_object_permissions)
726
726
727 self._calculate_default_permissions_user_groups(
727 self._calculate_default_permissions_user_groups(
728 user_inherit_object_permissions)
728 user_inherit_object_permissions)
729
729
730 def _calculate_repository_permissions(self):
730 def _calculate_repository_permissions(self):
731 """
731 """
732 Repository access permissions for the current user.
732 Repository access permissions for the current user.
733
733
734 Check if the user is part of user groups for this repository and
734 Check if the user is part of user groups for this repository and
735 fill in the permission from it. `_choose_permission` decides of which
735 fill in the permission from it. `_choose_permission` decides of which
736 permission should be selected based on selected method.
736 permission should be selected based on selected method.
737 """
737 """
738
738
739 # user group for repositories permissions
739 # user group for repositories permissions
740 user_repo_perms_from_user_group = Permission\
740 user_repo_perms_from_user_group = Permission\
741 .get_default_repo_perms_from_user_group(
741 .get_default_repo_perms_from_user_group(
742 self.user_id, self.scope_repo_id)
742 self.user_id, self.scope_repo_id)
743
743
744 multiple_counter = collections.defaultdict(int)
744 multiple_counter = collections.defaultdict(int)
745 for perm in user_repo_perms_from_user_group:
745 for perm in user_repo_perms_from_user_group:
746 r_k = perm.UserGroupRepoToPerm.repository.repo_name
746 r_k = perm.UserGroupRepoToPerm.repository.repo_name
747 obj_id = perm.UserGroupRepoToPerm.repository.repo_id
747 obj_id = perm.UserGroupRepoToPerm.repository.repo_id
748 multiple_counter[r_k] += 1
748 multiple_counter[r_k] += 1
749 p = perm.Permission.permission_name
749 p = perm.Permission.permission_name
750 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
750 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
751 .users_group.users_group_name
751 .users_group.users_group_name
752
752
753 if multiple_counter[r_k] > 1:
753 if multiple_counter[r_k] > 1:
754 cur_perm = self.permissions_repositories[r_k]
754 cur_perm = self.permissions_repositories[r_k]
755 p = self._choose_permission(p, cur_perm)
755 p = self._choose_permission(p, cur_perm)
756
756
757 self.permissions_repositories[r_k] = p, o, obj_id
757 self.permissions_repositories[r_k] = p, o, obj_id
758
758
759 if perm.Repository.user_id == self.user_id:
759 if perm.Repository.user_id == self.user_id:
760 # set admin if owner
760 # set admin if owner
761 p = 'repository.admin'
761 p = 'repository.admin'
762 o = PermOrigin.REPO_OWNER
762 o = PermOrigin.REPO_OWNER
763 self.permissions_repositories[r_k] = p, o, obj_id
763 self.permissions_repositories[r_k] = p, o, obj_id
764
764
765 if self.user_is_admin:
765 if self.user_is_admin:
766 p = 'repository.admin'
766 p = 'repository.admin'
767 o = PermOrigin.SUPER_ADMIN
767 o = PermOrigin.SUPER_ADMIN
768 self.permissions_repositories[r_k] = p, o, obj_id
768 self.permissions_repositories[r_k] = p, o, obj_id
769
769
770 # user explicit permissions for repositories, overrides any specified
770 # user explicit permissions for repositories, overrides any specified
771 # by the group permission
771 # by the group permission
772 user_repo_perms = Permission.get_default_repo_perms(
772 user_repo_perms = Permission.get_default_repo_perms(
773 self.user_id, self.scope_repo_id)
773 self.user_id, self.scope_repo_id)
774 for perm in user_repo_perms:
774 for perm in user_repo_perms:
775 r_k = perm.UserRepoToPerm.repository.repo_name
775 r_k = perm.UserRepoToPerm.repository.repo_name
776 obj_id = perm.UserRepoToPerm.repository.repo_id
776 obj_id = perm.UserRepoToPerm.repository.repo_id
777 p = perm.Permission.permission_name
777 p = perm.Permission.permission_name
778 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
778 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
779
779
780 if not self.explicit:
780 if not self.explicit:
781 cur_perm = self.permissions_repositories.get(
781 cur_perm = self.permissions_repositories.get(
782 r_k, 'repository.none')
782 r_k, 'repository.none')
783 p = self._choose_permission(p, cur_perm)
783 p = self._choose_permission(p, cur_perm)
784
784
785 self.permissions_repositories[r_k] = p, o, obj_id
785 self.permissions_repositories[r_k] = p, o, obj_id
786
786
787 if perm.Repository.user_id == self.user_id:
787 if perm.Repository.user_id == self.user_id:
788 # set admin if owner
788 # set admin if owner
789 p = 'repository.admin'
789 p = 'repository.admin'
790 o = PermOrigin.REPO_OWNER
790 o = PermOrigin.REPO_OWNER
791 self.permissions_repositories[r_k] = p, o, obj_id
791 self.permissions_repositories[r_k] = p, o, obj_id
792
792
793 if self.user_is_admin:
793 if self.user_is_admin:
794 p = 'repository.admin'
794 p = 'repository.admin'
795 o = PermOrigin.SUPER_ADMIN
795 o = PermOrigin.SUPER_ADMIN
796 self.permissions_repositories[r_k] = p, o, obj_id
796 self.permissions_repositories[r_k] = p, o, obj_id
797
797
798 def _calculate_repository_branch_permissions(self):
798 def _calculate_repository_branch_permissions(self):
799 # user group for repositories permissions
799 # user group for repositories permissions
800 user_repo_branch_perms_from_user_group = Permission\
800 user_repo_branch_perms_from_user_group = Permission\
801 .get_default_repo_branch_perms_from_user_group(
801 .get_default_repo_branch_perms_from_user_group(
802 self.user_id, self.scope_repo_id)
802 self.user_id, self.scope_repo_id)
803
803
804 multiple_counter = collections.defaultdict(int)
804 multiple_counter = collections.defaultdict(int)
805 for perm in user_repo_branch_perms_from_user_group:
805 for perm in user_repo_branch_perms_from_user_group:
806 r_k = perm.UserGroupRepoToPerm.repository.repo_name
806 r_k = perm.UserGroupRepoToPerm.repository.repo_name
807 p = perm.Permission.permission_name
807 p = perm.Permission.permission_name
808 pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
808 pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
809 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
809 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
810 .users_group.users_group_name
810 .users_group.users_group_name
811
811
812 multiple_counter[r_k] += 1
812 multiple_counter[r_k] += 1
813 if multiple_counter[r_k] > 1:
813 if multiple_counter[r_k] > 1:
814 cur_perm = self.permissions_repository_branches[r_k][pattern]
814 cur_perm = self.permissions_repository_branches[r_k][pattern]
815 p = self._choose_permission(p, cur_perm)
815 p = self._choose_permission(p, cur_perm)
816
816
817 self.permissions_repository_branches[r_k] = pattern, p, o
817 self.permissions_repository_branches[r_k] = pattern, p, o
818
818
819 # user explicit branch permissions for repositories, overrides
819 # user explicit branch permissions for repositories, overrides
820 # any specified by the group permission
820 # any specified by the group permission
821 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
821 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
822 self.user_id, self.scope_repo_id)
822 self.user_id, self.scope_repo_id)
823
823
824 for perm in user_repo_branch_perms:
824 for perm in user_repo_branch_perms:
825
825
826 r_k = perm.UserRepoToPerm.repository.repo_name
826 r_k = perm.UserRepoToPerm.repository.repo_name
827 p = perm.Permission.permission_name
827 p = perm.Permission.permission_name
828 pattern = perm.UserToRepoBranchPermission.branch_pattern
828 pattern = perm.UserToRepoBranchPermission.branch_pattern
829 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
829 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
830
830
831 if not self.explicit:
831 if not self.explicit:
832 cur_perm = self.permissions_repository_branches.get(r_k)
832 cur_perm = self.permissions_repository_branches.get(r_k)
833 if cur_perm:
833 if cur_perm:
834 cur_perm = cur_perm[pattern]
834 cur_perm = cur_perm[pattern]
835 cur_perm = cur_perm or 'branch.none'
835 cur_perm = cur_perm or 'branch.none'
836 p = self._choose_permission(p, cur_perm)
836 p = self._choose_permission(p, cur_perm)
837
837
838 # NOTE(marcink): register all pattern/perm instances in this
838 # NOTE(marcink): register all pattern/perm instances in this
839 # special dict that aggregates entries
839 # special dict that aggregates entries
840 self.permissions_repository_branches[r_k] = pattern, p, o
840 self.permissions_repository_branches[r_k] = pattern, p, o
841
841
842 def _calculate_repository_group_permissions(self):
842 def _calculate_repository_group_permissions(self):
843 """
843 """
844 Repository group permissions for the current user.
844 Repository group permissions for the current user.
845
845
846 Check if the user is part of user groups for repository groups and
846 Check if the user is part of user groups for repository groups and
847 fill in the permissions from it. `_choose_permission` decides of which
847 fill in the permissions from it. `_choose_permission` decides of which
848 permission should be selected based on selected method.
848 permission should be selected based on selected method.
849 """
849 """
850 # user group for repo groups permissions
850 # user group for repo groups permissions
851 user_repo_group_perms_from_user_group = Permission\
851 user_repo_group_perms_from_user_group = Permission\
852 .get_default_group_perms_from_user_group(
852 .get_default_group_perms_from_user_group(
853 self.user_id, self.scope_repo_group_id)
853 self.user_id, self.scope_repo_group_id)
854
854
855 multiple_counter = collections.defaultdict(int)
855 multiple_counter = collections.defaultdict(int)
856 for perm in user_repo_group_perms_from_user_group:
856 for perm in user_repo_group_perms_from_user_group:
857 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
857 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
858 obj_id = perm.UserGroupRepoGroupToPerm.group.group_id
858 obj_id = perm.UserGroupRepoGroupToPerm.group.group_id
859 multiple_counter[rg_k] += 1
859 multiple_counter[rg_k] += 1
860 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
860 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
861 .users_group.users_group_name
861 .users_group.users_group_name
862 p = perm.Permission.permission_name
862 p = perm.Permission.permission_name
863
863
864 if multiple_counter[rg_k] > 1:
864 if multiple_counter[rg_k] > 1:
865 cur_perm = self.permissions_repository_groups[rg_k]
865 cur_perm = self.permissions_repository_groups[rg_k]
866 p = self._choose_permission(p, cur_perm)
866 p = self._choose_permission(p, cur_perm)
867 self.permissions_repository_groups[rg_k] = p, o, obj_id
867 self.permissions_repository_groups[rg_k] = p, o, obj_id
868
868
869 if perm.RepoGroup.user_id == self.user_id:
869 if perm.RepoGroup.user_id == self.user_id:
870 # set admin if owner, even for member of other user group
870 # set admin if owner, even for member of other user group
871 p = 'group.admin'
871 p = 'group.admin'
872 o = PermOrigin.REPOGROUP_OWNER
872 o = PermOrigin.REPOGROUP_OWNER
873 self.permissions_repository_groups[rg_k] = p, o, obj_id
873 self.permissions_repository_groups[rg_k] = p, o, obj_id
874
874
875 if self.user_is_admin:
875 if self.user_is_admin:
876 p = 'group.admin'
876 p = 'group.admin'
877 o = PermOrigin.SUPER_ADMIN
877 o = PermOrigin.SUPER_ADMIN
878 self.permissions_repository_groups[rg_k] = p, o, obj_id
878 self.permissions_repository_groups[rg_k] = p, o, obj_id
879
879
880 # user explicit permissions for repository groups
880 # user explicit permissions for repository groups
881 user_repo_groups_perms = Permission.get_default_group_perms(
881 user_repo_groups_perms = Permission.get_default_group_perms(
882 self.user_id, self.scope_repo_group_id)
882 self.user_id, self.scope_repo_group_id)
883 for perm in user_repo_groups_perms:
883 for perm in user_repo_groups_perms:
884 rg_k = perm.UserRepoGroupToPerm.group.group_name
884 rg_k = perm.UserRepoGroupToPerm.group.group_name
885 obj_id = perm.UserRepoGroupToPerm.group.group_id
885 obj_id = perm.UserRepoGroupToPerm.group.group_id
886 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
886 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
887 .user.username
887 .user.username
888 p = perm.Permission.permission_name
888 p = perm.Permission.permission_name
889
889
890 if not self.explicit:
890 if not self.explicit:
891 cur_perm = self.permissions_repository_groups.get(rg_k, 'group.none')
891 cur_perm = self.permissions_repository_groups.get(rg_k, 'group.none')
892 p = self._choose_permission(p, cur_perm)
892 p = self._choose_permission(p, cur_perm)
893
893
894 self.permissions_repository_groups[rg_k] = p, o, obj_id
894 self.permissions_repository_groups[rg_k] = p, o, obj_id
895
895
896 if perm.RepoGroup.user_id == self.user_id:
896 if perm.RepoGroup.user_id == self.user_id:
897 # set admin if owner
897 # set admin if owner
898 p = 'group.admin'
898 p = 'group.admin'
899 o = PermOrigin.REPOGROUP_OWNER
899 o = PermOrigin.REPOGROUP_OWNER
900 self.permissions_repository_groups[rg_k] = p, o, obj_id
900 self.permissions_repository_groups[rg_k] = p, o, obj_id
901
901
902 if self.user_is_admin:
902 if self.user_is_admin:
903 p = 'group.admin'
903 p = 'group.admin'
904 o = PermOrigin.SUPER_ADMIN
904 o = PermOrigin.SUPER_ADMIN
905 self.permissions_repository_groups[rg_k] = p, o, obj_id
905 self.permissions_repository_groups[rg_k] = p, o, obj_id
906
906
907 def _calculate_user_group_permissions(self):
907 def _calculate_user_group_permissions(self):
908 """
908 """
909 User group permissions for the current user.
909 User group permissions for the current user.
910 """
910 """
911 # user group for user group permissions
911 # user group for user group permissions
912 user_group_from_user_group = Permission\
912 user_group_from_user_group = Permission\
913 .get_default_user_group_perms_from_user_group(
913 .get_default_user_group_perms_from_user_group(
914 self.user_id, self.scope_user_group_id)
914 self.user_id, self.scope_user_group_id)
915
915
916 multiple_counter = collections.defaultdict(int)
916 multiple_counter = collections.defaultdict(int)
917 for perm in user_group_from_user_group:
917 for perm in user_group_from_user_group:
918 ug_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
918 ug_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
919 obj_id = perm.UserGroupUserGroupToPerm.target_user_group.users_group_id
919 obj_id = perm.UserGroupUserGroupToPerm.target_user_group.users_group_id
920 multiple_counter[ug_k] += 1
920 multiple_counter[ug_k] += 1
921 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
921 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
922 .user_group.users_group_name
922 .user_group.users_group_name
923 p = perm.Permission.permission_name
923 p = perm.Permission.permission_name
924
924
925 if multiple_counter[ug_k] > 1:
925 if multiple_counter[ug_k] > 1:
926 cur_perm = self.permissions_user_groups[ug_k]
926 cur_perm = self.permissions_user_groups[ug_k]
927 p = self._choose_permission(p, cur_perm)
927 p = self._choose_permission(p, cur_perm)
928
928
929 self.permissions_user_groups[ug_k] = p, o, obj_id
929 self.permissions_user_groups[ug_k] = p, o, obj_id
930
930
931 if perm.UserGroup.user_id == self.user_id:
931 if perm.UserGroup.user_id == self.user_id:
932 # set admin if owner, even for member of other user group
932 # set admin if owner, even for member of other user group
933 p = 'usergroup.admin'
933 p = 'usergroup.admin'
934 o = PermOrigin.USERGROUP_OWNER
934 o = PermOrigin.USERGROUP_OWNER
935 self.permissions_user_groups[ug_k] = p, o, obj_id
935 self.permissions_user_groups[ug_k] = p, o, obj_id
936
936
937 if self.user_is_admin:
937 if self.user_is_admin:
938 p = 'usergroup.admin'
938 p = 'usergroup.admin'
939 o = PermOrigin.SUPER_ADMIN
939 o = PermOrigin.SUPER_ADMIN
940 self.permissions_user_groups[ug_k] = p, o, obj_id
940 self.permissions_user_groups[ug_k] = p, o, obj_id
941
941
942 # user explicit permission for user groups
942 # user explicit permission for user groups
943 user_user_groups_perms = Permission.get_default_user_group_perms(
943 user_user_groups_perms = Permission.get_default_user_group_perms(
944 self.user_id, self.scope_user_group_id)
944 self.user_id, self.scope_user_group_id)
945 for perm in user_user_groups_perms:
945 for perm in user_user_groups_perms:
946 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
946 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
947 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
947 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
948 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
948 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
949 .user.username
949 .user.username
950 p = perm.Permission.permission_name
950 p = perm.Permission.permission_name
951
951
952 if not self.explicit:
952 if not self.explicit:
953 cur_perm = self.permissions_user_groups.get(ug_k, 'usergroup.none')
953 cur_perm = self.permissions_user_groups.get(ug_k, 'usergroup.none')
954 p = self._choose_permission(p, cur_perm)
954 p = self._choose_permission(p, cur_perm)
955
955
956 self.permissions_user_groups[ug_k] = p, o, obj_id
956 self.permissions_user_groups[ug_k] = p, o, obj_id
957
957
958 if perm.UserGroup.user_id == self.user_id:
958 if perm.UserGroup.user_id == self.user_id:
959 # set admin if owner
959 # set admin if owner
960 p = 'usergroup.admin'
960 p = 'usergroup.admin'
961 o = PermOrigin.USERGROUP_OWNER
961 o = PermOrigin.USERGROUP_OWNER
962 self.permissions_user_groups[ug_k] = p, o, obj_id
962 self.permissions_user_groups[ug_k] = p, o, obj_id
963
963
964 if self.user_is_admin:
964 if self.user_is_admin:
965 p = 'usergroup.admin'
965 p = 'usergroup.admin'
966 o = PermOrigin.SUPER_ADMIN
966 o = PermOrigin.SUPER_ADMIN
967 self.permissions_user_groups[ug_k] = p, o, obj_id
967 self.permissions_user_groups[ug_k] = p, o, obj_id
968
968
969 def _choose_permission(self, new_perm, cur_perm):
969 def _choose_permission(self, new_perm, cur_perm):
970 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
970 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
971 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
971 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
972 if self.algo == 'higherwin':
972 if self.algo == 'higherwin':
973 if new_perm_val > cur_perm_val:
973 if new_perm_val > cur_perm_val:
974 return new_perm
974 return new_perm
975 return cur_perm
975 return cur_perm
976 elif self.algo == 'lowerwin':
976 elif self.algo == 'lowerwin':
977 if new_perm_val < cur_perm_val:
977 if new_perm_val < cur_perm_val:
978 return new_perm
978 return new_perm
979 return cur_perm
979 return cur_perm
980
980
981 def _permission_structure(self):
981 def _permission_structure(self):
982 return {
982 return {
983 'global': self.permissions_global,
983 'global': self.permissions_global,
984 'repositories': self.permissions_repositories,
984 'repositories': self.permissions_repositories,
985 'repository_branches': self.permissions_repository_branches,
985 'repository_branches': self.permissions_repository_branches,
986 'repositories_groups': self.permissions_repository_groups,
986 'repositories_groups': self.permissions_repository_groups,
987 'user_groups': self.permissions_user_groups,
987 'user_groups': self.permissions_user_groups,
988 }
988 }
989
989
990
990
991 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
991 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
992 """
992 """
993 Check if given controller_name is in whitelist of auth token access
993 Check if given controller_name is in whitelist of auth token access
994 """
994 """
995 if not whitelist:
995 if not whitelist:
996 from rhodecode import CONFIG
996 from rhodecode import CONFIG
997 whitelist = aslist(
997 whitelist = aslist(
998 CONFIG.get('api_access_controllers_whitelist'), sep=',')
998 CONFIG.get('api_access_controllers_whitelist'), sep=',')
999 # backward compat translation
999 # backward compat translation
1000 compat = {
1000 compat = {
1001 # old controller, new VIEW
1001 # old controller, new VIEW
1002 'ChangesetController:*': 'RepoCommitsView:*',
1002 'ChangesetController:*': 'RepoCommitsView:*',
1003 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
1003 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
1004 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
1004 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
1005 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
1005 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
1006 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
1006 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
1007 'GistsController:*': 'GistView:*',
1007 'GistsController:*': 'GistView:*',
1008 }
1008 }
1009
1009
1010 log.debug(
1010 log.debug(
1011 'Allowed views for AUTH TOKEN access: %s', whitelist)
1011 'Allowed views for AUTH TOKEN access: %s', whitelist)
1012 auth_token_access_valid = False
1012 auth_token_access_valid = False
1013
1013
1014 for entry in whitelist:
1014 for entry in whitelist:
1015 token_match = True
1015 token_match = True
1016 if entry in compat:
1016 if entry in compat:
1017 # translate from old Controllers to Pyramid Views
1017 # translate from old Controllers to Pyramid Views
1018 entry = compat[entry]
1018 entry = compat[entry]
1019
1019
1020 if '@' in entry:
1020 if '@' in entry:
1021 # specific AuthToken
1021 # specific AuthToken
1022 entry, allowed_token = entry.split('@', 1)
1022 entry, allowed_token = entry.split('@', 1)
1023 token_match = auth_token == allowed_token
1023 token_match = auth_token == allowed_token
1024
1024
1025 if fnmatch.fnmatch(view_name, entry) and token_match:
1025 if fnmatch.fnmatch(view_name, entry) and token_match:
1026 auth_token_access_valid = True
1026 auth_token_access_valid = True
1027 break
1027 break
1028
1028
1029 if auth_token_access_valid:
1029 if auth_token_access_valid:
1030 log.debug('view: `%s` matches entry in whitelist: %s',
1030 log.debug('view: `%s` matches entry in whitelist: %s',
1031 view_name, whitelist)
1031 view_name, whitelist)
1032
1032
1033 else:
1033 else:
1034 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
1034 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
1035 % (view_name, whitelist))
1035 % (view_name, whitelist))
1036 if auth_token:
1036 if auth_token:
1037 # if we use auth token key and don't have access it's a warning
1037 # if we use auth token key and don't have access it's a warning
1038 log.warning(msg)
1038 log.warning(msg)
1039 else:
1039 else:
1040 log.debug(msg)
1040 log.debug(msg)
1041
1041
1042 return auth_token_access_valid
1042 return auth_token_access_valid
1043
1043
1044
1044
1045 class AuthUser(object):
1045 class AuthUser(object):
1046 """
1046 """
1047 A simple object that handles all attributes of user in RhodeCode
1047 A simple object that handles all attributes of user in RhodeCode
1048
1048
1049 It does lookup based on API key,given user, or user present in session
1049 It does lookup based on API key,given user, or user present in session
1050 Then it fills all required information for such user. It also checks if
1050 Then it fills all required information for such user. It also checks if
1051 anonymous access is enabled and if so, it returns default user as logged in
1051 anonymous access is enabled and if so, it returns default user as logged in
1052 """
1052 """
1053 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
1053 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
1054 repo_read_perms = ['repository.read', 'repository.admin', 'repository.write']
1055 repo_group_read_perms = ['group.read', 'group.write', 'group.admin']
1054
1056
1055 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
1057 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
1056
1058
1057 self.user_id = user_id
1059 self.user_id = user_id
1058 self._api_key = api_key
1060 self._api_key = api_key
1059
1061
1060 self.api_key = None
1062 self.api_key = None
1061 self.username = username
1063 self.username = username
1062 self.ip_addr = ip_addr
1064 self.ip_addr = ip_addr
1063 self.name = ''
1065 self.name = ''
1064 self.lastname = ''
1066 self.lastname = ''
1065 self.first_name = ''
1067 self.first_name = ''
1066 self.last_name = ''
1068 self.last_name = ''
1067 self.email = ''
1069 self.email = ''
1068 self.is_authenticated = False
1070 self.is_authenticated = False
1069 self.admin = False
1071 self.admin = False
1070 self.inherit_default_permissions = False
1072 self.inherit_default_permissions = False
1071 self.password = ''
1073 self.password = ''
1072
1074
1073 self.anonymous_user = None # propagated on propagate_data
1075 self.anonymous_user = None # propagated on propagate_data
1074 self.propagate_data()
1076 self.propagate_data()
1075 self._instance = None
1077 self._instance = None
1076 self._permissions_scoped_cache = {} # used to bind scoped calculation
1078 self._permissions_scoped_cache = {} # used to bind scoped calculation
1077
1079
1078 @LazyProperty
1080 @LazyProperty
1079 def permissions(self):
1081 def permissions(self):
1080 return self.get_perms(user=self, cache=None)
1082 return self.get_perms(user=self, cache=None)
1081
1083
1082 @LazyProperty
1084 @LazyProperty
1083 def permissions_safe(self):
1085 def permissions_safe(self):
1084 """
1086 """
1085 Filtered permissions excluding not allowed repositories
1087 Filtered permissions excluding not allowed repositories
1086 """
1088 """
1087 perms = self.get_perms(user=self, cache=None)
1089 perms = self.get_perms(user=self, cache=None)
1088
1090
1089 perms['repositories'] = {
1091 perms['repositories'] = {
1090 k: v for k, v in perms['repositories'].items()
1092 k: v for k, v in perms['repositories'].items()
1091 if v != 'repository.none'}
1093 if v != 'repository.none'}
1092 perms['repositories_groups'] = {
1094 perms['repositories_groups'] = {
1093 k: v for k, v in perms['repositories_groups'].items()
1095 k: v for k, v in perms['repositories_groups'].items()
1094 if v != 'group.none'}
1096 if v != 'group.none'}
1095 perms['user_groups'] = {
1097 perms['user_groups'] = {
1096 k: v for k, v in perms['user_groups'].items()
1098 k: v for k, v in perms['user_groups'].items()
1097 if v != 'usergroup.none'}
1099 if v != 'usergroup.none'}
1098 perms['repository_branches'] = {
1100 perms['repository_branches'] = {
1099 k: v for k, v in perms['repository_branches'].iteritems()
1101 k: v for k, v in perms['repository_branches'].iteritems()
1100 if v != 'branch.none'}
1102 if v != 'branch.none'}
1101 return perms
1103 return perms
1102
1104
1103 @LazyProperty
1105 @LazyProperty
1104 def permissions_full_details(self):
1106 def permissions_full_details(self):
1105 return self.get_perms(
1107 return self.get_perms(
1106 user=self, cache=None, calculate_super_admin=True)
1108 user=self, cache=None, calculate_super_admin=True)
1107
1109
1108 def permissions_with_scope(self, scope):
1110 def permissions_with_scope(self, scope):
1109 """
1111 """
1110 Call the get_perms function with scoped data. The scope in that function
1112 Call the get_perms function with scoped data. The scope in that function
1111 narrows the SQL calls to the given ID of objects resulting in fetching
1113 narrows the SQL calls to the given ID of objects resulting in fetching
1112 Just particular permission we want to obtain. If scope is an empty dict
1114 Just particular permission we want to obtain. If scope is an empty dict
1113 then it basically narrows the scope to GLOBAL permissions only.
1115 then it basically narrows the scope to GLOBAL permissions only.
1114
1116
1115 :param scope: dict
1117 :param scope: dict
1116 """
1118 """
1117 if 'repo_name' in scope:
1119 if 'repo_name' in scope:
1118 obj = Repository.get_by_repo_name(scope['repo_name'])
1120 obj = Repository.get_by_repo_name(scope['repo_name'])
1119 if obj:
1121 if obj:
1120 scope['repo_id'] = obj.repo_id
1122 scope['repo_id'] = obj.repo_id
1121 _scope = collections.OrderedDict()
1123 _scope = collections.OrderedDict()
1122 _scope['repo_id'] = -1
1124 _scope['repo_id'] = -1
1123 _scope['user_group_id'] = -1
1125 _scope['user_group_id'] = -1
1124 _scope['repo_group_id'] = -1
1126 _scope['repo_group_id'] = -1
1125
1127
1126 for k in sorted(scope.keys()):
1128 for k in sorted(scope.keys()):
1127 _scope[k] = scope[k]
1129 _scope[k] = scope[k]
1128
1130
1129 # store in cache to mimic how the @LazyProperty works,
1131 # store in cache to mimic how the @LazyProperty works,
1130 # the difference here is that we use the unique key calculated
1132 # the difference here is that we use the unique key calculated
1131 # from params and values
1133 # from params and values
1132 return self.get_perms(user=self, cache=None, scope=_scope)
1134 return self.get_perms(user=self, cache=None, scope=_scope)
1133
1135
1134 def get_instance(self):
1136 def get_instance(self):
1135 return User.get(self.user_id)
1137 return User.get(self.user_id)
1136
1138
1137 def propagate_data(self):
1139 def propagate_data(self):
1138 """
1140 """
1139 Fills in user data and propagates values to this instance. Maps fetched
1141 Fills in user data and propagates values to this instance. Maps fetched
1140 user attributes to this class instance attributes
1142 user attributes to this class instance attributes
1141 """
1143 """
1142 log.debug('AuthUser: starting data propagation for new potential user')
1144 log.debug('AuthUser: starting data propagation for new potential user')
1143 user_model = UserModel()
1145 user_model = UserModel()
1144 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1146 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1145 is_user_loaded = False
1147 is_user_loaded = False
1146
1148
1147 # lookup by userid
1149 # lookup by userid
1148 if self.user_id is not None and self.user_id != anon_user.user_id:
1150 if self.user_id is not None and self.user_id != anon_user.user_id:
1149 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1151 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1150 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1152 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1151
1153
1152 # try go get user by api key
1154 # try go get user by api key
1153 elif self._api_key and self._api_key != anon_user.api_key:
1155 elif self._api_key and self._api_key != anon_user.api_key:
1154 log.debug('Trying Auth User lookup by API KEY: `...%s`', self._api_key[-4:])
1156 log.debug('Trying Auth User lookup by API KEY: `...%s`', self._api_key[-4:])
1155 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1157 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1156
1158
1157 # lookup by username
1159 # lookup by username
1158 elif self.username:
1160 elif self.username:
1159 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1161 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1160 is_user_loaded = user_model.fill_data(self, username=self.username)
1162 is_user_loaded = user_model.fill_data(self, username=self.username)
1161 else:
1163 else:
1162 log.debug('No data in %s that could been used to log in', self)
1164 log.debug('No data in %s that could been used to log in', self)
1163
1165
1164 if not is_user_loaded:
1166 if not is_user_loaded:
1165 log.debug(
1167 log.debug(
1166 'Failed to load user. Fallback to default user %s', anon_user)
1168 'Failed to load user. Fallback to default user %s', anon_user)
1167 # if we cannot authenticate user try anonymous
1169 # if we cannot authenticate user try anonymous
1168 if anon_user.active:
1170 if anon_user.active:
1169 log.debug('default user is active, using it as a session user')
1171 log.debug('default user is active, using it as a session user')
1170 user_model.fill_data(self, user_id=anon_user.user_id)
1172 user_model.fill_data(self, user_id=anon_user.user_id)
1171 # then we set this user is logged in
1173 # then we set this user is logged in
1172 self.is_authenticated = True
1174 self.is_authenticated = True
1173 else:
1175 else:
1174 log.debug('default user is NOT active')
1176 log.debug('default user is NOT active')
1175 # in case of disabled anonymous user we reset some of the
1177 # in case of disabled anonymous user we reset some of the
1176 # parameters so such user is "corrupted", skipping the fill_data
1178 # parameters so such user is "corrupted", skipping the fill_data
1177 for attr in ['user_id', 'username', 'admin', 'active']:
1179 for attr in ['user_id', 'username', 'admin', 'active']:
1178 setattr(self, attr, None)
1180 setattr(self, attr, None)
1179 self.is_authenticated = False
1181 self.is_authenticated = False
1180
1182
1181 if not self.username:
1183 if not self.username:
1182 self.username = 'None'
1184 self.username = 'None'
1183
1185
1184 log.debug('AuthUser: propagated user is now %s', self)
1186 log.debug('AuthUser: propagated user is now %s', self)
1185
1187
1186 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1188 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1187 calculate_super_admin=False, cache=None):
1189 calculate_super_admin=False, cache=None):
1188 """
1190 """
1189 Fills user permission attribute with permissions taken from database
1191 Fills user permission attribute with permissions taken from database
1190 works for permissions given for repositories, and for permissions that
1192 works for permissions given for repositories, and for permissions that
1191 are granted to groups
1193 are granted to groups
1192
1194
1193 :param user: instance of User object from database
1195 :param user: instance of User object from database
1194 :param explicit: In case there are permissions both for user and a group
1196 :param explicit: In case there are permissions both for user and a group
1195 that user is part of, explicit flag will defiine if user will
1197 that user is part of, explicit flag will defiine if user will
1196 explicitly override permissions from group, if it's False it will
1198 explicitly override permissions from group, if it's False it will
1197 make decision based on the algo
1199 make decision based on the algo
1198 :param algo: algorithm to decide what permission should be choose if
1200 :param algo: algorithm to decide what permission should be choose if
1199 it's multiple defined, eg user in two different groups. It also
1201 it's multiple defined, eg user in two different groups. It also
1200 decides if explicit flag is turned off how to specify the permission
1202 decides if explicit flag is turned off how to specify the permission
1201 for case when user is in a group + have defined separate permission
1203 for case when user is in a group + have defined separate permission
1202 :param calculate_super_admin: calculate permissions for super-admin in the
1204 :param calculate_super_admin: calculate permissions for super-admin in the
1203 same way as for regular user without speedups
1205 same way as for regular user without speedups
1204 :param cache: Use caching for calculation, None = let the cache backend decide
1206 :param cache: Use caching for calculation, None = let the cache backend decide
1205 """
1207 """
1206 user_id = user.user_id
1208 user_id = user.user_id
1207 user_is_admin = user.is_admin
1209 user_is_admin = user.is_admin
1208
1210
1209 # inheritance of global permissions like create repo/fork repo etc
1211 # inheritance of global permissions like create repo/fork repo etc
1210 user_inherit_default_permissions = user.inherit_default_permissions
1212 user_inherit_default_permissions = user.inherit_default_permissions
1211
1213
1212 cache_seconds = safe_int(
1214 cache_seconds = safe_int(
1213 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1215 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1214
1216
1215 if cache is None:
1217 if cache is None:
1216 # let the backend cache decide
1218 # let the backend cache decide
1217 cache_on = cache_seconds > 0
1219 cache_on = cache_seconds > 0
1218 else:
1220 else:
1219 cache_on = cache
1221 cache_on = cache
1220
1222
1221 log.debug(
1223 log.debug(
1222 'Computing PERMISSION tree for user %s scope `%s` '
1224 'Computing PERMISSION tree for user %s scope `%s` '
1223 'with caching: %s[TTL: %ss]', user, scope, cache_on, cache_seconds or 0)
1225 'with caching: %s[TTL: %ss]', user, scope, cache_on, cache_seconds or 0)
1224
1226
1225 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
1227 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
1226 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1228 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1227
1229
1228 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
1230 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
1229 condition=cache_on)
1231 condition=cache_on)
1230 def compute_perm_tree(cache_name,
1232 def compute_perm_tree(cache_name,
1231 user_id, scope, user_is_admin,user_inherit_default_permissions,
1233 user_id, scope, user_is_admin,user_inherit_default_permissions,
1232 explicit, algo, calculate_super_admin):
1234 explicit, algo, calculate_super_admin):
1233 return _cached_perms_data(
1235 return _cached_perms_data(
1234 user_id, scope, user_is_admin, user_inherit_default_permissions,
1236 user_id, scope, user_is_admin, user_inherit_default_permissions,
1235 explicit, algo, calculate_super_admin)
1237 explicit, algo, calculate_super_admin)
1236
1238
1237 start = time.time()
1239 start = time.time()
1238 result = compute_perm_tree(
1240 result = compute_perm_tree(
1239 'permissions', user_id, scope, user_is_admin,
1241 'permissions', user_id, scope, user_is_admin,
1240 user_inherit_default_permissions, explicit, algo,
1242 user_inherit_default_permissions, explicit, algo,
1241 calculate_super_admin)
1243 calculate_super_admin)
1242
1244
1243 result_repr = []
1245 result_repr = []
1244 for k in result:
1246 for k in result:
1245 result_repr.append((k, len(result[k])))
1247 result_repr.append((k, len(result[k])))
1246 total = time.time() - start
1248 total = time.time() - start
1247 log.debug('PERMISSION tree for user %s computed in %.4fs: %s',
1249 log.debug('PERMISSION tree for user %s computed in %.4fs: %s',
1248 user, total, result_repr)
1250 user, total, result_repr)
1249
1251
1250 return result
1252 return result
1251
1253
1252 @property
1254 @property
1253 def is_default(self):
1255 def is_default(self):
1254 return self.username == User.DEFAULT_USER
1256 return self.username == User.DEFAULT_USER
1255
1257
1256 @property
1258 @property
1257 def is_admin(self):
1259 def is_admin(self):
1258 return self.admin
1260 return self.admin
1259
1261
1260 @property
1262 @property
1261 def is_user_object(self):
1263 def is_user_object(self):
1262 return self.user_id is not None
1264 return self.user_id is not None
1263
1265
1264 @property
1266 @property
1265 def repositories_admin(self):
1267 def repositories_admin(self):
1266 """
1268 """
1267 Returns list of repositories you're an admin of
1269 Returns list of repositories you're an admin of
1268 """
1270 """
1269 return [
1271 return [
1270 x[0] for x in self.permissions['repositories'].items()
1272 x[0] for x in self.permissions['repositories'].items()
1271 if x[1] == 'repository.admin']
1273 if x[1] == 'repository.admin']
1272
1274
1273 @property
1275 @property
1274 def repository_groups_admin(self):
1276 def repository_groups_admin(self):
1275 """
1277 """
1276 Returns list of repository groups you're an admin of
1278 Returns list of repository groups you're an admin of
1277 """
1279 """
1278 return [
1280 return [
1279 x[0] for x in self.permissions['repositories_groups'].items()
1281 x[0] for x in self.permissions['repositories_groups'].items()
1280 if x[1] == 'group.admin']
1282 if x[1] == 'group.admin']
1281
1283
1282 @property
1284 @property
1283 def user_groups_admin(self):
1285 def user_groups_admin(self):
1284 """
1286 """
1285 Returns list of user groups you're an admin of
1287 Returns list of user groups you're an admin of
1286 """
1288 """
1287 return [
1289 return [
1288 x[0] for x in self.permissions['user_groups'].items()
1290 x[0] for x in self.permissions['user_groups'].items()
1289 if x[1] == 'usergroup.admin']
1291 if x[1] == 'usergroup.admin']
1290
1292
1291 def repo_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1293 def repo_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1292 if not perms:
1294 if not perms:
1293 perms = ['repository.read', 'repository.write', 'repository.admin']
1295 perms = ['repository.read', 'repository.write', 'repository.admin']
1294 allowed_ids = []
1296 allowed_ids = []
1295 for k, stack_data in self.permissions['repositories'].perm_origin_stack.items():
1297 for k, stack_data in self.permissions['repositories'].perm_origin_stack.items():
1296 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1298 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1297 if prefix_filter and not k.startswith(prefix_filter):
1299 if prefix_filter and not k.startswith(prefix_filter):
1298 continue
1300 continue
1299 if perm in perms:
1301 if perm in perms:
1300 allowed_ids.append(obj_id)
1302 allowed_ids.append(obj_id)
1301 return allowed_ids
1303 return allowed_ids
1302
1304
1303 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1305 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1304 """
1306 """
1305 Returns list of repository ids that user have access to based on given
1307 Returns list of repository ids that user have access to based on given
1306 perms. The cache flag should be only used in cases that are used for
1308 perms. The cache flag should be only used in cases that are used for
1307 display purposes, NOT IN ANY CASE for permission checks.
1309 display purposes, NOT IN ANY CASE for permission checks.
1308 """
1310 """
1309 from rhodecode.model.scm import RepoList
1311 from rhodecode.model.scm import RepoList
1310 if not perms:
1312 if not perms:
1311 perms = ['repository.read', 'repository.write', 'repository.admin']
1313 perms = ['repository.read', 'repository.write', 'repository.admin']
1312
1314
1313 def _cached_repo_acl(user_id, perm_def, _name_filter):
1315 def _cached_repo_acl(user_id, perm_def, _name_filter):
1314 qry = Repository.query()
1316 qry = Repository.query()
1315 if _name_filter:
1317 if _name_filter:
1316 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1318 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1317 qry = qry.filter(
1319 qry = qry.filter(
1318 Repository.repo_name.ilike(ilike_expression))
1320 Repository.repo_name.ilike(ilike_expression))
1319
1321
1320 return [x.repo_id for x in
1322 return [x.repo_id for x in
1321 RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1323 RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1322
1324
1323 return _cached_repo_acl(self.user_id, perms, name_filter)
1325 return _cached_repo_acl(self.user_id, perms, name_filter)
1324
1326
1325 def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1327 def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1326 if not perms:
1328 if not perms:
1327 perms = ['group.read', 'group.write', 'group.admin']
1329 perms = ['group.read', 'group.write', 'group.admin']
1328 allowed_ids = []
1330 allowed_ids = []
1329 for k, stack_data in self.permissions['repositories_groups'].perm_origin_stack.items():
1331 for k, stack_data in self.permissions['repositories_groups'].perm_origin_stack.items():
1330 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1332 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1331 if prefix_filter and not k.startswith(prefix_filter):
1333 if prefix_filter and not k.startswith(prefix_filter):
1332 continue
1334 continue
1333 if perm in perms:
1335 if perm in perms:
1334 allowed_ids.append(obj_id)
1336 allowed_ids.append(obj_id)
1335 return allowed_ids
1337 return allowed_ids
1336
1338
1337 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1339 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1338 """
1340 """
1339 Returns list of repository group ids that user have access to based on given
1341 Returns list of repository group ids that user have access to based on given
1340 perms. The cache flag should be only used in cases that are used for
1342 perms. The cache flag should be only used in cases that are used for
1341 display purposes, NOT IN ANY CASE for permission checks.
1343 display purposes, NOT IN ANY CASE for permission checks.
1342 """
1344 """
1343 from rhodecode.model.scm import RepoGroupList
1345 from rhodecode.model.scm import RepoGroupList
1344 if not perms:
1346 if not perms:
1345 perms = ['group.read', 'group.write', 'group.admin']
1347 perms = ['group.read', 'group.write', 'group.admin']
1346
1348
1347 def _cached_repo_group_acl(user_id, perm_def, _name_filter):
1349 def _cached_repo_group_acl(user_id, perm_def, _name_filter):
1348 qry = RepoGroup.query()
1350 qry = RepoGroup.query()
1349 if _name_filter:
1351 if _name_filter:
1350 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1352 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1351 qry = qry.filter(
1353 qry = qry.filter(
1352 RepoGroup.group_name.ilike(ilike_expression))
1354 RepoGroup.group_name.ilike(ilike_expression))
1353
1355
1354 return [x.group_id for x in
1356 return [x.group_id for x in
1355 RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1357 RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1356
1358
1357 return _cached_repo_group_acl(self.user_id, perms, name_filter)
1359 return _cached_repo_group_acl(self.user_id, perms, name_filter)
1358
1360
1359 def user_group_acl_ids_from_stack(self, perms=None, cache=False):
1361 def user_group_acl_ids_from_stack(self, perms=None, cache=False):
1360 if not perms:
1362 if not perms:
1361 perms = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
1363 perms = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
1362 allowed_ids = []
1364 allowed_ids = []
1363 for k, stack_data in self.permissions['user_groups'].perm_origin_stack.items():
1365 for k, stack_data in self.permissions['user_groups'].perm_origin_stack.items():
1364 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1366 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1365 if perm in perms:
1367 if perm in perms:
1366 allowed_ids.append(obj_id)
1368 allowed_ids.append(obj_id)
1367 return allowed_ids
1369 return allowed_ids
1368
1370
1369 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1371 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1370 """
1372 """
1371 Returns list of user group ids that user have access to based on given
1373 Returns list of user group ids that user have access to based on given
1372 perms. The cache flag should be only used in cases that are used for
1374 perms. The cache flag should be only used in cases that are used for
1373 display purposes, NOT IN ANY CASE for permission checks.
1375 display purposes, NOT IN ANY CASE for permission checks.
1374 """
1376 """
1375 from rhodecode.model.scm import UserGroupList
1377 from rhodecode.model.scm import UserGroupList
1376 if not perms:
1378 if not perms:
1377 perms = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
1379 perms = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
1378
1380
1379 def _cached_user_group_acl(user_id, perm_def, name_filter):
1381 def _cached_user_group_acl(user_id, perm_def, name_filter):
1380 qry = UserGroup.query()
1382 qry = UserGroup.query()
1381 if name_filter:
1383 if name_filter:
1382 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1384 ilike_expression = u'%{}%'.format(safe_unicode(name_filter))
1383 qry = qry.filter(
1385 qry = qry.filter(
1384 UserGroup.users_group_name.ilike(ilike_expression))
1386 UserGroup.users_group_name.ilike(ilike_expression))
1385
1387
1386 return [x.users_group_id for x in
1388 return [x.users_group_id for x in
1387 UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1389 UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1388
1390
1389 return _cached_user_group_acl(self.user_id, perms, name_filter)
1391 return _cached_user_group_acl(self.user_id, perms, name_filter)
1390
1392
1391 @property
1393 @property
1392 def ip_allowed(self):
1394 def ip_allowed(self):
1393 """
1395 """
1394 Checks if ip_addr used in constructor is allowed from defined list of
1396 Checks if ip_addr used in constructor is allowed from defined list of
1395 allowed ip_addresses for user
1397 allowed ip_addresses for user
1396
1398
1397 :returns: boolean, True if ip is in allowed ip range
1399 :returns: boolean, True if ip is in allowed ip range
1398 """
1400 """
1399 # check IP
1401 # check IP
1400 inherit = self.inherit_default_permissions
1402 inherit = self.inherit_default_permissions
1401 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1403 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1402 inherit_from_default=inherit)
1404 inherit_from_default=inherit)
1403 @property
1405 @property
1404 def personal_repo_group(self):
1406 def personal_repo_group(self):
1405 return RepoGroup.get_user_personal_repo_group(self.user_id)
1407 return RepoGroup.get_user_personal_repo_group(self.user_id)
1406
1408
1407 @LazyProperty
1409 @LazyProperty
1408 def feed_token(self):
1410 def feed_token(self):
1409 return self.get_instance().feed_token
1411 return self.get_instance().feed_token
1410
1412
1411 @LazyProperty
1413 @LazyProperty
1412 def artifact_token(self):
1414 def artifact_token(self):
1413 return self.get_instance().artifact_token
1415 return self.get_instance().artifact_token
1414
1416
1415 @classmethod
1417 @classmethod
1416 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1418 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1417 allowed_ips = AuthUser.get_allowed_ips(
1419 allowed_ips = AuthUser.get_allowed_ips(
1418 user_id, cache=True, inherit_from_default=inherit_from_default)
1420 user_id, cache=True, inherit_from_default=inherit_from_default)
1419 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1421 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1420 log.debug('IP:%s for user %s is in range of %s',
1422 log.debug('IP:%s for user %s is in range of %s',
1421 ip_addr, user_id, allowed_ips)
1423 ip_addr, user_id, allowed_ips)
1422 return True
1424 return True
1423 else:
1425 else:
1424 log.info('Access for IP:%s forbidden for user %s, '
1426 log.info('Access for IP:%s forbidden for user %s, '
1425 'not in %s', ip_addr, user_id, allowed_ips)
1427 'not in %s', ip_addr, user_id, allowed_ips)
1426 return False
1428 return False
1427
1429
1428 def get_branch_permissions(self, repo_name, perms=None):
1430 def get_branch_permissions(self, repo_name, perms=None):
1429 perms = perms or self.permissions_with_scope({'repo_name': repo_name})
1431 perms = perms or self.permissions_with_scope({'repo_name': repo_name})
1430 branch_perms = perms.get('repository_branches', {})
1432 branch_perms = perms.get('repository_branches', {})
1431 if not branch_perms:
1433 if not branch_perms:
1432 return {}
1434 return {}
1433 repo_branch_perms = branch_perms.get(repo_name)
1435 repo_branch_perms = branch_perms.get(repo_name)
1434 return repo_branch_perms or {}
1436 return repo_branch_perms or {}
1435
1437
1436 def get_rule_and_branch_permission(self, repo_name, branch_name):
1438 def get_rule_and_branch_permission(self, repo_name, branch_name):
1437 """
1439 """
1438 Check if this AuthUser has defined any permissions for branches. If any of
1440 Check if this AuthUser has defined any permissions for branches. If any of
1439 the rules match in order, we return the matching permissions
1441 the rules match in order, we return the matching permissions
1440 """
1442 """
1441
1443
1442 rule = default_perm = ''
1444 rule = default_perm = ''
1443
1445
1444 repo_branch_perms = self.get_branch_permissions(repo_name=repo_name)
1446 repo_branch_perms = self.get_branch_permissions(repo_name=repo_name)
1445 if not repo_branch_perms:
1447 if not repo_branch_perms:
1446 return rule, default_perm
1448 return rule, default_perm
1447
1449
1448 # now calculate the permissions
1450 # now calculate the permissions
1449 for pattern, branch_perm in repo_branch_perms.items():
1451 for pattern, branch_perm in repo_branch_perms.items():
1450 if fnmatch.fnmatch(branch_name, pattern):
1452 if fnmatch.fnmatch(branch_name, pattern):
1451 rule = '`{}`=>{}'.format(pattern, branch_perm)
1453 rule = '`{}`=>{}'.format(pattern, branch_perm)
1452 return rule, branch_perm
1454 return rule, branch_perm
1453
1455
1454 return rule, default_perm
1456 return rule, default_perm
1455
1457
1456 def __repr__(self):
1458 def __repr__(self):
1457 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1459 return "<AuthUser('id:%s[%s] ip:%s auth:%s')>"\
1458 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1460 % (self.user_id, self.username, self.ip_addr, self.is_authenticated)
1459
1461
1460 def set_authenticated(self, authenticated=True):
1462 def set_authenticated(self, authenticated=True):
1461 if self.user_id != self.anonymous_user.user_id:
1463 if self.user_id != self.anonymous_user.user_id:
1462 self.is_authenticated = authenticated
1464 self.is_authenticated = authenticated
1463
1465
1464 def get_cookie_store(self):
1466 def get_cookie_store(self):
1465 return {
1467 return {
1466 'username': self.username,
1468 'username': self.username,
1467 'password': md5(self.password or ''),
1469 'password': md5(self.password or ''),
1468 'user_id': self.user_id,
1470 'user_id': self.user_id,
1469 'is_authenticated': self.is_authenticated
1471 'is_authenticated': self.is_authenticated
1470 }
1472 }
1471
1473
1472 @classmethod
1474 @classmethod
1473 def from_cookie_store(cls, cookie_store):
1475 def from_cookie_store(cls, cookie_store):
1474 """
1476 """
1475 Creates AuthUser from a cookie store
1477 Creates AuthUser from a cookie store
1476
1478
1477 :param cls:
1479 :param cls:
1478 :param cookie_store:
1480 :param cookie_store:
1479 """
1481 """
1480 user_id = cookie_store.get('user_id')
1482 user_id = cookie_store.get('user_id')
1481 username = cookie_store.get('username')
1483 username = cookie_store.get('username')
1482 api_key = cookie_store.get('api_key')
1484 api_key = cookie_store.get('api_key')
1483 return AuthUser(user_id, api_key, username)
1485 return AuthUser(user_id, api_key, username)
1484
1486
1485 @classmethod
1487 @classmethod
1486 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1488 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1487 _set = set()
1489 _set = set()
1488
1490
1489 if inherit_from_default:
1491 if inherit_from_default:
1490 def_user_id = User.get_default_user(cache=True).user_id
1492 def_user_id = User.get_default_user(cache=True).user_id
1491 default_ips = UserIpMap.query().filter(UserIpMap.user_id == def_user_id)
1493 default_ips = UserIpMap.query().filter(UserIpMap.user_id == def_user_id)
1492 if cache:
1494 if cache:
1493 default_ips = default_ips.options(
1495 default_ips = default_ips.options(
1494 FromCache("sql_cache_short", "get_user_ips_default"))
1496 FromCache("sql_cache_short", "get_user_ips_default"))
1495
1497
1496 # populate from default user
1498 # populate from default user
1497 for ip in default_ips:
1499 for ip in default_ips:
1498 try:
1500 try:
1499 _set.add(ip.ip_addr)
1501 _set.add(ip.ip_addr)
1500 except ObjectDeletedError:
1502 except ObjectDeletedError:
1501 # since we use heavy caching sometimes it happens that
1503 # since we use heavy caching sometimes it happens that
1502 # we get deleted objects here, we just skip them
1504 # we get deleted objects here, we just skip them
1503 pass
1505 pass
1504
1506
1505 # NOTE:(marcink) we don't want to load any rules for empty
1507 # NOTE:(marcink) we don't want to load any rules for empty
1506 # user_id which is the case of access of non logged users when anonymous
1508 # user_id which is the case of access of non logged users when anonymous
1507 # access is disabled
1509 # access is disabled
1508 user_ips = []
1510 user_ips = []
1509 if user_id:
1511 if user_id:
1510 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1512 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1511 if cache:
1513 if cache:
1512 user_ips = user_ips.options(
1514 user_ips = user_ips.options(
1513 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1515 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1514
1516
1515 for ip in user_ips:
1517 for ip in user_ips:
1516 try:
1518 try:
1517 _set.add(ip.ip_addr)
1519 _set.add(ip.ip_addr)
1518 except ObjectDeletedError:
1520 except ObjectDeletedError:
1519 # since we use heavy caching sometimes it happens that we get
1521 # since we use heavy caching sometimes it happens that we get
1520 # deleted objects here, we just skip them
1522 # deleted objects here, we just skip them
1521 pass
1523 pass
1522 return _set or {ip for ip in ['0.0.0.0/0', '::/0']}
1524 return _set or {ip for ip in ['0.0.0.0/0', '::/0']}
1523
1525
1524
1526
1525 def set_available_permissions(settings):
1527 def set_available_permissions(settings):
1526 """
1528 """
1527 This function will propagate pyramid settings with all available defined
1529 This function will propagate pyramid settings with all available defined
1528 permission given in db. We don't want to check each time from db for new
1530 permission given in db. We don't want to check each time from db for new
1529 permissions since adding a new permission also requires application restart
1531 permissions since adding a new permission also requires application restart
1530 ie. to decorate new views with the newly created permission
1532 ie. to decorate new views with the newly created permission
1531
1533
1532 :param settings: current pyramid registry.settings
1534 :param settings: current pyramid registry.settings
1533
1535
1534 """
1536 """
1535 log.debug('auth: getting information about all available permissions')
1537 log.debug('auth: getting information about all available permissions')
1536 try:
1538 try:
1537 sa = meta.Session
1539 sa = meta.Session
1538 all_perms = sa.query(Permission).all()
1540 all_perms = sa.query(Permission).all()
1539 settings.setdefault('available_permissions',
1541 settings.setdefault('available_permissions',
1540 [x.permission_name for x in all_perms])
1542 [x.permission_name for x in all_perms])
1541 log.debug('auth: set available permissions')
1543 log.debug('auth: set available permissions')
1542 except Exception:
1544 except Exception:
1543 log.exception('Failed to fetch permissions from the database.')
1545 log.exception('Failed to fetch permissions from the database.')
1544 raise
1546 raise
1545
1547
1546
1548
1547 def get_csrf_token(session, force_new=False, save_if_missing=True):
1549 def get_csrf_token(session, force_new=False, save_if_missing=True):
1548 """
1550 """
1549 Return the current authentication token, creating one if one doesn't
1551 Return the current authentication token, creating one if one doesn't
1550 already exist and the save_if_missing flag is present.
1552 already exist and the save_if_missing flag is present.
1551
1553
1552 :param session: pass in the pyramid session, else we use the global ones
1554 :param session: pass in the pyramid session, else we use the global ones
1553 :param force_new: force to re-generate the token and store it in session
1555 :param force_new: force to re-generate the token and store it in session
1554 :param save_if_missing: save the newly generated token if it's missing in
1556 :param save_if_missing: save the newly generated token if it's missing in
1555 session
1557 session
1556 """
1558 """
1557 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1559 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1558 # from pyramid.csrf import get_csrf_token
1560 # from pyramid.csrf import get_csrf_token
1559
1561
1560 if (csrf_token_key not in session and save_if_missing) or force_new:
1562 if (csrf_token_key not in session and save_if_missing) or force_new:
1561 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1563 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1562 session[csrf_token_key] = token
1564 session[csrf_token_key] = token
1563 if hasattr(session, 'save'):
1565 if hasattr(session, 'save'):
1564 session.save()
1566 session.save()
1565 return session.get(csrf_token_key)
1567 return session.get(csrf_token_key)
1566
1568
1567
1569
1568 def get_request(perm_class_instance):
1570 def get_request(perm_class_instance):
1569 from pyramid.threadlocal import get_current_request
1571 from pyramid.threadlocal import get_current_request
1570 pyramid_request = get_current_request()
1572 pyramid_request = get_current_request()
1571 return pyramid_request
1573 return pyramid_request
1572
1574
1573
1575
1574 # CHECK DECORATORS
1576 # CHECK DECORATORS
1575 class CSRFRequired(object):
1577 class CSRFRequired(object):
1576 """
1578 """
1577 Decorator for authenticating a form
1579 Decorator for authenticating a form
1578
1580
1579 This decorator uses an authorization token stored in the client's
1581 This decorator uses an authorization token stored in the client's
1580 session for prevention of certain Cross-site request forgery (CSRF)
1582 session for prevention of certain Cross-site request forgery (CSRF)
1581 attacks (See
1583 attacks (See
1582 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1584 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1583 information).
1585 information).
1584
1586
1585 For use with the ``secure_form`` helper functions.
1587 For use with the ``secure_form`` helper functions.
1586
1588
1587 """
1589 """
1588 def __init__(self, token=csrf_token_key, header='X-CSRF-Token', except_methods=None):
1590 def __init__(self, token=csrf_token_key, header='X-CSRF-Token', except_methods=None):
1589 self.token = token
1591 self.token = token
1590 self.header = header
1592 self.header = header
1591 self.except_methods = except_methods or []
1593 self.except_methods = except_methods or []
1592
1594
1593 def __call__(self, func):
1595 def __call__(self, func):
1594 return get_cython_compat_decorator(self.__wrapper, func)
1596 return get_cython_compat_decorator(self.__wrapper, func)
1595
1597
1596 def _get_csrf(self, _request):
1598 def _get_csrf(self, _request):
1597 return _request.POST.get(self.token, _request.headers.get(self.header))
1599 return _request.POST.get(self.token, _request.headers.get(self.header))
1598
1600
1599 def check_csrf(self, _request, cur_token):
1601 def check_csrf(self, _request, cur_token):
1600 supplied_token = self._get_csrf(_request)
1602 supplied_token = self._get_csrf(_request)
1601 return supplied_token and supplied_token == cur_token
1603 return supplied_token and supplied_token == cur_token
1602
1604
1603 def _get_request(self):
1605 def _get_request(self):
1604 return get_request(self)
1606 return get_request(self)
1605
1607
1606 def __wrapper(self, func, *fargs, **fkwargs):
1608 def __wrapper(self, func, *fargs, **fkwargs):
1607 request = self._get_request()
1609 request = self._get_request()
1608
1610
1609 if request.method in self.except_methods:
1611 if request.method in self.except_methods:
1610 return func(*fargs, **fkwargs)
1612 return func(*fargs, **fkwargs)
1611
1613
1612 cur_token = get_csrf_token(request.session, save_if_missing=False)
1614 cur_token = get_csrf_token(request.session, save_if_missing=False)
1613 if self.check_csrf(request, cur_token):
1615 if self.check_csrf(request, cur_token):
1614 if request.POST.get(self.token):
1616 if request.POST.get(self.token):
1615 del request.POST[self.token]
1617 del request.POST[self.token]
1616 return func(*fargs, **fkwargs)
1618 return func(*fargs, **fkwargs)
1617 else:
1619 else:
1618 reason = 'token-missing'
1620 reason = 'token-missing'
1619 supplied_token = self._get_csrf(request)
1621 supplied_token = self._get_csrf(request)
1620 if supplied_token and cur_token != supplied_token:
1622 if supplied_token and cur_token != supplied_token:
1621 reason = 'token-mismatch [%s:%s]' % (
1623 reason = 'token-mismatch [%s:%s]' % (
1622 cur_token or ''[:6], supplied_token or ''[:6])
1624 cur_token or ''[:6], supplied_token or ''[:6])
1623
1625
1624 csrf_message = \
1626 csrf_message = \
1625 ("Cross-site request forgery detected, request denied. See "
1627 ("Cross-site request forgery detected, request denied. See "
1626 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1628 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1627 "more information.")
1629 "more information.")
1628 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1630 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1629 'REMOTE_ADDR:%s, HEADERS:%s' % (
1631 'REMOTE_ADDR:%s, HEADERS:%s' % (
1630 request, reason, request.remote_addr, request.headers))
1632 request, reason, request.remote_addr, request.headers))
1631
1633
1632 raise HTTPForbidden(explanation=csrf_message)
1634 raise HTTPForbidden(explanation=csrf_message)
1633
1635
1634
1636
1635 class LoginRequired(object):
1637 class LoginRequired(object):
1636 """
1638 """
1637 Must be logged in to execute this function else
1639 Must be logged in to execute this function else
1638 redirect to login page
1640 redirect to login page
1639
1641
1640 :param api_access: if enabled this checks only for valid auth token
1642 :param api_access: if enabled this checks only for valid auth token
1641 and grants access based on valid token
1643 and grants access based on valid token
1642 """
1644 """
1643 def __init__(self, auth_token_access=None):
1645 def __init__(self, auth_token_access=None):
1644 self.auth_token_access = auth_token_access
1646 self.auth_token_access = auth_token_access
1645 if self.auth_token_access:
1647 if self.auth_token_access:
1646 valid_type = set(auth_token_access).intersection(set(UserApiKeys.ROLES))
1648 valid_type = set(auth_token_access).intersection(set(UserApiKeys.ROLES))
1647 if not valid_type:
1649 if not valid_type:
1648 raise ValueError('auth_token_access must be on of {}, got {}'.format(
1650 raise ValueError('auth_token_access must be on of {}, got {}'.format(
1649 UserApiKeys.ROLES, auth_token_access))
1651 UserApiKeys.ROLES, auth_token_access))
1650
1652
1651 def __call__(self, func):
1653 def __call__(self, func):
1652 return get_cython_compat_decorator(self.__wrapper, func)
1654 return get_cython_compat_decorator(self.__wrapper, func)
1653
1655
1654 def _get_request(self):
1656 def _get_request(self):
1655 return get_request(self)
1657 return get_request(self)
1656
1658
1657 def __wrapper(self, func, *fargs, **fkwargs):
1659 def __wrapper(self, func, *fargs, **fkwargs):
1658 from rhodecode.lib import helpers as h
1660 from rhodecode.lib import helpers as h
1659 cls = fargs[0]
1661 cls = fargs[0]
1660 user = cls._rhodecode_user
1662 user = cls._rhodecode_user
1661 request = self._get_request()
1663 request = self._get_request()
1662 _ = request.translate
1664 _ = request.translate
1663
1665
1664 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1666 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1665 log.debug('Starting login restriction checks for user: %s', user)
1667 log.debug('Starting login restriction checks for user: %s', user)
1666 # check if our IP is allowed
1668 # check if our IP is allowed
1667 ip_access_valid = True
1669 ip_access_valid = True
1668 if not user.ip_allowed:
1670 if not user.ip_allowed:
1669 h.flash(h.literal(_('IP {} not allowed'.format(user.ip_addr))),
1671 h.flash(h.literal(_('IP {} not allowed'.format(user.ip_addr))),
1670 category='warning')
1672 category='warning')
1671 ip_access_valid = False
1673 ip_access_valid = False
1672
1674
1673 # we used stored token that is extract from GET or URL param (if any)
1675 # we used stored token that is extract from GET or URL param (if any)
1674 _auth_token = request.user_auth_token
1676 _auth_token = request.user_auth_token
1675
1677
1676 # check if we used an AUTH_TOKEN and it's a valid one
1678 # check if we used an AUTH_TOKEN and it's a valid one
1677 # defined white-list of controllers which API access will be enabled
1679 # defined white-list of controllers which API access will be enabled
1678 whitelist = None
1680 whitelist = None
1679 if self.auth_token_access:
1681 if self.auth_token_access:
1680 # since this location is allowed by @LoginRequired decorator it's our
1682 # since this location is allowed by @LoginRequired decorator it's our
1681 # only whitelist
1683 # only whitelist
1682 whitelist = [loc]
1684 whitelist = [loc]
1683 auth_token_access_valid = allowed_auth_token_access(
1685 auth_token_access_valid = allowed_auth_token_access(
1684 loc, whitelist=whitelist, auth_token=_auth_token)
1686 loc, whitelist=whitelist, auth_token=_auth_token)
1685
1687
1686 # explicit controller is enabled or API is in our whitelist
1688 # explicit controller is enabled or API is in our whitelist
1687 if auth_token_access_valid:
1689 if auth_token_access_valid:
1688 log.debug('Checking AUTH TOKEN access for %s', cls)
1690 log.debug('Checking AUTH TOKEN access for %s', cls)
1689 db_user = user.get_instance()
1691 db_user = user.get_instance()
1690
1692
1691 if db_user:
1693 if db_user:
1692 if self.auth_token_access:
1694 if self.auth_token_access:
1693 roles = self.auth_token_access
1695 roles = self.auth_token_access
1694 else:
1696 else:
1695 roles = [UserApiKeys.ROLE_HTTP]
1697 roles = [UserApiKeys.ROLE_HTTP]
1696 log.debug('AUTH TOKEN: checking auth for user %s and roles %s',
1698 log.debug('AUTH TOKEN: checking auth for user %s and roles %s',
1697 db_user, roles)
1699 db_user, roles)
1698 token_match = db_user.authenticate_by_token(
1700 token_match = db_user.authenticate_by_token(
1699 _auth_token, roles=roles)
1701 _auth_token, roles=roles)
1700 else:
1702 else:
1701 log.debug('Unable to fetch db instance for auth user: %s', user)
1703 log.debug('Unable to fetch db instance for auth user: %s', user)
1702 token_match = False
1704 token_match = False
1703
1705
1704 if _auth_token and token_match:
1706 if _auth_token and token_match:
1705 auth_token_access_valid = True
1707 auth_token_access_valid = True
1706 log.debug('AUTH TOKEN ****%s is VALID', _auth_token[-4:])
1708 log.debug('AUTH TOKEN ****%s is VALID', _auth_token[-4:])
1707 else:
1709 else:
1708 auth_token_access_valid = False
1710 auth_token_access_valid = False
1709 if not _auth_token:
1711 if not _auth_token:
1710 log.debug("AUTH TOKEN *NOT* present in request")
1712 log.debug("AUTH TOKEN *NOT* present in request")
1711 else:
1713 else:
1712 log.warning("AUTH TOKEN ****%s *NOT* valid", _auth_token[-4:])
1714 log.warning("AUTH TOKEN ****%s *NOT* valid", _auth_token[-4:])
1713
1715
1714 log.debug('Checking if %s is authenticated @ %s', user.username, loc)
1716 log.debug('Checking if %s is authenticated @ %s', user.username, loc)
1715 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1717 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1716 else 'AUTH_TOKEN_AUTH'
1718 else 'AUTH_TOKEN_AUTH'
1717
1719
1718 if ip_access_valid and (
1720 if ip_access_valid and (
1719 user.is_authenticated or auth_token_access_valid):
1721 user.is_authenticated or auth_token_access_valid):
1720 log.info('user %s authenticating with:%s IS authenticated on func %s',
1722 log.info('user %s authenticating with:%s IS authenticated on func %s',
1721 user, reason, loc)
1723 user, reason, loc)
1722
1724
1723 return func(*fargs, **fkwargs)
1725 return func(*fargs, **fkwargs)
1724 else:
1726 else:
1725 log.warning(
1727 log.warning(
1726 'user %s authenticating with:%s NOT authenticated on '
1728 'user %s authenticating with:%s NOT authenticated on '
1727 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s',
1729 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s',
1728 user, reason, loc, ip_access_valid, auth_token_access_valid)
1730 user, reason, loc, ip_access_valid, auth_token_access_valid)
1729 # we preserve the get PARAM
1731 # we preserve the get PARAM
1730 came_from = get_came_from(request)
1732 came_from = get_came_from(request)
1731
1733
1732 log.debug('redirecting to login page with %s', came_from)
1734 log.debug('redirecting to login page with %s', came_from)
1733 raise HTTPFound(
1735 raise HTTPFound(
1734 h.route_path('login', _query={'came_from': came_from}))
1736 h.route_path('login', _query={'came_from': came_from}))
1735
1737
1736
1738
1737 class NotAnonymous(object):
1739 class NotAnonymous(object):
1738 """
1740 """
1739 Must be logged in to execute this function else
1741 Must be logged in to execute this function else
1740 redirect to login page
1742 redirect to login page
1741 """
1743 """
1742
1744
1743 def __call__(self, func):
1745 def __call__(self, func):
1744 return get_cython_compat_decorator(self.__wrapper, func)
1746 return get_cython_compat_decorator(self.__wrapper, func)
1745
1747
1746 def _get_request(self):
1748 def _get_request(self):
1747 return get_request(self)
1749 return get_request(self)
1748
1750
1749 def __wrapper(self, func, *fargs, **fkwargs):
1751 def __wrapper(self, func, *fargs, **fkwargs):
1750 import rhodecode.lib.helpers as h
1752 import rhodecode.lib.helpers as h
1751 cls = fargs[0]
1753 cls = fargs[0]
1752 self.user = cls._rhodecode_user
1754 self.user = cls._rhodecode_user
1753 request = self._get_request()
1755 request = self._get_request()
1754 _ = request.translate
1756 _ = request.translate
1755 log.debug('Checking if user is not anonymous @%s', cls)
1757 log.debug('Checking if user is not anonymous @%s', cls)
1756
1758
1757 anonymous = self.user.username == User.DEFAULT_USER
1759 anonymous = self.user.username == User.DEFAULT_USER
1758
1760
1759 if anonymous:
1761 if anonymous:
1760 came_from = get_came_from(request)
1762 came_from = get_came_from(request)
1761 h.flash(_('You need to be a registered user to '
1763 h.flash(_('You need to be a registered user to '
1762 'perform this action'),
1764 'perform this action'),
1763 category='warning')
1765 category='warning')
1764 raise HTTPFound(
1766 raise HTTPFound(
1765 h.route_path('login', _query={'came_from': came_from}))
1767 h.route_path('login', _query={'came_from': came_from}))
1766 else:
1768 else:
1767 return func(*fargs, **fkwargs)
1769 return func(*fargs, **fkwargs)
1768
1770
1769
1771
1770 class PermsDecorator(object):
1772 class PermsDecorator(object):
1771 """
1773 """
1772 Base class for controller decorators, we extract the current user from
1774 Base class for controller decorators, we extract the current user from
1773 the class itself, which has it stored in base controllers
1775 the class itself, which has it stored in base controllers
1774 """
1776 """
1775
1777
1776 def __init__(self, *required_perms):
1778 def __init__(self, *required_perms):
1777 self.required_perms = set(required_perms)
1779 self.required_perms = set(required_perms)
1778
1780
1779 def __call__(self, func):
1781 def __call__(self, func):
1780 return get_cython_compat_decorator(self.__wrapper, func)
1782 return get_cython_compat_decorator(self.__wrapper, func)
1781
1783
1782 def _get_request(self):
1784 def _get_request(self):
1783 return get_request(self)
1785 return get_request(self)
1784
1786
1785 def __wrapper(self, func, *fargs, **fkwargs):
1787 def __wrapper(self, func, *fargs, **fkwargs):
1786 import rhodecode.lib.helpers as h
1788 import rhodecode.lib.helpers as h
1787 cls = fargs[0]
1789 cls = fargs[0]
1788 _user = cls._rhodecode_user
1790 _user = cls._rhodecode_user
1789 request = self._get_request()
1791 request = self._get_request()
1790 _ = request.translate
1792 _ = request.translate
1791
1793
1792 log.debug('checking %s permissions %s for %s %s',
1794 log.debug('checking %s permissions %s for %s %s',
1793 self.__class__.__name__, self.required_perms, cls, _user)
1795 self.__class__.__name__, self.required_perms, cls, _user)
1794
1796
1795 if self.check_permissions(_user):
1797 if self.check_permissions(_user):
1796 log.debug('Permission granted for %s %s', cls, _user)
1798 log.debug('Permission granted for %s %s', cls, _user)
1797 return func(*fargs, **fkwargs)
1799 return func(*fargs, **fkwargs)
1798
1800
1799 else:
1801 else:
1800 log.debug('Permission denied for %s %s', cls, _user)
1802 log.debug('Permission denied for %s %s', cls, _user)
1801 anonymous = _user.username == User.DEFAULT_USER
1803 anonymous = _user.username == User.DEFAULT_USER
1802
1804
1803 if anonymous:
1805 if anonymous:
1804 came_from = get_came_from(self._get_request())
1806 came_from = get_came_from(self._get_request())
1805 h.flash(_('You need to be signed in to view this page'),
1807 h.flash(_('You need to be signed in to view this page'),
1806 category='warning')
1808 category='warning')
1807 raise HTTPFound(
1809 raise HTTPFound(
1808 h.route_path('login', _query={'came_from': came_from}))
1810 h.route_path('login', _query={'came_from': came_from}))
1809
1811
1810 else:
1812 else:
1811 # redirect with 404 to prevent resource discovery
1813 # redirect with 404 to prevent resource discovery
1812 raise HTTPNotFound()
1814 raise HTTPNotFound()
1813
1815
1814 def check_permissions(self, user):
1816 def check_permissions(self, user):
1815 """Dummy function for overriding"""
1817 """Dummy function for overriding"""
1816 raise NotImplementedError(
1818 raise NotImplementedError(
1817 'You have to write this function in child class')
1819 'You have to write this function in child class')
1818
1820
1819
1821
1820 class HasPermissionAllDecorator(PermsDecorator):
1822 class HasPermissionAllDecorator(PermsDecorator):
1821 """
1823 """
1822 Checks for access permission for all given predicates. All of them
1824 Checks for access permission for all given predicates. All of them
1823 have to be meet in order to fulfill the request
1825 have to be meet in order to fulfill the request
1824 """
1826 """
1825
1827
1826 def check_permissions(self, user):
1828 def check_permissions(self, user):
1827 perms = user.permissions_with_scope({})
1829 perms = user.permissions_with_scope({})
1828 if self.required_perms.issubset(perms['global']):
1830 if self.required_perms.issubset(perms['global']):
1829 return True
1831 return True
1830 return False
1832 return False
1831
1833
1832
1834
1833 class HasPermissionAnyDecorator(PermsDecorator):
1835 class HasPermissionAnyDecorator(PermsDecorator):
1834 """
1836 """
1835 Checks for access permission for any of given predicates. In order to
1837 Checks for access permission for any of given predicates. In order to
1836 fulfill the request any of predicates must be meet
1838 fulfill the request any of predicates must be meet
1837 """
1839 """
1838
1840
1839 def check_permissions(self, user):
1841 def check_permissions(self, user):
1840 perms = user.permissions_with_scope({})
1842 perms = user.permissions_with_scope({})
1841 if self.required_perms.intersection(perms['global']):
1843 if self.required_perms.intersection(perms['global']):
1842 return True
1844 return True
1843 return False
1845 return False
1844
1846
1845
1847
1846 class HasRepoPermissionAllDecorator(PermsDecorator):
1848 class HasRepoPermissionAllDecorator(PermsDecorator):
1847 """
1849 """
1848 Checks for access permission for all given predicates for specific
1850 Checks for access permission for all given predicates for specific
1849 repository. All of them have to be meet in order to fulfill the request
1851 repository. All of them have to be meet in order to fulfill the request
1850 """
1852 """
1851 def _get_repo_name(self):
1853 def _get_repo_name(self):
1852 _request = self._get_request()
1854 _request = self._get_request()
1853 return get_repo_slug(_request)
1855 return get_repo_slug(_request)
1854
1856
1855 def check_permissions(self, user):
1857 def check_permissions(self, user):
1856 perms = user.permissions
1858 perms = user.permissions
1857 repo_name = self._get_repo_name()
1859 repo_name = self._get_repo_name()
1858
1860
1859 try:
1861 try:
1860 user_perms = {perms['repositories'][repo_name]}
1862 user_perms = {perms['repositories'][repo_name]}
1861 except KeyError:
1863 except KeyError:
1862 log.debug('cannot locate repo with name: `%s` in permissions defs',
1864 log.debug('cannot locate repo with name: `%s` in permissions defs',
1863 repo_name)
1865 repo_name)
1864 return False
1866 return False
1865
1867
1866 log.debug('checking `%s` permissions for repo `%s`',
1868 log.debug('checking `%s` permissions for repo `%s`',
1867 user_perms, repo_name)
1869 user_perms, repo_name)
1868 if self.required_perms.issubset(user_perms):
1870 if self.required_perms.issubset(user_perms):
1869 return True
1871 return True
1870 return False
1872 return False
1871
1873
1872
1874
1873 class HasRepoPermissionAnyDecorator(PermsDecorator):
1875 class HasRepoPermissionAnyDecorator(PermsDecorator):
1874 """
1876 """
1875 Checks for access permission for any of given predicates for specific
1877 Checks for access permission for any of given predicates for specific
1876 repository. In order to fulfill the request any of predicates must be meet
1878 repository. In order to fulfill the request any of predicates must be meet
1877 """
1879 """
1878 def _get_repo_name(self):
1880 def _get_repo_name(self):
1879 _request = self._get_request()
1881 _request = self._get_request()
1880 return get_repo_slug(_request)
1882 return get_repo_slug(_request)
1881
1883
1882 def check_permissions(self, user):
1884 def check_permissions(self, user):
1883 perms = user.permissions
1885 perms = user.permissions
1884 repo_name = self._get_repo_name()
1886 repo_name = self._get_repo_name()
1885
1887
1886 try:
1888 try:
1887 user_perms = {perms['repositories'][repo_name]}
1889 user_perms = {perms['repositories'][repo_name]}
1888 except KeyError:
1890 except KeyError:
1889 log.debug(
1891 log.debug(
1890 'cannot locate repo with name: `%s` in permissions defs',
1892 'cannot locate repo with name: `%s` in permissions defs',
1891 repo_name)
1893 repo_name)
1892 return False
1894 return False
1893
1895
1894 log.debug('checking `%s` permissions for repo `%s`',
1896 log.debug('checking `%s` permissions for repo `%s`',
1895 user_perms, repo_name)
1897 user_perms, repo_name)
1896 if self.required_perms.intersection(user_perms):
1898 if self.required_perms.intersection(user_perms):
1897 return True
1899 return True
1898 return False
1900 return False
1899
1901
1900
1902
1901 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1903 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1902 """
1904 """
1903 Checks for access permission for all given predicates for specific
1905 Checks for access permission for all given predicates for specific
1904 repository group. All of them have to be meet in order to
1906 repository group. All of them have to be meet in order to
1905 fulfill the request
1907 fulfill the request
1906 """
1908 """
1907 def _get_repo_group_name(self):
1909 def _get_repo_group_name(self):
1908 _request = self._get_request()
1910 _request = self._get_request()
1909 return get_repo_group_slug(_request)
1911 return get_repo_group_slug(_request)
1910
1912
1911 def check_permissions(self, user):
1913 def check_permissions(self, user):
1912 perms = user.permissions
1914 perms = user.permissions
1913 group_name = self._get_repo_group_name()
1915 group_name = self._get_repo_group_name()
1914 try:
1916 try:
1915 user_perms = {perms['repositories_groups'][group_name]}
1917 user_perms = {perms['repositories_groups'][group_name]}
1916 except KeyError:
1918 except KeyError:
1917 log.debug(
1919 log.debug(
1918 'cannot locate repo group with name: `%s` in permissions defs',
1920 'cannot locate repo group with name: `%s` in permissions defs',
1919 group_name)
1921 group_name)
1920 return False
1922 return False
1921
1923
1922 log.debug('checking `%s` permissions for repo group `%s`',
1924 log.debug('checking `%s` permissions for repo group `%s`',
1923 user_perms, group_name)
1925 user_perms, group_name)
1924 if self.required_perms.issubset(user_perms):
1926 if self.required_perms.issubset(user_perms):
1925 return True
1927 return True
1926 return False
1928 return False
1927
1929
1928
1930
1929 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
1931 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
1930 """
1932 """
1931 Checks for access permission for any of given predicates for specific
1933 Checks for access permission for any of given predicates for specific
1932 repository group. In order to fulfill the request any
1934 repository group. In order to fulfill the request any
1933 of predicates must be met
1935 of predicates must be met
1934 """
1936 """
1935 def _get_repo_group_name(self):
1937 def _get_repo_group_name(self):
1936 _request = self._get_request()
1938 _request = self._get_request()
1937 return get_repo_group_slug(_request)
1939 return get_repo_group_slug(_request)
1938
1940
1939 def check_permissions(self, user):
1941 def check_permissions(self, user):
1940 perms = user.permissions
1942 perms = user.permissions
1941 group_name = self._get_repo_group_name()
1943 group_name = self._get_repo_group_name()
1942
1944
1943 try:
1945 try:
1944 user_perms = {perms['repositories_groups'][group_name]}
1946 user_perms = {perms['repositories_groups'][group_name]}
1945 except KeyError:
1947 except KeyError:
1946 log.debug(
1948 log.debug(
1947 'cannot locate repo group with name: `%s` in permissions defs',
1949 'cannot locate repo group with name: `%s` in permissions defs',
1948 group_name)
1950 group_name)
1949 return False
1951 return False
1950
1952
1951 log.debug('checking `%s` permissions for repo group `%s`',
1953 log.debug('checking `%s` permissions for repo group `%s`',
1952 user_perms, group_name)
1954 user_perms, group_name)
1953 if self.required_perms.intersection(user_perms):
1955 if self.required_perms.intersection(user_perms):
1954 return True
1956 return True
1955 return False
1957 return False
1956
1958
1957
1959
1958 class HasUserGroupPermissionAllDecorator(PermsDecorator):
1960 class HasUserGroupPermissionAllDecorator(PermsDecorator):
1959 """
1961 """
1960 Checks for access permission for all given predicates for specific
1962 Checks for access permission for all given predicates for specific
1961 user group. All of them have to be meet in order to fulfill the request
1963 user group. All of them have to be meet in order to fulfill the request
1962 """
1964 """
1963 def _get_user_group_name(self):
1965 def _get_user_group_name(self):
1964 _request = self._get_request()
1966 _request = self._get_request()
1965 return get_user_group_slug(_request)
1967 return get_user_group_slug(_request)
1966
1968
1967 def check_permissions(self, user):
1969 def check_permissions(self, user):
1968 perms = user.permissions
1970 perms = user.permissions
1969 group_name = self._get_user_group_name()
1971 group_name = self._get_user_group_name()
1970 try:
1972 try:
1971 user_perms = {perms['user_groups'][group_name]}
1973 user_perms = {perms['user_groups'][group_name]}
1972 except KeyError:
1974 except KeyError:
1973 return False
1975 return False
1974
1976
1975 if self.required_perms.issubset(user_perms):
1977 if self.required_perms.issubset(user_perms):
1976 return True
1978 return True
1977 return False
1979 return False
1978
1980
1979
1981
1980 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
1982 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
1981 """
1983 """
1982 Checks for access permission for any of given predicates for specific
1984 Checks for access permission for any of given predicates for specific
1983 user group. In order to fulfill the request any of predicates must be meet
1985 user group. In order to fulfill the request any of predicates must be meet
1984 """
1986 """
1985 def _get_user_group_name(self):
1987 def _get_user_group_name(self):
1986 _request = self._get_request()
1988 _request = self._get_request()
1987 return get_user_group_slug(_request)
1989 return get_user_group_slug(_request)
1988
1990
1989 def check_permissions(self, user):
1991 def check_permissions(self, user):
1990 perms = user.permissions
1992 perms = user.permissions
1991 group_name = self._get_user_group_name()
1993 group_name = self._get_user_group_name()
1992 try:
1994 try:
1993 user_perms = {perms['user_groups'][group_name]}
1995 user_perms = {perms['user_groups'][group_name]}
1994 except KeyError:
1996 except KeyError:
1995 return False
1997 return False
1996
1998
1997 if self.required_perms.intersection(user_perms):
1999 if self.required_perms.intersection(user_perms):
1998 return True
2000 return True
1999 return False
2001 return False
2000
2002
2001
2003
2002 # CHECK FUNCTIONS
2004 # CHECK FUNCTIONS
2003 class PermsFunction(object):
2005 class PermsFunction(object):
2004 """Base function for other check functions"""
2006 """Base function for other check functions"""
2005
2007
2006 def __init__(self, *perms):
2008 def __init__(self, *perms):
2007 self.required_perms = set(perms)
2009 self.required_perms = set(perms)
2008 self.repo_name = None
2010 self.repo_name = None
2009 self.repo_group_name = None
2011 self.repo_group_name = None
2010 self.user_group_name = None
2012 self.user_group_name = None
2011
2013
2012 def __bool__(self):
2014 def __bool__(self):
2013 frame = inspect.currentframe()
2015 frame = inspect.currentframe()
2014 stack_trace = traceback.format_stack(frame)
2016 stack_trace = traceback.format_stack(frame)
2015 log.error('Checking bool value on a class instance of perm '
2017 log.error('Checking bool value on a class instance of perm '
2016 'function is not allowed: %s', ''.join(stack_trace))
2018 'function is not allowed: %s', ''.join(stack_trace))
2017 # rather than throwing errors, here we always return False so if by
2019 # rather than throwing errors, here we always return False so if by
2018 # accident someone checks truth for just an instance it will always end
2020 # accident someone checks truth for just an instance it will always end
2019 # up in returning False
2021 # up in returning False
2020 return False
2022 return False
2021 __nonzero__ = __bool__
2023 __nonzero__ = __bool__
2022
2024
2023 def __call__(self, check_location='', user=None):
2025 def __call__(self, check_location='', user=None):
2024 if not user:
2026 if not user:
2025 log.debug('Using user attribute from global request')
2027 log.debug('Using user attribute from global request')
2026 request = self._get_request()
2028 request = self._get_request()
2027 user = request.user
2029 user = request.user
2028
2030
2029 # init auth user if not already given
2031 # init auth user if not already given
2030 if not isinstance(user, AuthUser):
2032 if not isinstance(user, AuthUser):
2031 log.debug('Wrapping user %s into AuthUser', user)
2033 log.debug('Wrapping user %s into AuthUser', user)
2032 user = AuthUser(user.user_id)
2034 user = AuthUser(user.user_id)
2033
2035
2034 cls_name = self.__class__.__name__
2036 cls_name = self.__class__.__name__
2035 check_scope = self._get_check_scope(cls_name)
2037 check_scope = self._get_check_scope(cls_name)
2036 check_location = check_location or 'unspecified location'
2038 check_location = check_location or 'unspecified location'
2037
2039
2038 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
2040 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
2039 self.required_perms, user, check_scope, check_location)
2041 self.required_perms, user, check_scope, check_location)
2040 if not user:
2042 if not user:
2041 log.warning('Empty user given for permission check')
2043 log.warning('Empty user given for permission check')
2042 return False
2044 return False
2043
2045
2044 if self.check_permissions(user):
2046 if self.check_permissions(user):
2045 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2047 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2046 check_scope, user, check_location)
2048 check_scope, user, check_location)
2047 return True
2049 return True
2048
2050
2049 else:
2051 else:
2050 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2052 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2051 check_scope, user, check_location)
2053 check_scope, user, check_location)
2052 return False
2054 return False
2053
2055
2054 def _get_request(self):
2056 def _get_request(self):
2055 return get_request(self)
2057 return get_request(self)
2056
2058
2057 def _get_check_scope(self, cls_name):
2059 def _get_check_scope(self, cls_name):
2058 return {
2060 return {
2059 'HasPermissionAll': 'GLOBAL',
2061 'HasPermissionAll': 'GLOBAL',
2060 'HasPermissionAny': 'GLOBAL',
2062 'HasPermissionAny': 'GLOBAL',
2061 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
2063 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
2062 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
2064 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
2063 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
2065 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
2064 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
2066 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
2065 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
2067 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
2066 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
2068 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
2067 }.get(cls_name, '?:%s' % cls_name)
2069 }.get(cls_name, '?:%s' % cls_name)
2068
2070
2069 def check_permissions(self, user):
2071 def check_permissions(self, user):
2070 """Dummy function for overriding"""
2072 """Dummy function for overriding"""
2071 raise Exception('You have to write this function in child class')
2073 raise Exception('You have to write this function in child class')
2072
2074
2073
2075
2074 class HasPermissionAll(PermsFunction):
2076 class HasPermissionAll(PermsFunction):
2075 def check_permissions(self, user):
2077 def check_permissions(self, user):
2076 perms = user.permissions_with_scope({})
2078 perms = user.permissions_with_scope({})
2077 if self.required_perms.issubset(perms.get('global')):
2079 if self.required_perms.issubset(perms.get('global')):
2078 return True
2080 return True
2079 return False
2081 return False
2080
2082
2081
2083
2082 class HasPermissionAny(PermsFunction):
2084 class HasPermissionAny(PermsFunction):
2083 def check_permissions(self, user):
2085 def check_permissions(self, user):
2084 perms = user.permissions_with_scope({})
2086 perms = user.permissions_with_scope({})
2085 if self.required_perms.intersection(perms.get('global')):
2087 if self.required_perms.intersection(perms.get('global')):
2086 return True
2088 return True
2087 return False
2089 return False
2088
2090
2089
2091
2090 class HasRepoPermissionAll(PermsFunction):
2092 class HasRepoPermissionAll(PermsFunction):
2091 def __call__(self, repo_name=None, check_location='', user=None):
2093 def __call__(self, repo_name=None, check_location='', user=None):
2092 self.repo_name = repo_name
2094 self.repo_name = repo_name
2093 return super(HasRepoPermissionAll, self).__call__(check_location, user)
2095 return super(HasRepoPermissionAll, self).__call__(check_location, user)
2094
2096
2095 def _get_repo_name(self):
2097 def _get_repo_name(self):
2096 if not self.repo_name:
2098 if not self.repo_name:
2097 _request = self._get_request()
2099 _request = self._get_request()
2098 self.repo_name = get_repo_slug(_request)
2100 self.repo_name = get_repo_slug(_request)
2099 return self.repo_name
2101 return self.repo_name
2100
2102
2101 def check_permissions(self, user):
2103 def check_permissions(self, user):
2102 self.repo_name = self._get_repo_name()
2104 self.repo_name = self._get_repo_name()
2103 perms = user.permissions
2105 perms = user.permissions
2104 try:
2106 try:
2105 user_perms = {perms['repositories'][self.repo_name]}
2107 user_perms = {perms['repositories'][self.repo_name]}
2106 except KeyError:
2108 except KeyError:
2107 return False
2109 return False
2108 if self.required_perms.issubset(user_perms):
2110 if self.required_perms.issubset(user_perms):
2109 return True
2111 return True
2110 return False
2112 return False
2111
2113
2112
2114
2113 class HasRepoPermissionAny(PermsFunction):
2115 class HasRepoPermissionAny(PermsFunction):
2114 def __call__(self, repo_name=None, check_location='', user=None):
2116 def __call__(self, repo_name=None, check_location='', user=None):
2115 self.repo_name = repo_name
2117 self.repo_name = repo_name
2116 return super(HasRepoPermissionAny, self).__call__(check_location, user)
2118 return super(HasRepoPermissionAny, self).__call__(check_location, user)
2117
2119
2118 def _get_repo_name(self):
2120 def _get_repo_name(self):
2119 if not self.repo_name:
2121 if not self.repo_name:
2120 _request = self._get_request()
2122 _request = self._get_request()
2121 self.repo_name = get_repo_slug(_request)
2123 self.repo_name = get_repo_slug(_request)
2122 return self.repo_name
2124 return self.repo_name
2123
2125
2124 def check_permissions(self, user):
2126 def check_permissions(self, user):
2125 self.repo_name = self._get_repo_name()
2127 self.repo_name = self._get_repo_name()
2126 perms = user.permissions
2128 perms = user.permissions
2127 try:
2129 try:
2128 user_perms = {perms['repositories'][self.repo_name]}
2130 user_perms = {perms['repositories'][self.repo_name]}
2129 except KeyError:
2131 except KeyError:
2130 return False
2132 return False
2131 if self.required_perms.intersection(user_perms):
2133 if self.required_perms.intersection(user_perms):
2132 return True
2134 return True
2133 return False
2135 return False
2134
2136
2135
2137
2136 class HasRepoGroupPermissionAny(PermsFunction):
2138 class HasRepoGroupPermissionAny(PermsFunction):
2137 def __call__(self, group_name=None, check_location='', user=None):
2139 def __call__(self, group_name=None, check_location='', user=None):
2138 self.repo_group_name = group_name
2140 self.repo_group_name = group_name
2139 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
2141 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
2140
2142
2141 def check_permissions(self, user):
2143 def check_permissions(self, user):
2142 perms = user.permissions
2144 perms = user.permissions
2143 try:
2145 try:
2144 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2146 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2145 except KeyError:
2147 except KeyError:
2146 return False
2148 return False
2147 if self.required_perms.intersection(user_perms):
2149 if self.required_perms.intersection(user_perms):
2148 return True
2150 return True
2149 return False
2151 return False
2150
2152
2151
2153
2152 class HasRepoGroupPermissionAll(PermsFunction):
2154 class HasRepoGroupPermissionAll(PermsFunction):
2153 def __call__(self, group_name=None, check_location='', user=None):
2155 def __call__(self, group_name=None, check_location='', user=None):
2154 self.repo_group_name = group_name
2156 self.repo_group_name = group_name
2155 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
2157 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
2156
2158
2157 def check_permissions(self, user):
2159 def check_permissions(self, user):
2158 perms = user.permissions
2160 perms = user.permissions
2159 try:
2161 try:
2160 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2162 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2161 except KeyError:
2163 except KeyError:
2162 return False
2164 return False
2163 if self.required_perms.issubset(user_perms):
2165 if self.required_perms.issubset(user_perms):
2164 return True
2166 return True
2165 return False
2167 return False
2166
2168
2167
2169
2168 class HasUserGroupPermissionAny(PermsFunction):
2170 class HasUserGroupPermissionAny(PermsFunction):
2169 def __call__(self, user_group_name=None, check_location='', user=None):
2171 def __call__(self, user_group_name=None, check_location='', user=None):
2170 self.user_group_name = user_group_name
2172 self.user_group_name = user_group_name
2171 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
2173 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
2172
2174
2173 def check_permissions(self, user):
2175 def check_permissions(self, user):
2174 perms = user.permissions
2176 perms = user.permissions
2175 try:
2177 try:
2176 user_perms = {perms['user_groups'][self.user_group_name]}
2178 user_perms = {perms['user_groups'][self.user_group_name]}
2177 except KeyError:
2179 except KeyError:
2178 return False
2180 return False
2179 if self.required_perms.intersection(user_perms):
2181 if self.required_perms.intersection(user_perms):
2180 return True
2182 return True
2181 return False
2183 return False
2182
2184
2183
2185
2184 class HasUserGroupPermissionAll(PermsFunction):
2186 class HasUserGroupPermissionAll(PermsFunction):
2185 def __call__(self, user_group_name=None, check_location='', user=None):
2187 def __call__(self, user_group_name=None, check_location='', user=None):
2186 self.user_group_name = user_group_name
2188 self.user_group_name = user_group_name
2187 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
2189 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
2188
2190
2189 def check_permissions(self, user):
2191 def check_permissions(self, user):
2190 perms = user.permissions
2192 perms = user.permissions
2191 try:
2193 try:
2192 user_perms = {perms['user_groups'][self.user_group_name]}
2194 user_perms = {perms['user_groups'][self.user_group_name]}
2193 except KeyError:
2195 except KeyError:
2194 return False
2196 return False
2195 if self.required_perms.issubset(user_perms):
2197 if self.required_perms.issubset(user_perms):
2196 return True
2198 return True
2197 return False
2199 return False
2198
2200
2199
2201
2200 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
2202 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
2201 class HasPermissionAnyMiddleware(object):
2203 class HasPermissionAnyMiddleware(object):
2202 def __init__(self, *perms):
2204 def __init__(self, *perms):
2203 self.required_perms = set(perms)
2205 self.required_perms = set(perms)
2204
2206
2205 def __call__(self, auth_user, repo_name):
2207 def __call__(self, auth_user, repo_name):
2206 # repo_name MUST be unicode, since we handle keys in permission
2208 # repo_name MUST be unicode, since we handle keys in permission
2207 # dict by unicode
2209 # dict by unicode
2208 repo_name = safe_unicode(repo_name)
2210 repo_name = safe_unicode(repo_name)
2209 log.debug(
2211 log.debug(
2210 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2212 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2211 self.required_perms, auth_user, repo_name)
2213 self.required_perms, auth_user, repo_name)
2212
2214
2213 if self.check_permissions(auth_user, repo_name):
2215 if self.check_permissions(auth_user, repo_name):
2214 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2216 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2215 repo_name, auth_user, 'PermissionMiddleware')
2217 repo_name, auth_user, 'PermissionMiddleware')
2216 return True
2218 return True
2217
2219
2218 else:
2220 else:
2219 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2221 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2220 repo_name, auth_user, 'PermissionMiddleware')
2222 repo_name, auth_user, 'PermissionMiddleware')
2221 return False
2223 return False
2222
2224
2223 def check_permissions(self, user, repo_name):
2225 def check_permissions(self, user, repo_name):
2224 perms = user.permissions_with_scope({'repo_name': repo_name})
2226 perms = user.permissions_with_scope({'repo_name': repo_name})
2225
2227
2226 try:
2228 try:
2227 user_perms = {perms['repositories'][repo_name]}
2229 user_perms = {perms['repositories'][repo_name]}
2228 except Exception:
2230 except Exception:
2229 log.exception('Error while accessing user permissions')
2231 log.exception('Error while accessing user permissions')
2230 return False
2232 return False
2231
2233
2232 if self.required_perms.intersection(user_perms):
2234 if self.required_perms.intersection(user_perms):
2233 return True
2235 return True
2234 return False
2236 return False
2235
2237
2236
2238
2237 # SPECIAL VERSION TO HANDLE API AUTH
2239 # SPECIAL VERSION TO HANDLE API AUTH
2238 class _BaseApiPerm(object):
2240 class _BaseApiPerm(object):
2239 def __init__(self, *perms):
2241 def __init__(self, *perms):
2240 self.required_perms = set(perms)
2242 self.required_perms = set(perms)
2241
2243
2242 def __call__(self, check_location=None, user=None, repo_name=None,
2244 def __call__(self, check_location=None, user=None, repo_name=None,
2243 group_name=None, user_group_name=None):
2245 group_name=None, user_group_name=None):
2244 cls_name = self.__class__.__name__
2246 cls_name = self.__class__.__name__
2245 check_scope = 'global:%s' % (self.required_perms,)
2247 check_scope = 'global:%s' % (self.required_perms,)
2246 if repo_name:
2248 if repo_name:
2247 check_scope += ', repo_name:%s' % (repo_name,)
2249 check_scope += ', repo_name:%s' % (repo_name,)
2248
2250
2249 if group_name:
2251 if group_name:
2250 check_scope += ', repo_group_name:%s' % (group_name,)
2252 check_scope += ', repo_group_name:%s' % (group_name,)
2251
2253
2252 if user_group_name:
2254 if user_group_name:
2253 check_scope += ', user_group_name:%s' % (user_group_name,)
2255 check_scope += ', user_group_name:%s' % (user_group_name,)
2254
2256
2255 log.debug('checking cls:%s %s %s @ %s',
2257 log.debug('checking cls:%s %s %s @ %s',
2256 cls_name, self.required_perms, check_scope, check_location)
2258 cls_name, self.required_perms, check_scope, check_location)
2257 if not user:
2259 if not user:
2258 log.debug('Empty User passed into arguments')
2260 log.debug('Empty User passed into arguments')
2259 return False
2261 return False
2260
2262
2261 # process user
2263 # process user
2262 if not isinstance(user, AuthUser):
2264 if not isinstance(user, AuthUser):
2263 user = AuthUser(user.user_id)
2265 user = AuthUser(user.user_id)
2264 if not check_location:
2266 if not check_location:
2265 check_location = 'unspecified'
2267 check_location = 'unspecified'
2266 if self.check_permissions(user.permissions, repo_name, group_name,
2268 if self.check_permissions(user.permissions, repo_name, group_name,
2267 user_group_name):
2269 user_group_name):
2268 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2270 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2269 check_scope, user, check_location)
2271 check_scope, user, check_location)
2270 return True
2272 return True
2271
2273
2272 else:
2274 else:
2273 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2275 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2274 check_scope, user, check_location)
2276 check_scope, user, check_location)
2275 return False
2277 return False
2276
2278
2277 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2279 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2278 user_group_name=None):
2280 user_group_name=None):
2279 """
2281 """
2280 implement in child class should return True if permissions are ok,
2282 implement in child class should return True if permissions are ok,
2281 False otherwise
2283 False otherwise
2282
2284
2283 :param perm_defs: dict with permission definitions
2285 :param perm_defs: dict with permission definitions
2284 :param repo_name: repo name
2286 :param repo_name: repo name
2285 """
2287 """
2286 raise NotImplementedError()
2288 raise NotImplementedError()
2287
2289
2288
2290
2289 class HasPermissionAllApi(_BaseApiPerm):
2291 class HasPermissionAllApi(_BaseApiPerm):
2290 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2292 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2291 user_group_name=None):
2293 user_group_name=None):
2292 if self.required_perms.issubset(perm_defs.get('global')):
2294 if self.required_perms.issubset(perm_defs.get('global')):
2293 return True
2295 return True
2294 return False
2296 return False
2295
2297
2296
2298
2297 class HasPermissionAnyApi(_BaseApiPerm):
2299 class HasPermissionAnyApi(_BaseApiPerm):
2298 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2300 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2299 user_group_name=None):
2301 user_group_name=None):
2300 if self.required_perms.intersection(perm_defs.get('global')):
2302 if self.required_perms.intersection(perm_defs.get('global')):
2301 return True
2303 return True
2302 return False
2304 return False
2303
2305
2304
2306
2305 class HasRepoPermissionAllApi(_BaseApiPerm):
2307 class HasRepoPermissionAllApi(_BaseApiPerm):
2306 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2308 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2307 user_group_name=None):
2309 user_group_name=None):
2308 try:
2310 try:
2309 _user_perms = {perm_defs['repositories'][repo_name]}
2311 _user_perms = {perm_defs['repositories'][repo_name]}
2310 except KeyError:
2312 except KeyError:
2311 log.warning(traceback.format_exc())
2313 log.warning(traceback.format_exc())
2312 return False
2314 return False
2313 if self.required_perms.issubset(_user_perms):
2315 if self.required_perms.issubset(_user_perms):
2314 return True
2316 return True
2315 return False
2317 return False
2316
2318
2317
2319
2318 class HasRepoPermissionAnyApi(_BaseApiPerm):
2320 class HasRepoPermissionAnyApi(_BaseApiPerm):
2319 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2321 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2320 user_group_name=None):
2322 user_group_name=None):
2321 try:
2323 try:
2322 _user_perms = {perm_defs['repositories'][repo_name]}
2324 _user_perms = {perm_defs['repositories'][repo_name]}
2323 except KeyError:
2325 except KeyError:
2324 log.warning(traceback.format_exc())
2326 log.warning(traceback.format_exc())
2325 return False
2327 return False
2326 if self.required_perms.intersection(_user_perms):
2328 if self.required_perms.intersection(_user_perms):
2327 return True
2329 return True
2328 return False
2330 return False
2329
2331
2330
2332
2331 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2333 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2332 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2334 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2333 user_group_name=None):
2335 user_group_name=None):
2334 try:
2336 try:
2335 _user_perms = {perm_defs['repositories_groups'][group_name]}
2337 _user_perms = {perm_defs['repositories_groups'][group_name]}
2336 except KeyError:
2338 except KeyError:
2337 log.warning(traceback.format_exc())
2339 log.warning(traceback.format_exc())
2338 return False
2340 return False
2339 if self.required_perms.intersection(_user_perms):
2341 if self.required_perms.intersection(_user_perms):
2340 return True
2342 return True
2341 return False
2343 return False
2342
2344
2343
2345
2344 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2346 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2345 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2347 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2346 user_group_name=None):
2348 user_group_name=None):
2347 try:
2349 try:
2348 _user_perms = {perm_defs['repositories_groups'][group_name]}
2350 _user_perms = {perm_defs['repositories_groups'][group_name]}
2349 except KeyError:
2351 except KeyError:
2350 log.warning(traceback.format_exc())
2352 log.warning(traceback.format_exc())
2351 return False
2353 return False
2352 if self.required_perms.issubset(_user_perms):
2354 if self.required_perms.issubset(_user_perms):
2353 return True
2355 return True
2354 return False
2356 return False
2355
2357
2356
2358
2357 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2359 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2358 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2360 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2359 user_group_name=None):
2361 user_group_name=None):
2360 try:
2362 try:
2361 _user_perms = {perm_defs['user_groups'][user_group_name]}
2363 _user_perms = {perm_defs['user_groups'][user_group_name]}
2362 except KeyError:
2364 except KeyError:
2363 log.warning(traceback.format_exc())
2365 log.warning(traceback.format_exc())
2364 return False
2366 return False
2365 if self.required_perms.intersection(_user_perms):
2367 if self.required_perms.intersection(_user_perms):
2366 return True
2368 return True
2367 return False
2369 return False
2368
2370
2369
2371
2370 def check_ip_access(source_ip, allowed_ips=None):
2372 def check_ip_access(source_ip, allowed_ips=None):
2371 """
2373 """
2372 Checks if source_ip is a subnet of any of allowed_ips.
2374 Checks if source_ip is a subnet of any of allowed_ips.
2373
2375
2374 :param source_ip:
2376 :param source_ip:
2375 :param allowed_ips: list of allowed ips together with mask
2377 :param allowed_ips: list of allowed ips together with mask
2376 """
2378 """
2377 log.debug('checking if ip:%s is subnet of %s', source_ip, allowed_ips)
2379 log.debug('checking if ip:%s is subnet of %s', source_ip, allowed_ips)
2378 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2380 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2379 if isinstance(allowed_ips, (tuple, list, set)):
2381 if isinstance(allowed_ips, (tuple, list, set)):
2380 for ip in allowed_ips:
2382 for ip in allowed_ips:
2381 ip = safe_unicode(ip)
2383 ip = safe_unicode(ip)
2382 try:
2384 try:
2383 network_address = ipaddress.ip_network(ip, strict=False)
2385 network_address = ipaddress.ip_network(ip, strict=False)
2384 if source_ip_address in network_address:
2386 if source_ip_address in network_address:
2385 log.debug('IP %s is network %s', source_ip_address, network_address)
2387 log.debug('IP %s is network %s', source_ip_address, network_address)
2386 return True
2388 return True
2387 # for any case we cannot determine the IP, don't crash just
2389 # for any case we cannot determine the IP, don't crash just
2388 # skip it and log as error, we want to say forbidden still when
2390 # skip it and log as error, we want to say forbidden still when
2389 # sending bad IP
2391 # sending bad IP
2390 except Exception:
2392 except Exception:
2391 log.error(traceback.format_exc())
2393 log.error(traceback.format_exc())
2392 continue
2394 continue
2393 return False
2395 return False
2394
2396
2395
2397
2396 def get_cython_compat_decorator(wrapper, func):
2398 def get_cython_compat_decorator(wrapper, func):
2397 """
2399 """
2398 Creates a cython compatible decorator. The previously used
2400 Creates a cython compatible decorator. The previously used
2399 decorator.decorator() function seems to be incompatible with cython.
2401 decorator.decorator() function seems to be incompatible with cython.
2400
2402
2401 :param wrapper: __wrapper method of the decorator class
2403 :param wrapper: __wrapper method of the decorator class
2402 :param func: decorated function
2404 :param func: decorated function
2403 """
2405 """
2404 @wraps(func)
2406 @wraps(func)
2405 def local_wrapper(*args, **kwds):
2407 def local_wrapper(*args, **kwds):
2406 return wrapper(func, *args, **kwds)
2408 return wrapper(func, *args, **kwds)
2407 local_wrapper.__wrapped__ = func
2409 local_wrapper.__wrapped__ = func
2408 return local_wrapper
2410 return local_wrapper
2409
2411
2410
2412
@@ -1,57 +1,57 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('My account')} ${c.rhodecode_user.username}
5 ${_('My account')} ${c.rhodecode_user.username}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}
7 &middot; ${h.branding(c.rhodecode_name)}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${_('My Account')}
12 ${_('My Account')}
13 </%def>
13 </%def>
14
14
15 <%def name="menu_bar_nav()">
15 <%def name="menu_bar_nav()">
16 ${self.menu_items(active='my_account')}
16 ${self.menu_items(active='my_account')}
17 </%def>
17 </%def>
18
18
19 <%def name="main()">
19 <%def name="main()">
20 <div class="box">
20 <div class="box">
21 <div class="title">
21 <div class="title">
22 ${self.breadcrumbs()}
22 ${self.breadcrumbs()}
23 </div>
23 </div>
24
24
25 <div class="sidebar-col-wrapper scw-small">
25 <div class="sidebar-col-wrapper scw-small">
26 ##main
26 ##main
27 <div class="sidebar">
27 <div class="sidebar">
28 <ul class="nav nav-pills nav-stacked">
28 <ul class="nav nav-pills nav-stacked">
29 <li class="${h.is_active(['profile', 'profile_edit'], c.active)}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
29 <li class="${h.is_active(['profile', 'profile_edit'], c.active)}"><a href="${h.route_path('my_account_profile')}">${_('Profile')}</a></li>
30 <li class="${h.is_active('emails', c.active)}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
30 <li class="${h.is_active('emails', c.active)}"><a href="${h.route_path('my_account_emails')}">${_('Emails')}</a></li>
31 <li class="${h.is_active('password', c.active)}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
31 <li class="${h.is_active('password', c.active)}"><a href="${h.route_path('my_account_password')}">${_('Password')}</a></li>
32 <li class="${h.is_active('bookmarks', c.active)}"><a href="${h.route_path('my_account_bookmarks')}">${_('Bookmarks')}</a></li>
32 <li class="${h.is_active('bookmarks', c.active)}"><a href="${h.route_path('my_account_bookmarks')}">${_('Bookmarks')}</a></li>
33 <li class="${h.is_active('auth_tokens', c.active)}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
33 <li class="${h.is_active('auth_tokens', c.active)}"><a href="${h.route_path('my_account_auth_tokens')}">${_('Auth Tokens')}</a></li>
34 <li class="${h.is_active(['ssh_keys', 'ssh_keys_generate'], c.active)}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
34 <li class="${h.is_active(['ssh_keys', 'ssh_keys_generate'], c.active)}"><a href="${h.route_path('my_account_ssh_keys')}">${_('SSH Keys')}</a></li>
35 <li class="${h.is_active('user_group_membership', c.active)}"><a href="${h.route_path('my_account_user_group_membership')}">${_('User Group Membership')}</a></li>
35 <li class="${h.is_active('user_group_membership', c.active)}"><a href="${h.route_path('my_account_user_group_membership')}">${_('User Group Membership')}</a></li>
36
36
37 ## TODO: Find a better integration of oauth/saml views into navigation.
37 ## TODO: Find a better integration of oauth/saml views into navigation.
38 <% my_account_external_url = h.route_path_or_none('my_account_external_identity') %>
38 <% my_account_external_url = h.route_path_or_none('my_account_external_identity') %>
39 % if my_account_external_url:
39 % if my_account_external_url:
40 <li class="${h.is_active('external_identity', c.active)}"><a href="${my_account_external_url}">${_('External Identities')}</a></li>
40 <li class="${h.is_active('external_identity', c.active)}"><a href="${my_account_external_url}">${_('External Identities')}</a></li>
41 % endif
41 % endif
42
42
43 <li class="${h.is_active('repos', c.active)}"><a href="${h.route_path('my_account_repos')}">${_('Repositories')}</a></li>
43 <li class="${h.is_active('repos', c.active)}"><a href="${h.route_path('my_account_repos')}">${_('Owned Repositories')}</a></li>
44 <li class="${h.is_active('watched', c.active)}"><a href="${h.route_path('my_account_watched')}">${_('Watched')}</a></li>
44 <li class="${h.is_active('watched', c.active)}"><a href="${h.route_path('my_account_watched')}">${_('Watched Repositories')}</a></li>
45 <li class="${h.is_active('pullrequests', c.active)}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
45 <li class="${h.is_active('pullrequests', c.active)}"><a href="${h.route_path('my_account_pullrequests')}">${_('Pull Requests')}</a></li>
46 <li class="${h.is_active('perms', c.active)}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
46 <li class="${h.is_active('perms', c.active)}"><a href="${h.route_path('my_account_perms')}">${_('Permissions')}</a></li>
47 <li class="${h.is_active('my_notifications', c.active)}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
47 <li class="${h.is_active('my_notifications', c.active)}"><a href="${h.route_path('my_account_notifications')}">${_('Live Notifications')}</a></li>
48 </ul>
48 </ul>
49 </div>
49 </div>
50
50
51 <div class="main-content-full-width">
51 <div class="main-content-full-width">
52 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
52 <%include file="/admin/my_account/my_account_${c.active}.mako"/>
53 </div>
53 </div>
54 </div>
54 </div>
55 </div>
55 </div>
56
56
57 </%def>
57 </%def>
@@ -1,68 +1,49 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Repositories You Own')}</h3>
3 <h3 class="panel-title">${_('Repositories You Own')}</h3>
4 </div>
4 </div>
5
5
6 <div class="panel-body">
6 <div class="panel-body">
7 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
7 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
8
8
9 <div id="repos_list_wrap">
9 <div id="repos_list_wrap">
10 <table id="repo_list_table" class="display"></table>
10 <table id="repo_list_table" class="display"></table>
11 </div>
11 </div>
12 </div>
12 </div>
13 </div>
13 </div>
14
14
15 <script>
15 <script>
16 $(document).ready(function() {
16 $(document).ready(function() {
17
17
18 var get_datatable_count = function(){
18 // repo list
19 var api = $('#repo_list_table').dataTable().api();
19 $repoListTable = $('#repo_list_table');
20 $('#repo_count').text(api.page.info().recordsDisplay);
21 };
22
20
23 // repo list
21 $repoListTable.DataTable({
24 $('#repo_list_table').DataTable({
25 data: ${c.data|n},
22 data: ${c.data|n},
26 dom: 'rtp',
23 dom: 'rtp',
27 pageLength: ${c.visual.admin_grid_items},
24 pageLength: ${c.visual.admin_grid_items},
28 order: [[ 0, "asc" ]],
25 order: [[ 0, "asc" ]],
29 columns: [
26 columns: [
30 { data: {"_": "name",
27 { data: {"_": "name",
31 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
28 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
32 { data: 'menu', className: "quick_repo_menu" },
33 { data: {"_": "last_changeset",
34 "sort": "last_changeset_raw",
35 "type": Number}, title: "${_('Commit')}", className: "td-hash" },
36 { data: {"_": "action",
37 "sort": "action"}, title: "${_('Action')}", className: "td-action" }
38 ],
29 ],
39 language: {
30 language: {
40 paginate: DEFAULT_GRID_PAGINATION,
31 paginate: DEFAULT_GRID_PAGINATION,
41 emptyTable: _gettext("No repositories available yet.")
32 emptyTable: _gettext("No repositories available yet.")
42 },
33 },
43 "initComplete": function( settings, json ) {
44 get_datatable_count();
45 quick_repo_menu();
46 }
47 });
48
34
49 // update the counter when doing search
50 $('#repo_list_table').on( 'search.dt', function (e,settings) {
51 get_datatable_count();
52 });
35 });
53
36
54 // filter, filter both grids
37 // filter
55 $('#q_filter').on( 'keyup', function () {
38 $('#q_filter').on('keyup',
56 var repo_api = $('#repo_list_table').dataTable().api();
39 $.debounce(250, function() {
57 repo_api
40 $repoListTable.DataTable().search(
58 .columns(0)
41 $('#q_filter').val()
59 .search(this.value)
42 ).draw();
60 .draw();
43 })
61 });
44 );
62
45
63 // refilter table if page load via back button
64 $("#q_filter").trigger('keyup');
65
46
66 });
47 });
67
48
68 </script>
49 </script>
@@ -1,66 +1,49 b''
1 <div class="panel panel-default">
1 <div class="panel panel-default">
2 <div class="panel-heading">
2 <div class="panel-heading">
3 <h3 class="panel-title">${_('Your Watched Repositories')}</h3>
3 <h3 class="panel-title">${_('Your Watched Repositories')}</h3>
4 </div>
4 </div>
5
5
6 <div class="panel-body">
6 <div class="panel-body">
7 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
7 <input class="q_filter_box" id="q_filter" size="15" type="text" name="filter" placeholder="${_('quick filter...')}" value=""/>
8
8
9 <div id="repos_list_wrap">
9 <div id="repos_list_wrap">
10 <table id="repo_list_table" class="display"></table>
10 <table id="repo_list_table" class="display"></table>
11 </div>
11 </div>
12 </div>
12 </div>
13 </div>
13 </div>
14
14
15 <script>
15 <script>
16 $(document).ready(function() {
16 $(document).ready(function() {
17
17
18 var get_datatable_count = function(){
18 // repo list
19 var api = $('#repo_list_table').dataTable().api();
19 $repoListTable = $('#repo_list_table');
20 $('#repo_count').text(api.page.info().recordsDisplay);
21 };
22
20
23 // repo list
21 $repoListTable.DataTable({
24 $('#repo_list_table').DataTable({
25 data: ${c.data|n},
22 data: ${c.data|n},
26 dom: 'rtp',
23 dom: 'rtp',
27 pageLength: ${c.visual.admin_grid_items},
24 pageLength: ${c.visual.admin_grid_items},
28 order: [[ 0, "asc" ]],
25 order: [[ 0, "asc" ]],
29 columns: [
26 columns: [
30 { data: {"_": "name",
27 { data: {"_": "name",
31 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
28 "sort": "name_raw"}, title: "${_('Name')}", className: "td-componentname" },
32 { data: 'menu', className: "quick_repo_menu" },
33 { data: {"_": "last_changeset",
34 "sort": "last_changeset_raw",
35 "type": Number}, title: "${_('Commit')}", className: "td-hash" }
36 ],
29 ],
37 language: {
30 language: {
38 paginate: DEFAULT_GRID_PAGINATION,
31 paginate: DEFAULT_GRID_PAGINATION,
39 emptyTable: _gettext("No repositories available yet.")
32 emptyTable: _gettext("No repositories available yet.")
40 },
33 },
41 "initComplete": function( settings, json ) {
42 get_datatable_count();
43 quick_repo_menu();
44 }
45 });
46
34
47 // update the counter when doing search
48 $('#repo_list_table').on( 'search.dt', function (e,settings) {
49 get_datatable_count();
50 });
35 });
51
36
52 // filter, filter both grids
37 // filter
53 $('#q_filter').on( 'keyup', function () {
38 $('#q_filter').on('keyup',
54 var repo_api = $('#repo_list_table').dataTable().api();
39 $.debounce(250, function() {
55 repo_api
40 $repoListTable.DataTable().search(
56 .columns(0)
41 $('#q_filter').val()
57 .search(this.value)
42 ).draw();
58 .draw();
43 })
59 });
44 );
60
45
61 // refilter table if page load via back button
62 $("#q_filter").trigger('keyup');
63
46
64 });
47 });
65
48
66 </script>
49 </script>
General Comments 0
You need to be logged in to leave comments. Login now