##// END OF EJS Templates
core: added user-notice logic to push notice messages....
ergo -
r4300:8f93504d default
parent child Browse files
Show More

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

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