##// END OF EJS Templates
Include the current user as a created_by/deleted_by attribute for USER_HOOK extensions.
Jonathan Sternberg -
r4017:509923da default
parent child Browse files
Show More
@@ -1,190 +1,192 b''
1 1 # Additional mappings that are not present in the pygments lexers
2 2 # used for building stats
3 3 # format is {'ext':['Names']} eg. {'py':['Python']} note: there can be
4 4 # more than one name for extension
5 5 # NOTE: that this will overide any mappings in LANGUAGES_EXTENSIONS_MAP
6 6 # build by pygments
7 7 EXTRA_MAPPINGS = {}
8 8
9 9 # additional lexer definitions for custom files
10 10 # it's overrides pygments lexers, and uses defined name of lexer to colorize the
11 11 # files. Format is {'ext': 'lexer_name'}
12 12 # List of lexers can be printed running:
13 13 # python -c "import pprint;from pygments import lexers;pprint.pprint([(x[0], x[1]) for x in lexers.get_all_lexers()]);"
14 14
15 15 EXTRA_LEXERS = {}
16 16
17 17 #==============================================================================
18 18 # WHOOSH INDEX EXTENSIONS
19 19 #==============================================================================
20 20 # if INDEX_EXTENSIONS is [] it'll use pygments lexers extensions by default.
21 21 # To set your own just add to this list extensions to index with content
22 22 INDEX_EXTENSIONS = []
23 23
24 24 # additional extensions for indexing besides the default from pygments
25 25 # those get's added to INDEX_EXTENSIONS
26 26 EXTRA_INDEX_EXTENSIONS = []
27 27
28 28
29 29 #==============================================================================
30 30 # POST CREATE REPOSITORY HOOK
31 31 #==============================================================================
32 32 # this function will be executed after each repository is created
33 33 def _crrepohook(*args, **kwargs):
34 34 """
35 35 Post create repository HOOK
36 36 kwargs available:
37 37 :param repo_name:
38 38 :param repo_type:
39 39 :param description:
40 40 :param private:
41 41 :param created_on:
42 42 :param enable_downloads:
43 43 :param repo_id:
44 44 :param user_id:
45 45 :param enable_statistics:
46 46 :param clone_uri:
47 47 :param fork_id:
48 48 :param group_id:
49 49 :param created_by:
50 50 """
51 51 return 0
52 52 CREATE_REPO_HOOK = _crrepohook
53 53
54 54
55 55 #==============================================================================
56 56 # POST CREATE USER HOOK
57 57 #==============================================================================
58 58 # this function will be executed after each user is created
59 59 def _cruserhook(*args, **kwargs):
60 60 """
61 61 Post create user HOOK
62 62 kwargs available:
63 63 :param username:
64 64 :param full_name_or_username:
65 65 :param full_contact:
66 66 :param user_id:
67 67 :param name:
68 68 :param firstname:
69 69 :param short_contact:
70 70 :param admin:
71 71 :param lastname:
72 72 :param ip_addresses:
73 73 :param ldap_dn:
74 74 :param email:
75 75 :param api_key:
76 76 :param last_login:
77 77 :param full_name:
78 78 :param active:
79 79 :param password:
80 80 :param emails:
81 81 :param inherit_default_permissions:
82 :param created_by:
82 83 """
83 84 return 0
84 85 CREATE_USER_HOOK = _cruserhook
85 86
86 87
87 88 #==============================================================================
88 89 # POST DELETE REPOSITORY HOOK
89 90 #==============================================================================
90 91 # this function will be executed after each repository deletion
91 92 def _dlrepohook(*args, **kwargs):
92 93 """
93 94 Post delete repository HOOK
94 95 kwargs available:
95 96 :param repo_name:
96 97 :param repo_type:
97 98 :param description:
98 99 :param private:
99 100 :param created_on:
100 101 :param enable_downloads:
101 102 :param repo_id:
102 103 :param user_id:
103 104 :param enable_statistics:
104 105 :param clone_uri:
105 106 :param fork_id:
106 107 :param group_id:
107 108 :param deleted_by:
108 109 :param deleted_on:
109 110 """
110 111 return 0
111 112 DELETE_REPO_HOOK = _dlrepohook
112 113
113 114
114 115 #==============================================================================
115 116 # POST DELETE USER HOOK
116 117 #==============================================================================
117 118 # this function will be executed after each user is deleted
118 119 def _dluserhook(*args, **kwargs):
119 120 """
120 121 Post delete user HOOK
121 122 kwargs available:
122 123 :param username:
123 124 :param full_name_or_username:
124 125 :param full_contact:
125 126 :param user_id:
126 127 :param name:
127 128 :param firstname:
128 129 :param short_contact:
129 130 :param admin:
130 131 :param lastname:
131 132 :param ip_addresses:
132 133 :param ldap_dn:
133 134 :param email:
134 135 :param api_key:
135 136 :param last_login:
136 137 :param full_name:
137 138 :param active:
138 139 :param password:
139 140 :param emails:
140 141 :param inherit_default_permissions:
142 :param deleted_by:
141 143 """
142 144 return 0
143 145 DELETE_USER_HOOK = _dluserhook
144 146
145 147
146 148 #==============================================================================
147 149 # POST PUSH HOOK
148 150 #==============================================================================
149 151
150 152 # this function will be executed after each push it's executed after the
151 153 # build-in hook that RhodeCode uses for logging pushes
152 154 def _pushhook(*args, **kwargs):
153 155 """
154 156 Post push hook
155 157 kwargs available:
156 158
157 159 :param server_url: url of instance that triggered this hook
158 160 :param config: path to .ini config used
159 161 :param scm: type of VS 'git' or 'hg'
160 162 :param username: name of user who pushed
161 163 :param ip: ip of who pushed
162 164 :param action: push
163 165 :param repository: repository name
164 166 :param pushed_revs: list of pushed revisions
165 167 """
166 168 return 0
167 169 PUSH_HOOK = _pushhook
168 170
169 171
170 172 #==============================================================================
171 173 # POST PULL HOOK
172 174 #==============================================================================
173 175
174 176 # this function will be executed after each push it's executed after the
175 177 # build-in hook that RhodeCode uses for logging pulls
176 178 def _pullhook(*args, **kwargs):
177 179 """
178 180 Post pull hook
179 181 kwargs available::
180 182
181 183 :param server_url: url of instance that triggered this hook
182 184 :param config: path to .ini config used
183 185 :param scm: type of VS 'git' or 'hg'
184 186 :param username: name of user who pulled
185 187 :param ip: ip of who pulled
186 188 :param action: pull
187 189 :param repository: repository name
188 190 """
189 191 return 0
190 192 PULL_HOOK = _pullhook
@@ -1,463 +1,463 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.hooks
4 4 ~~~~~~~~~~~~~~~~~~~
5 5
6 6 Hooks runned by rhodecode
7 7
8 8 :created_on: Aug 6, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25 import os
26 26 import sys
27 27 import time
28 28 import binascii
29 29 import traceback
30 30 from inspect import isfunction
31 31
32 32 from rhodecode.lib.vcs.utils.hgcompat import nullrev, revrange
33 33 from rhodecode.lib import helpers as h
34 34 from rhodecode.lib.utils import action_logger
35 35 from rhodecode.lib.vcs.backends.base import EmptyChangeset
36 36 from rhodecode.lib.compat import json
37 37 from rhodecode.lib.exceptions import HTTPLockedRC
38 38 from rhodecode.lib.utils2 import safe_str, _extract_extras
39 39 from rhodecode.model.db import Repository, User
40 40
41 41
42 42 def _get_scm_size(alias, root_path):
43 43
44 44 if not alias.startswith('.'):
45 45 alias += '.'
46 46
47 47 size_scm, size_root = 0, 0
48 48 for path, dirs, files in os.walk(safe_str(root_path)):
49 49 if path.find(alias) != -1:
50 50 for f in files:
51 51 try:
52 52 size_scm += os.path.getsize(os.path.join(path, f))
53 53 except OSError:
54 54 pass
55 55 else:
56 56 for f in files:
57 57 try:
58 58 size_root += os.path.getsize(os.path.join(path, f))
59 59 except OSError:
60 60 pass
61 61
62 62 size_scm_f = h.format_byte_size(size_scm)
63 63 size_root_f = h.format_byte_size(size_root)
64 64 size_total_f = h.format_byte_size(size_root + size_scm)
65 65
66 66 return size_scm_f, size_root_f, size_total_f
67 67
68 68
69 69 def repo_size(ui, repo, hooktype=None, **kwargs):
70 70 """
71 71 Presents size of repository after push
72 72
73 73 :param ui:
74 74 :param repo:
75 75 :param hooktype:
76 76 """
77 77
78 78 size_hg_f, size_root_f, size_total_f = _get_scm_size('.hg', repo.root)
79 79
80 80 last_cs = repo[len(repo) - 1]
81 81
82 82 msg = ('Repository size .hg:%s repo:%s total:%s\n'
83 83 'Last revision is now r%s:%s\n') % (
84 84 size_hg_f, size_root_f, size_total_f, last_cs.rev(), last_cs.hex()[:12]
85 85 )
86 86
87 87 sys.stdout.write(msg)
88 88
89 89
90 90 def pre_push(ui, repo, **kwargs):
91 91 # pre push function, currently used to ban pushing when
92 92 # repository is locked
93 93 ex = _extract_extras()
94 94
95 95 usr = User.get_by_username(ex.username)
96 96 if ex.locked_by[0] and usr.user_id != int(ex.locked_by[0]):
97 97 locked_by = User.get(ex.locked_by[0]).username
98 98 # this exception is interpreted in git/hg middlewares and based
99 99 # on that proper return code is server to client
100 100 _http_ret = HTTPLockedRC(ex.repository, locked_by)
101 101 if str(_http_ret.code).startswith('2'):
102 102 #2xx Codes don't raise exceptions
103 103 sys.stdout.write(_http_ret.title)
104 104 else:
105 105 raise _http_ret
106 106
107 107
108 108 def pre_pull(ui, repo, **kwargs):
109 109 # pre push function, currently used to ban pushing when
110 110 # repository is locked
111 111 ex = _extract_extras()
112 112 if ex.locked_by[0]:
113 113 locked_by = User.get(ex.locked_by[0]).username
114 114 # this exception is interpreted in git/hg middlewares and based
115 115 # on that proper return code is server to client
116 116 _http_ret = HTTPLockedRC(ex.repository, locked_by)
117 117 if str(_http_ret.code).startswith('2'):
118 118 #2xx Codes don't raise exceptions
119 119 sys.stdout.write(_http_ret.title)
120 120 else:
121 121 raise _http_ret
122 122
123 123
124 124 def log_pull_action(ui, repo, **kwargs):
125 125 """
126 126 Logs user last pull action
127 127
128 128 :param ui:
129 129 :param repo:
130 130 """
131 131 ex = _extract_extras()
132 132
133 133 user = User.get_by_username(ex.username)
134 134 action = 'pull'
135 135 action_logger(user, action, ex.repository, ex.ip, commit=True)
136 136 # extension hook call
137 137 from rhodecode import EXTENSIONS
138 138 callback = getattr(EXTENSIONS, 'PULL_HOOK', None)
139 139 if isfunction(callback):
140 140 kw = {}
141 141 kw.update(ex)
142 142 callback(**kw)
143 143
144 144 if ex.make_lock is not None and ex.make_lock:
145 145 Repository.lock(Repository.get_by_repo_name(ex.repository), user.user_id)
146 146 #msg = 'Made lock on repo `%s`' % repository
147 147 #sys.stdout.write(msg)
148 148
149 149 if ex.locked_by[0]:
150 150 locked_by = User.get(ex.locked_by[0]).username
151 151 _http_ret = HTTPLockedRC(ex.repository, locked_by)
152 152 if str(_http_ret.code).startswith('2'):
153 153 #2xx Codes don't raise exceptions
154 154 sys.stdout.write(_http_ret.title)
155 155 return 0
156 156
157 157
158 158 def log_push_action(ui, repo, **kwargs):
159 159 """
160 160 Maps user last push action to new changeset id, from mercurial
161 161
162 162 :param ui:
163 163 :param repo: repo object containing the `ui` object
164 164 """
165 165
166 166 ex = _extract_extras()
167 167
168 168 action = ex.action + ':%s'
169 169
170 170 if ex.scm == 'hg':
171 171 node = kwargs['node']
172 172
173 173 def get_revs(repo, rev_opt):
174 174 if rev_opt:
175 175 revs = revrange(repo, rev_opt)
176 176
177 177 if len(revs) == 0:
178 178 return (nullrev, nullrev)
179 179 return (max(revs), min(revs))
180 180 else:
181 181 return (len(repo) - 1, 0)
182 182
183 183 stop, start = get_revs(repo, [node + ':'])
184 184 h = binascii.hexlify
185 185 revs = [h(repo[r].node()) for r in xrange(start, stop + 1)]
186 186 elif ex.scm == 'git':
187 187 revs = kwargs.get('_git_revs', [])
188 188 if '_git_revs' in kwargs:
189 189 kwargs.pop('_git_revs')
190 190
191 191 action = action % ','.join(revs)
192 192
193 193 action_logger(ex.username, action, ex.repository, ex.ip, commit=True)
194 194
195 195 # extension hook call
196 196 from rhodecode import EXTENSIONS
197 197 callback = getattr(EXTENSIONS, 'PUSH_HOOK', None)
198 198 if isfunction(callback):
199 199 kw = {'pushed_revs': revs}
200 200 kw.update(ex)
201 201 callback(**kw)
202 202
203 203 if ex.make_lock is not None and not ex.make_lock:
204 204 Repository.unlock(Repository.get_by_repo_name(ex.repository))
205 205 msg = 'Released lock on repo `%s`\n' % ex.repository
206 206 sys.stdout.write(msg)
207 207
208 208 if ex.locked_by[0]:
209 209 locked_by = User.get(ex.locked_by[0]).username
210 210 _http_ret = HTTPLockedRC(ex.repository, locked_by)
211 211 if str(_http_ret.code).startswith('2'):
212 212 #2xx Codes don't raise exceptions
213 213 sys.stdout.write(_http_ret.title)
214 214
215 215 return 0
216 216
217 217
218 218 def log_create_repository(repository_dict, created_by, **kwargs):
219 219 """
220 220 Post create repository Hook. This is a dummy function for admins to re-use
221 221 if needed. It's taken from rhodecode-extensions module and executed
222 222 if present
223 223
224 224 :param repository: dict dump of repository object
225 225 :param created_by: username who created repository
226 226
227 227 available keys of repository_dict:
228 228
229 229 'repo_type',
230 230 'description',
231 231 'private',
232 232 'created_on',
233 233 'enable_downloads',
234 234 'repo_id',
235 235 'user_id',
236 236 'enable_statistics',
237 237 'clone_uri',
238 238 'fork_id',
239 239 'group_id',
240 240 'repo_name'
241 241
242 242 """
243 243 from rhodecode import EXTENSIONS
244 244 callback = getattr(EXTENSIONS, 'CREATE_REPO_HOOK', None)
245 245 if isfunction(callback):
246 246 kw = {}
247 247 kw.update(repository_dict)
248 248 kw.update({'created_by': created_by})
249 249 kw.update(kwargs)
250 250 return callback(**kw)
251 251
252 252 return 0
253 253
254 254
255 def log_create_user(user_dict, **kwargs):
255 def log_create_user(user_dict, created_by, **kwargs):
256 256 """
257 257 Post create user Hook. This is a dummy function for admins to re-use
258 258 if needed. It's taken from rhodecode-extensions module and executed
259 259 if present
260 260
261 261 :param user_dict: dict dump of user object
262 262
263 263 available keys for user_dict:
264 264
265 265 'username',
266 266 'full_name_or_username',
267 267 'full_contact',
268 268 'user_id',
269 269 'name',
270 270 'firstname',
271 271 'short_contact',
272 272 'admin',
273 273 'lastname',
274 274 'ip_addresses',
275 275 'ldap_dn',
276 276 'email',
277 277 'api_key',
278 278 'last_login',
279 279 'full_name',
280 280 'active',
281 281 'password',
282 282 'emails',
283 283 'inherit_default_permissions'
284 284
285 285 """
286 286 from rhodecode import EXTENSIONS
287 287 callback = getattr(EXTENSIONS, 'CREATE_USER_HOOK', None)
288 288 if isfunction(callback):
289 return callback(**user_dict)
289 return callback(created_by=created_by, **user_dict)
290 290
291 291 return 0
292 292
293 293
294 294 def log_delete_repository(repository_dict, deleted_by, **kwargs):
295 295 """
296 296 Post delete repository Hook. This is a dummy function for admins to re-use
297 297 if needed. It's taken from rhodecode-extensions module and executed
298 298 if present
299 299
300 300 :param repository: dict dump of repository object
301 301 :param deleted_by: username who deleted the repository
302 302
303 303 available keys of repository_dict:
304 304
305 305 'repo_type',
306 306 'description',
307 307 'private',
308 308 'created_on',
309 309 'enable_downloads',
310 310 'repo_id',
311 311 'user_id',
312 312 'enable_statistics',
313 313 'clone_uri',
314 314 'fork_id',
315 315 'group_id',
316 316 'repo_name'
317 317
318 318 """
319 319 from rhodecode import EXTENSIONS
320 320 callback = getattr(EXTENSIONS, 'DELETE_REPO_HOOK', None)
321 321 if isfunction(callback):
322 322 kw = {}
323 323 kw.update(repository_dict)
324 324 kw.update({'deleted_by': deleted_by,
325 325 'deleted_on': time.time()})
326 326 kw.update(kwargs)
327 327 return callback(**kw)
328 328
329 329 return 0
330 330
331 331
332 def log_delete_user(user_dict, **kwargs):
332 def log_delete_user(user_dict, deleted_by, **kwargs):
333 333 """
334 334 Post delete user Hook. This is a dummy function for admins to re-use
335 335 if needed. It's taken from rhodecode-extensions module and executed
336 336 if present
337 337
338 338 :param user_dict: dict dump of user object
339 339
340 340 available keys for user_dict:
341 341
342 342 'username',
343 343 'full_name_or_username',
344 344 'full_contact',
345 345 'user_id',
346 346 'name',
347 347 'firstname',
348 348 'short_contact',
349 349 'admin',
350 350 'lastname',
351 351 'ip_addresses',
352 352 'ldap_dn',
353 353 'email',
354 354 'api_key',
355 355 'last_login',
356 356 'full_name',
357 357 'active',
358 358 'password',
359 359 'emails',
360 360 'inherit_default_permissions'
361 361
362 362 """
363 363 from rhodecode import EXTENSIONS
364 364 callback = getattr(EXTENSIONS, 'DELETE_USER_HOOK', None)
365 365 if isfunction(callback):
366 return callback(**user_dict)
366 return callback(deleted_by=deleted_by, **user_dict)
367 367
368 368 return 0
369 369
370 370
371 371 handle_git_pre_receive = (lambda repo_path, revs, env:
372 372 handle_git_receive(repo_path, revs, env, hook_type='pre'))
373 373 handle_git_post_receive = (lambda repo_path, revs, env:
374 374 handle_git_receive(repo_path, revs, env, hook_type='post'))
375 375
376 376
377 377 def handle_git_receive(repo_path, revs, env, hook_type='post'):
378 378 """
379 379 A really hacky method that is runned by git post-receive hook and logs
380 380 an push action together with pushed revisions. It's executed by subprocess
381 381 thus needs all info to be able to create a on the fly pylons enviroment,
382 382 connect to database and run the logging code. Hacky as sh*t but works.
383 383
384 384 :param repo_path:
385 385 :param revs:
386 386 :param env:
387 387 """
388 388 from paste.deploy import appconfig
389 389 from sqlalchemy import engine_from_config
390 390 from rhodecode.config.environment import load_environment
391 391 from rhodecode.model import init_model
392 392 from rhodecode.model.db import RhodeCodeUi
393 393 from rhodecode.lib.utils import make_ui
394 394 extras = _extract_extras(env)
395 395
396 396 path, ini_name = os.path.split(extras['config'])
397 397 conf = appconfig('config:%s' % ini_name, relative_to=path)
398 398 load_environment(conf.global_conf, conf.local_conf)
399 399
400 400 engine = engine_from_config(conf, 'sqlalchemy.db1.')
401 401 init_model(engine)
402 402
403 403 baseui = make_ui('db')
404 404 # fix if it's not a bare repo
405 405 if repo_path.endswith(os.sep + '.git'):
406 406 repo_path = repo_path[:-5]
407 407
408 408 repo = Repository.get_by_full_path(repo_path)
409 409 if not repo:
410 410 raise OSError('Repository %s not found in database'
411 411 % (safe_str(repo_path)))
412 412
413 413 _hooks = dict(baseui.configitems('hooks')) or {}
414 414
415 415 if hook_type == 'pre':
416 416 repo = repo.scm_instance
417 417 else:
418 418 #post push shouldn't use the cached instance never
419 419 repo = repo.scm_instance_no_cache()
420 420
421 421 if hook_type == 'pre':
422 422 pre_push(baseui, repo)
423 423
424 424 # if push hook is enabled via web interface
425 425 elif hook_type == 'post' and _hooks.get(RhodeCodeUi.HOOK_PUSH):
426 426
427 427 rev_data = []
428 428 for l in revs:
429 429 old_rev, new_rev, ref = l.split(' ')
430 430 _ref_data = ref.split('/')
431 431 if _ref_data[1] in ['tags', 'heads']:
432 432 rev_data.append({'old_rev': old_rev,
433 433 'new_rev': new_rev,
434 434 'ref': ref,
435 435 'type': _ref_data[1],
436 436 'name': _ref_data[2].strip()})
437 437
438 438 git_revs = []
439 439 for push_ref in rev_data:
440 440 _type = push_ref['type']
441 441 if _type == 'heads':
442 442 if push_ref['old_rev'] == EmptyChangeset().raw_id:
443 443 cmd = "for-each-ref --format='%(refname)' 'refs/heads/*'"
444 444 heads = repo.run_git_command(cmd)[0]
445 445 heads = heads.replace(push_ref['ref'], '')
446 446 heads = ' '.join(map(lambda c: c.strip('\n').strip(),
447 447 heads.splitlines()))
448 448 cmd = (('log %(new_rev)s' % push_ref) +
449 449 ' --reverse --pretty=format:"%H" --not ' + heads)
450 450 git_revs += repo.run_git_command(cmd)[0].splitlines()
451 451
452 452 elif push_ref['new_rev'] == EmptyChangeset().raw_id:
453 453 #delete branch case
454 454 git_revs += ['delete_branch=>%s' % push_ref['name']]
455 455 else:
456 456 cmd = (('log %(old_rev)s..%(new_rev)s' % push_ref) +
457 457 ' --reverse --pretty=format:"%H"')
458 458 git_revs += repo.run_git_command(cmd)[0].splitlines()
459 459
460 460 elif _type == 'tags':
461 461 git_revs += ['tag=>%s' % push_ref['name']]
462 462
463 463 log_push_action(baseui, repo, _git_revs=git_revs)
@@ -1,819 +1,829 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28 import itertools
29 29 import collections
30 30 from pylons import url
31 31 from pylons.i18n.translation import _
32 32
33 33 from sqlalchemy.exc import DatabaseError
34 34 from sqlalchemy.orm import joinedload
35 35
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key, get_current_rhodecode_user
37 37 from rhodecode.lib.caching_query import FromCache
38 38 from rhodecode.model import BaseModel
39 39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
40 40 UserToPerm, UserGroupRepoToPerm, UserGroupToPerm, UserGroupMember, \
41 41 Notification, RepoGroup, UserRepoGroupToPerm, UserGroupRepoGroupToPerm, \
42 42 UserEmailMap, UserIpMap, UserGroupUserGroupToPerm, UserGroup
43 43 from rhodecode.lib.exceptions import DefaultUserException, \
44 44 UserOwnsReposException
45 45 from rhodecode.model.meta import Session
46 46
47 47
48 48 log = logging.getLogger(__name__)
49 49
50 50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
51 51
52 52
53 53 class UserModel(BaseModel):
54 54 cls = User
55 55
56 56 def get(self, user_id, cache=False):
57 57 user = self.sa.query(User)
58 58 if cache:
59 59 user = user.options(FromCache("sql_cache_short",
60 60 "get_user_%s" % user_id))
61 61 return user.get(user_id)
62 62
63 63 def get_user(self, user):
64 64 return self._get_user(user)
65 65
66 66 def get_by_username(self, username, cache=False, case_insensitive=False):
67 67
68 68 if case_insensitive:
69 69 user = self.sa.query(User).filter(User.username.ilike(username))
70 70 else:
71 71 user = self.sa.query(User)\
72 72 .filter(User.username == username)
73 73 if cache:
74 74 user = user.options(FromCache("sql_cache_short",
75 75 "get_user_%s" % username))
76 76 return user.scalar()
77 77
78 78 def get_by_email(self, email, cache=False, case_insensitive=False):
79 79 return User.get_by_email(email, case_insensitive, cache)
80 80
81 81 def get_by_api_key(self, api_key, cache=False):
82 82 return User.get_by_api_key(api_key, cache)
83 83
84 def create(self, form_data):
84 def create(self, form_data, cur_user=None):
85 if not cur_user:
86 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
85 87 from rhodecode.lib.auth import get_crypt_password
86 88 try:
87 89 new_user = User()
88 90 for k, v in form_data.items():
89 91 if k == 'password':
90 92 v = get_crypt_password(v)
91 93 if k == 'firstname':
92 94 k = 'name'
93 95 setattr(new_user, k, v)
94 96
95 97 new_user.api_key = generate_api_key(form_data['username'])
96 98 self.sa.add(new_user)
97 99
98 100 from rhodecode.lib.hooks import log_create_user
99 log_create_user(new_user.get_dict())
101 log_create_user(new_user.get_dict(), cur_user)
100 102 return new_user
101 103 except Exception:
102 104 log.error(traceback.format_exc())
103 105 raise
104 106
105 107 def create_or_update(self, username, password, email, firstname='',
106 lastname='', active=True, admin=False, ldap_dn=None):
108 lastname='', active=True, admin=False, ldap_dn=None, cur_user=None):
107 109 """
108 110 Creates a new instance if not found, or updates current one
109 111
110 112 :param username:
111 113 :param password:
112 114 :param email:
113 115 :param active:
114 116 :param firstname:
115 117 :param lastname:
116 118 :param active:
117 119 :param admin:
118 120 :param ldap_dn:
119 121 """
122 if not cur_user:
123 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
120 124
121 125 from rhodecode.lib.auth import get_crypt_password
122 126
123 127 log.debug('Checking for %s account in RhodeCode database' % username)
124 128 user = User.get_by_username(username, case_insensitive=True)
125 129 if user is None:
126 130 log.debug('creating new user %s' % username)
127 131 new_user = User()
128 132 edit = False
129 133 else:
130 134 log.debug('updating user %s' % username)
131 135 new_user = user
132 136 edit = True
133 137
134 138 try:
135 139 new_user.username = username
136 140 new_user.admin = admin
137 141 # set password only if creating an user or password is changed
138 142 if not edit or user.password != password:
139 143 new_user.password = get_crypt_password(password) if password else None
140 144 new_user.api_key = generate_api_key(username)
141 145 new_user.email = email
142 146 new_user.active = active
143 147 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
144 148 new_user.name = firstname
145 149 new_user.lastname = lastname
146 150 self.sa.add(new_user)
147 151
148 152 if not edit:
149 153 from rhodecode.lib.hooks import log_create_user
150 log_create_user(new_user.get_dict())
154 log_create_user(new_user.get_dict(), cur_user)
151 155 return new_user
152 156 except (DatabaseError,):
153 157 log.error(traceback.format_exc())
154 158 raise
155 159
156 def create_for_container_auth(self, username, attrs):
160 def create_for_container_auth(self, username, attrs, cur_user=None):
157 161 """
158 162 Creates the given user if it's not already in the database
159 163
160 164 :param username:
161 165 :param attrs:
162 166 """
167 if not cur_user:
168 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
163 169 if self.get_by_username(username, case_insensitive=True) is None:
164 170
165 171 # autogenerate email for container account without one
166 172 generate_email = lambda usr: '%s@container_auth.account' % usr
167 173
168 174 try:
169 175 new_user = User()
170 176 new_user.username = username
171 177 new_user.password = None
172 178 new_user.api_key = generate_api_key(username)
173 179 new_user.email = attrs['email']
174 180 new_user.active = attrs.get('active', True)
175 181 new_user.name = attrs['name'] or generate_email(username)
176 182 new_user.lastname = attrs['lastname']
177 183
178 184 self.sa.add(new_user)
179 185
180 186 from rhodecode.lib.hooks import log_create_user
181 log_create_user(new_user.get_dict())
187 log_create_user(new_user.get_dict(), cur_user)
182 188 return new_user
183 189 except (DatabaseError,):
184 190 log.error(traceback.format_exc())
185 191 self.sa.rollback()
186 192 raise
187 193 log.debug('User %s already exists. Skipping creation of account'
188 194 ' for container auth.', username)
189 195 return None
190 196
191 def create_ldap(self, username, password, user_dn, attrs):
197 def create_ldap(self, username, password, user_dn, attrs, cur_user=None):
192 198 """
193 199 Checks if user is in database, if not creates this user marked
194 200 as ldap user
195 201
196 202 :param username:
197 203 :param password:
198 204 :param user_dn:
199 205 :param attrs:
200 206 """
207 if not cur_user:
208 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
201 209 from rhodecode.lib.auth import get_crypt_password
202 210 log.debug('Checking for such ldap account in RhodeCode database')
203 211 if self.get_by_username(username, case_insensitive=True) is None:
204 212
205 213 # autogenerate email for ldap account without one
206 214 generate_email = lambda usr: '%s@ldap.account' % usr
207 215
208 216 try:
209 217 new_user = User()
210 218 username = username.lower()
211 219 # add ldap account always lowercase
212 220 new_user.username = username
213 221 new_user.password = get_crypt_password(password)
214 222 new_user.api_key = generate_api_key(username)
215 223 new_user.email = attrs['email'] or generate_email(username)
216 224 new_user.active = attrs.get('active', True)
217 225 new_user.ldap_dn = safe_unicode(user_dn)
218 226 new_user.name = attrs['name']
219 227 new_user.lastname = attrs['lastname']
220 228
221 229 self.sa.add(new_user)
222 230
223 231 from rhodecode.lib.hooks import log_create_user
224 log_create_user(new_user.get_dict())
232 log_create_user(new_user.get_dict(), cur_user)
225 233 return new_user
226 234 except (DatabaseError,):
227 235 log.error(traceback.format_exc())
228 236 self.sa.rollback()
229 237 raise
230 238 log.debug('this %s user exists skipping creation of ldap account',
231 239 username)
232 240 return None
233 241
234 242 def create_registration(self, form_data):
235 243 from rhodecode.model.notification import NotificationModel
236 244
237 245 try:
238 246 form_data['admin'] = False
239 247 new_user = self.create(form_data)
240 248
241 249 self.sa.add(new_user)
242 250 self.sa.flush()
243 251
244 252 # notification to admins
245 253 subject = _('New user registration')
246 254 body = ('New user registration\n'
247 255 '---------------------\n'
248 256 '- Username: %s\n'
249 257 '- Full Name: %s\n'
250 258 '- Email: %s\n')
251 259 body = body % (new_user.username, new_user.full_name,
252 260 new_user.email)
253 261 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
254 262 kw = {'registered_user_url': edit_url}
255 263 NotificationModel().create(created_by=new_user, subject=subject,
256 264 body=body, recipients=None,
257 265 type_=Notification.TYPE_REGISTRATION,
258 266 email_kwargs=kw)
259 267
260 268 except Exception:
261 269 log.error(traceback.format_exc())
262 270 raise
263 271
264 272 def update(self, user_id, form_data, skip_attrs=[]):
265 273 from rhodecode.lib.auth import get_crypt_password
266 274 try:
267 275 user = self.get(user_id, cache=False)
268 276 if user.username == 'default':
269 277 raise DefaultUserException(
270 278 _("You can't Edit this user since it's"
271 279 " crucial for entire application"))
272 280
273 281 for k, v in form_data.items():
274 282 if k in skip_attrs:
275 283 continue
276 284 if k == 'new_password' and v:
277 285 user.password = get_crypt_password(v)
278 286 user.api_key = generate_api_key(user.username)
279 287 else:
280 288 if k == 'firstname':
281 289 k = 'name'
282 290 setattr(user, k, v)
283 291 self.sa.add(user)
284 292 except Exception:
285 293 log.error(traceback.format_exc())
286 294 raise
287 295
288 296 def update_user(self, user, **kwargs):
289 297 from rhodecode.lib.auth import get_crypt_password
290 298 try:
291 299 user = self._get_user(user)
292 300 if user.username == 'default':
293 301 raise DefaultUserException(
294 302 _("You can't Edit this user since it's"
295 303 " crucial for entire application")
296 304 )
297 305
298 306 for k, v in kwargs.items():
299 307 if k == 'password' and v:
300 308 v = get_crypt_password(v)
301 309 user.api_key = generate_api_key(user.username)
302 310
303 311 setattr(user, k, v)
304 312 self.sa.add(user)
305 313 return user
306 314 except Exception:
307 315 log.error(traceback.format_exc())
308 316 raise
309 317
310 def delete(self, user):
318 def delete(self, user, cur_user=None):
319 if not cur_user:
320 cur_user = getattr(get_current_rhodecode_user(), 'username', '?')
311 321 user = self._get_user(user)
312 322
313 323 try:
314 324 if user.username == 'default':
315 325 raise DefaultUserException(
316 326 _(u"You can't remove this user since it's"
317 327 " crucial for entire application")
318 328 )
319 329 if user.repositories:
320 330 repos = [x.repo_name for x in user.repositories]
321 331 raise UserOwnsReposException(
322 332 _(u'user "%s" still owns %s repositories and cannot be '
323 333 'removed. Switch owners or remove those repositories. %s')
324 334 % (user.username, len(repos), ', '.join(repos))
325 335 )
326 336 self.sa.delete(user)
327 337
328 338 from rhodecode.lib.hooks import log_delete_user
329 log_delete_user(user.get_dict())
339 log_delete_user(user.get_dict(), cur_user)
330 340 except Exception:
331 341 log.error(traceback.format_exc())
332 342 raise
333 343
334 344 def reset_password_link(self, data):
335 345 from rhodecode.lib.celerylib import tasks, run_task
336 346 from rhodecode.model.notification import EmailNotificationModel
337 347 user_email = data['email']
338 348 try:
339 349 user = User.get_by_email(user_email)
340 350 if user:
341 351 log.debug('password reset user found %s' % user)
342 352 link = url('reset_password_confirmation', key=user.api_key,
343 353 qualified=True)
344 354 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
345 355 body = EmailNotificationModel().get_email_tmpl(reg_type,
346 356 **{'user': user.short_contact,
347 357 'reset_url': link})
348 358 log.debug('sending email')
349 359 run_task(tasks.send_email, user_email,
350 360 _("Password reset link"), body, body)
351 361 log.info('send new password mail to %s' % user_email)
352 362 else:
353 363 log.debug("password reset email %s not found" % user_email)
354 364 except Exception:
355 365 log.error(traceback.format_exc())
356 366 return False
357 367
358 368 return True
359 369
360 370 def reset_password(self, data):
361 371 from rhodecode.lib.celerylib import tasks, run_task
362 372 from rhodecode.lib import auth
363 373 user_email = data['email']
364 374 try:
365 375 try:
366 376 user = User.get_by_email(user_email)
367 377 new_passwd = auth.PasswordGenerator().gen_password(8,
368 378 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
369 379 if user:
370 380 user.password = auth.get_crypt_password(new_passwd)
371 381 user.api_key = auth.generate_api_key(user.username)
372 382 Session().add(user)
373 383 Session().commit()
374 384 log.info('change password for %s' % user_email)
375 385 if new_passwd is None:
376 386 raise Exception('unable to generate new password')
377 387 except Exception:
378 388 log.error(traceback.format_exc())
379 389 Session().rollback()
380 390
381 391 run_task(tasks.send_email, user_email,
382 392 _('Your new password'),
383 393 _('Your new RhodeCode password:%s') % (new_passwd))
384 394 log.info('send new password mail to %s' % user_email)
385 395
386 396 except Exception:
387 397 log.error('Failed to update user password')
388 398 log.error(traceback.format_exc())
389 399
390 400 return True
391 401
392 402 def fill_data(self, auth_user, user_id=None, api_key=None):
393 403 """
394 404 Fetches auth_user by user_id,or api_key if present.
395 405 Fills auth_user attributes with those taken from database.
396 406 Additionally set's is_authenitated if lookup fails
397 407 present in database
398 408
399 409 :param auth_user: instance of user to set attributes
400 410 :param user_id: user id to fetch by
401 411 :param api_key: api key to fetch by
402 412 """
403 413 if user_id is None and api_key is None:
404 414 raise Exception('You need to pass user_id or api_key')
405 415
406 416 try:
407 417 if api_key:
408 418 dbuser = self.get_by_api_key(api_key)
409 419 else:
410 420 dbuser = self.get(user_id)
411 421
412 422 if dbuser is not None and dbuser.active:
413 423 log.debug('filling %s data' % dbuser)
414 424 for k, v in dbuser.get_dict().items():
415 425 setattr(auth_user, k, v)
416 426 else:
417 427 return False
418 428
419 429 except Exception:
420 430 log.error(traceback.format_exc())
421 431 auth_user.is_authenticated = False
422 432 return False
423 433
424 434 return True
425 435
426 436 def fill_perms(self, user, explicit=True, algo='higherwin'):
427 437 """
428 438 Fills user permission attribute with permissions taken from database
429 439 works for permissions given for repositories, and for permissions that
430 440 are granted to groups
431 441
432 442 :param user: user instance to fill his perms
433 443 :param explicit: In case there are permissions both for user and a group
434 444 that user is part of, explicit flag will defiine if user will
435 445 explicitly override permissions from group, if it's False it will
436 446 make decision based on the algo
437 447 :param algo: algorithm to decide what permission should be choose if
438 448 it's multiple defined, eg user in two different groups. It also
439 449 decides if explicit flag is turned off how to specify the permission
440 450 for case when user is in a group + have defined separate permission
441 451 """
442 452 RK = 'repositories'
443 453 GK = 'repositories_groups'
444 454 UK = 'user_groups'
445 455 GLOBAL = 'global'
446 456 user.permissions[RK] = {}
447 457 user.permissions[GK] = {}
448 458 user.permissions[UK] = {}
449 459 user.permissions[GLOBAL] = set()
450 460
451 461 def _choose_perm(new_perm, cur_perm):
452 462 new_perm_val = PERM_WEIGHTS[new_perm]
453 463 cur_perm_val = PERM_WEIGHTS[cur_perm]
454 464 if algo == 'higherwin':
455 465 if new_perm_val > cur_perm_val:
456 466 return new_perm
457 467 return cur_perm
458 468 elif algo == 'lowerwin':
459 469 if new_perm_val < cur_perm_val:
460 470 return new_perm
461 471 return cur_perm
462 472
463 473 #======================================================================
464 474 # fetch default permissions
465 475 #======================================================================
466 476 default_user = User.get_by_username('default', cache=True)
467 477 default_user_id = default_user.user_id
468 478
469 479 default_repo_perms = Permission.get_default_perms(default_user_id)
470 480 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
471 481 default_user_group_perms = Permission.get_default_user_group_perms(default_user_id)
472 482
473 483 if user.is_admin:
474 484 #==================================================================
475 485 # admin user have all default rights for repositories
476 486 # and groups set to admin
477 487 #==================================================================
478 488 user.permissions[GLOBAL].add('hg.admin')
479 489
480 490 # repositories
481 491 for perm in default_repo_perms:
482 492 r_k = perm.UserRepoToPerm.repository.repo_name
483 493 p = 'repository.admin'
484 494 user.permissions[RK][r_k] = p
485 495
486 496 # repository groups
487 497 for perm in default_repo_groups_perms:
488 498 rg_k = perm.UserRepoGroupToPerm.group.group_name
489 499 p = 'group.admin'
490 500 user.permissions[GK][rg_k] = p
491 501
492 502 # user groups
493 503 for perm in default_user_group_perms:
494 504 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
495 505 p = 'usergroup.admin'
496 506 user.permissions[UK][u_k] = p
497 507 return user
498 508
499 509 #==================================================================
500 510 # SET DEFAULTS GLOBAL, REPOS, REPOSITORY GROUPS
501 511 #==================================================================
502 512 uid = user.user_id
503 513
504 514 # default global permissions taken fron the default user
505 515 default_global_perms = self.sa.query(UserToPerm)\
506 516 .filter(UserToPerm.user_id == default_user_id)
507 517
508 518 for perm in default_global_perms:
509 519 user.permissions[GLOBAL].add(perm.permission.permission_name)
510 520
511 521 # defaults for repositories, taken from default user
512 522 for perm in default_repo_perms:
513 523 r_k = perm.UserRepoToPerm.repository.repo_name
514 524 if perm.Repository.private and not (perm.Repository.user_id == uid):
515 525 # disable defaults for private repos,
516 526 p = 'repository.none'
517 527 elif perm.Repository.user_id == uid:
518 528 # set admin if owner
519 529 p = 'repository.admin'
520 530 else:
521 531 p = perm.Permission.permission_name
522 532
523 533 user.permissions[RK][r_k] = p
524 534
525 535 # defaults for repository groups taken from default user permission
526 536 # on given group
527 537 for perm in default_repo_groups_perms:
528 538 rg_k = perm.UserRepoGroupToPerm.group.group_name
529 539 p = perm.Permission.permission_name
530 540 user.permissions[GK][rg_k] = p
531 541
532 542 # defaults for user groups taken from default user permission
533 543 # on given user group
534 544 for perm in default_user_group_perms:
535 545 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
536 546 p = perm.Permission.permission_name
537 547 user.permissions[UK][u_k] = p
538 548
539 549 #======================================================================
540 550 # !! OVERRIDE GLOBALS !! with user permissions if any found
541 551 #======================================================================
542 552 # those can be configured from groups or users explicitly
543 553 _configurable = set([
544 554 'hg.fork.none', 'hg.fork.repository',
545 555 'hg.create.none', 'hg.create.repository',
546 556 'hg.usergroup.create.false', 'hg.usergroup.create.true'
547 557 ])
548 558
549 559 # USER GROUPS comes first
550 560 # user group global permissions
551 561 user_perms_from_users_groups = self.sa.query(UserGroupToPerm)\
552 562 .options(joinedload(UserGroupToPerm.permission))\
553 563 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
554 564 UserGroupMember.users_group_id))\
555 565 .filter(UserGroupMember.user_id == uid)\
556 566 .order_by(UserGroupToPerm.users_group_id)\
557 567 .all()
558 568 #need to group here by groups since user can be in more than one group
559 569 _grouped = [[x, list(y)] for x, y in
560 570 itertools.groupby(user_perms_from_users_groups,
561 571 lambda x:x.users_group)]
562 572 for gr, perms in _grouped:
563 573 # since user can be in multiple groups iterate over them and
564 574 # select the lowest permissions first (more explicit)
565 575 ##TODO: do this^^
566 576 if not gr.inherit_default_permissions:
567 577 # NEED TO IGNORE all configurable permissions and
568 578 # replace them with explicitly set
569 579 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
570 580 .difference(_configurable)
571 581 for perm in perms:
572 582 user.permissions[GLOBAL].add(perm.permission.permission_name)
573 583
574 584 # user specific global permissions
575 585 user_perms = self.sa.query(UserToPerm)\
576 586 .options(joinedload(UserToPerm.permission))\
577 587 .filter(UserToPerm.user_id == uid).all()
578 588
579 589 if not user.inherit_default_permissions:
580 590 # NEED TO IGNORE all configurable permissions and
581 591 # replace them with explicitly set
582 592 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
583 593 .difference(_configurable)
584 594
585 595 for perm in user_perms:
586 596 user.permissions[GLOBAL].add(perm.permission.permission_name)
587 597 ## END GLOBAL PERMISSIONS
588 598
589 599 #======================================================================
590 600 # !! PERMISSIONS FOR REPOSITORIES !!
591 601 #======================================================================
592 602 #======================================================================
593 603 # check if user is part of user groups for this repository and
594 604 # fill in his permission from it. _choose_perm decides of which
595 605 # permission should be selected based on selected method
596 606 #======================================================================
597 607
598 608 # user group for repositories permissions
599 609 user_repo_perms_from_users_groups = \
600 610 self.sa.query(UserGroupRepoToPerm, Permission, Repository,)\
601 611 .join((Repository, UserGroupRepoToPerm.repository_id ==
602 612 Repository.repo_id))\
603 613 .join((Permission, UserGroupRepoToPerm.permission_id ==
604 614 Permission.permission_id))\
605 615 .join((UserGroupMember, UserGroupRepoToPerm.users_group_id ==
606 616 UserGroupMember.users_group_id))\
607 617 .filter(UserGroupMember.user_id == uid)\
608 618 .all()
609 619
610 620 multiple_counter = collections.defaultdict(int)
611 621 for perm in user_repo_perms_from_users_groups:
612 622 r_k = perm.UserGroupRepoToPerm.repository.repo_name
613 623 multiple_counter[r_k] += 1
614 624 p = perm.Permission.permission_name
615 625 cur_perm = user.permissions[RK][r_k]
616 626
617 627 if perm.Repository.user_id == uid:
618 628 # set admin if owner
619 629 p = 'repository.admin'
620 630 else:
621 631 if multiple_counter[r_k] > 1:
622 632 p = _choose_perm(p, cur_perm)
623 633 user.permissions[RK][r_k] = p
624 634
625 635 # user explicit permissions for repositories, overrides any specified
626 636 # by the group permission
627 637 user_repo_perms = Permission.get_default_perms(uid)
628 638 for perm in user_repo_perms:
629 639 r_k = perm.UserRepoToPerm.repository.repo_name
630 640 cur_perm = user.permissions[RK][r_k]
631 641 # set admin if owner
632 642 if perm.Repository.user_id == uid:
633 643 p = 'repository.admin'
634 644 else:
635 645 p = perm.Permission.permission_name
636 646 if not explicit:
637 647 p = _choose_perm(p, cur_perm)
638 648 user.permissions[RK][r_k] = p
639 649
640 650 #======================================================================
641 651 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
642 652 #======================================================================
643 653 #======================================================================
644 654 # check if user is part of user groups for this repository groups and
645 655 # fill in his permission from it. _choose_perm decides of which
646 656 # permission should be selected based on selected method
647 657 #======================================================================
648 658 # user group for repo groups permissions
649 659 user_repo_group_perms_from_users_groups = \
650 660 self.sa.query(UserGroupRepoGroupToPerm, Permission, RepoGroup)\
651 661 .join((RepoGroup, UserGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
652 662 .join((Permission, UserGroupRepoGroupToPerm.permission_id
653 663 == Permission.permission_id))\
654 664 .join((UserGroupMember, UserGroupRepoGroupToPerm.users_group_id
655 665 == UserGroupMember.users_group_id))\
656 666 .filter(UserGroupMember.user_id == uid)\
657 667 .all()
658 668
659 669 multiple_counter = collections.defaultdict(int)
660 670 for perm in user_repo_group_perms_from_users_groups:
661 671 g_k = perm.UserGroupRepoGroupToPerm.group.group_name
662 672 multiple_counter[g_k] += 1
663 673 p = perm.Permission.permission_name
664 674 cur_perm = user.permissions[GK][g_k]
665 675 if multiple_counter[g_k] > 1:
666 676 p = _choose_perm(p, cur_perm)
667 677 user.permissions[GK][g_k] = p
668 678
669 679 # user explicit permissions for repository groups
670 680 user_repo_groups_perms = Permission.get_default_group_perms(uid)
671 681 for perm in user_repo_groups_perms:
672 682 rg_k = perm.UserRepoGroupToPerm.group.group_name
673 683 p = perm.Permission.permission_name
674 684 cur_perm = user.permissions[GK][rg_k]
675 685 if not explicit:
676 686 p = _choose_perm(p, cur_perm)
677 687 user.permissions[GK][rg_k] = p
678 688
679 689 #======================================================================
680 690 # !! PERMISSIONS FOR USER GROUPS !!
681 691 #======================================================================
682 692 # user group for user group permissions
683 693 user_group_user_groups_perms = \
684 694 self.sa.query(UserGroupUserGroupToPerm, Permission, UserGroup)\
685 695 .join((UserGroup, UserGroupUserGroupToPerm.target_user_group_id
686 696 == UserGroup.users_group_id))\
687 697 .join((Permission, UserGroupUserGroupToPerm.permission_id
688 698 == Permission.permission_id))\
689 699 .join((UserGroupMember, UserGroupUserGroupToPerm.user_group_id
690 700 == UserGroupMember.users_group_id))\
691 701 .filter(UserGroupMember.user_id == uid)\
692 702 .all()
693 703
694 704 multiple_counter = collections.defaultdict(int)
695 705 for perm in user_group_user_groups_perms:
696 706 g_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
697 707 multiple_counter[g_k] += 1
698 708 p = perm.Permission.permission_name
699 709 cur_perm = user.permissions[UK][g_k]
700 710 if multiple_counter[g_k] > 1:
701 711 p = _choose_perm(p, cur_perm)
702 712 user.permissions[UK][g_k] = p
703 713
704 714 #user explicit permission for user groups
705 715 user_user_groups_perms = Permission.get_default_user_group_perms(uid)
706 716 for perm in user_user_groups_perms:
707 717 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
708 718 p = perm.Permission.permission_name
709 719 cur_perm = user.permissions[UK][u_k]
710 720 if not explicit:
711 721 p = _choose_perm(p, cur_perm)
712 722 user.permissions[UK][u_k] = p
713 723
714 724 return user
715 725
716 726 def has_perm(self, user, perm):
717 727 perm = self._get_perm(perm)
718 728 user = self._get_user(user)
719 729
720 730 return UserToPerm.query().filter(UserToPerm.user == user)\
721 731 .filter(UserToPerm.permission == perm).scalar() is not None
722 732
723 733 def grant_perm(self, user, perm):
724 734 """
725 735 Grant user global permissions
726 736
727 737 :param user:
728 738 :param perm:
729 739 """
730 740 user = self._get_user(user)
731 741 perm = self._get_perm(perm)
732 742 # if this permission is already granted skip it
733 743 _perm = UserToPerm.query()\
734 744 .filter(UserToPerm.user == user)\
735 745 .filter(UserToPerm.permission == perm)\
736 746 .scalar()
737 747 if _perm:
738 748 return
739 749 new = UserToPerm()
740 750 new.user = user
741 751 new.permission = perm
742 752 self.sa.add(new)
743 753
744 754 def revoke_perm(self, user, perm):
745 755 """
746 756 Revoke users global permissions
747 757
748 758 :param user:
749 759 :param perm:
750 760 """
751 761 user = self._get_user(user)
752 762 perm = self._get_perm(perm)
753 763
754 764 obj = UserToPerm.query()\
755 765 .filter(UserToPerm.user == user)\
756 766 .filter(UserToPerm.permission == perm)\
757 767 .scalar()
758 768 if obj:
759 769 self.sa.delete(obj)
760 770
761 771 def add_extra_email(self, user, email):
762 772 """
763 773 Adds email address to UserEmailMap
764 774
765 775 :param user:
766 776 :param email:
767 777 """
768 778 from rhodecode.model import forms
769 779 form = forms.UserExtraEmailForm()()
770 780 data = form.to_python(dict(email=email))
771 781 user = self._get_user(user)
772 782
773 783 obj = UserEmailMap()
774 784 obj.user = user
775 785 obj.email = data['email']
776 786 self.sa.add(obj)
777 787 return obj
778 788
779 789 def delete_extra_email(self, user, email_id):
780 790 """
781 791 Removes email address from UserEmailMap
782 792
783 793 :param user:
784 794 :param email_id:
785 795 """
786 796 user = self._get_user(user)
787 797 obj = UserEmailMap.query().get(email_id)
788 798 if obj:
789 799 self.sa.delete(obj)
790 800
791 801 def add_extra_ip(self, user, ip):
792 802 """
793 803 Adds ip address to UserIpMap
794 804
795 805 :param user:
796 806 :param ip:
797 807 """
798 808 from rhodecode.model import forms
799 809 form = forms.UserExtraIpForm()()
800 810 data = form.to_python(dict(ip=ip))
801 811 user = self._get_user(user)
802 812
803 813 obj = UserIpMap()
804 814 obj.user = user
805 815 obj.ip_addr = data['ip']
806 816 self.sa.add(obj)
807 817 return obj
808 818
809 819 def delete_extra_ip(self, user, ip_id):
810 820 """
811 821 Removes ip address from UserIpMap
812 822
813 823 :param user:
814 824 :param ip_id:
815 825 """
816 826 user = self._get_user(user)
817 827 obj = UserIpMap.query().get(ip_id)
818 828 if obj:
819 829 self.sa.delete(obj)
General Comments 0
You need to be logged in to leave comments. Login now