##// END OF EJS Templates
#399 added inheritance of permissions for users group on repos groups
marcink -
r2129:43481c3d beta
parent child Browse files
Show More
@@ -1,590 +1,591 b''
1 1 .. _changelog:
2 2
3 3 =========
4 4 Changelog
5 5 =========
6 6
7 7
8 8 1.3.4 (**2012-XX-XX**)
9 9 ----------------------
10 10
11 11 :status: in-progress
12 12 :branch: beta
13 13
14 14 news
15 15 ++++
16 16
17 17 - Whoosh logging is now controlled by the .ini files logging setup
18 18 - added clone-url into edit form on /settings page
19 19 - added help text into repo add/edit forms
20 20 - created rcextensions module with additional mappings (ref #322) and
21 21 post push/pull/create repo hooks callbacks
22 22 - implemented #377 Users view for his own permissions on account page
23 - #399 added inheritance of permissions for users group on repos groups
23 24
24 25 fixes
25 26 +++++
26 27
27 28 - fixed #390 cache invalidation problems on repos inside group
28 29 - fixed #385 clone by ID url was loosing proxy prefix in URL
29 30 - fixed some unicode problems with waitress
30 31 - fixed issue with escaping < and > in changeset commits
31 32 - fixed error occurring during recursive group creation in API
32 33 create_repo function
33 34 - fixed #393 py2.5 fixes for routes url generator
34 35 - fixed #397 Private repository groups shows up before login
35 36 - fixed #396 fixed problems with revoking users in nested groups
36 37
37 38 1.3.3 (**2012-03-02**)
38 39 ----------------------
39 40
40 41 news
41 42 ++++
42 43
43 44
44 45 fixes
45 46 +++++
46 47
47 48 - fixed some python2.5 compatibility issues
48 49 - fixed issues with removed repos was accidentally added as groups, after
49 50 full rescan of paths
50 51 - fixes #376 Cannot edit user (using container auth)
51 52 - fixes #378 Invalid image urls on changeset screen with proxy-prefix
52 53 configuration
53 54 - fixed initial sorting of repos inside repo group
54 55 - fixes issue when user tried to resubmit same permission into user/user_groups
55 56 - bumped beaker version that fixes #375 leap error bug
56 57 - fixed raw_changeset for git. It was generated with hg patch headers
57 58 - fixed vcs issue with last_changeset for filenodes
58 59 - fixed missing commit after hook delete
59 60 - fixed #372 issues with git operation detection that caused a security issue
60 61 for git repos
61 62
62 63 1.3.2 (**2012-02-28**)
63 64 ----------------------
64 65
65 66 news
66 67 ++++
67 68
68 69
69 70 fixes
70 71 +++++
71 72
72 73 - fixed git protocol issues with repos-groups
73 74 - fixed git remote repos validator that prevented from cloning remote git repos
74 75 - fixes #370 ending slashes fixes for repo and groups
75 76 - fixes #368 improved git-protocol detection to handle other clients
76 77 - fixes #366 When Setting Repository Group To Blank Repo Group Wont Be
77 78 Moved To Root
78 79 - fixes #371 fixed issues with beaker/sqlalchemy and non-ascii cache keys
79 80 - fixed #373 missing cascade drop on user_group_to_perm table
80 81
81 82 1.3.1 (**2012-02-27**)
82 83 ----------------------
83 84
84 85 news
85 86 ++++
86 87
87 88
88 89 fixes
89 90 +++++
90 91
91 92 - redirection loop occurs when remember-me wasn't checked during login
92 93 - fixes issues with git blob history generation
93 94 - don't fetch branch for git in file history dropdown. Causes unneeded slowness
94 95
95 96 1.3.0 (**2012-02-26**)
96 97 ----------------------
97 98
98 99 news
99 100 ++++
100 101
101 102 - code review, inspired by github code-comments
102 103 - #215 rst and markdown README files support
103 104 - #252 Container-based and proxy pass-through authentication support
104 105 - #44 branch browser. Filtering of changelog by branches
105 106 - mercurial bookmarks support
106 107 - new hover top menu, optimized to add maximum size for important views
107 108 - configurable clone url template with possibility to specify protocol like
108 109 ssh:// or http:// and also manually alter other parts of clone_url.
109 110 - enabled largefiles extension by default
110 111 - optimized summary file pages and saved a lot of unused space in them
111 112 - #239 option to manually mark repository as fork
112 113 - #320 mapping of commit authors to RhodeCode users
113 114 - #304 hashes are displayed using monospace font
114 115 - diff configuration, toggle white lines and context lines
115 116 - #307 configurable diffs, whitespace toggle, increasing context lines
116 117 - sorting on branches, tags and bookmarks using YUI datatable
117 118 - improved file filter on files page
118 119 - implements #330 api method for listing nodes ar particular revision
119 120 - #73 added linking issues in commit messages to chosen issue tracker url
120 121 based on user defined regular expression
121 122 - added linking of changesets in commit messages
122 123 - new compact changelog with expandable commit messages
123 124 - firstname and lastname are optional in user creation
124 125 - #348 added post-create repository hook
125 126 - #212 global encoding settings is now configurable from .ini files
126 127 - #227 added repository groups permissions
127 128 - markdown gets codehilite extensions
128 129 - new API methods, delete_repositories, grante/revoke permissions for groups
129 130 and repos
130 131
131 132
132 133 fixes
133 134 +++++
134 135
135 136 - rewrote dbsession management for atomic operations, and better error handling
136 137 - fixed sorting of repo tables
137 138 - #326 escape of special html entities in diffs
138 139 - normalized user_name => username in api attributes
139 140 - fixes #298 ldap created users with mixed case emails created conflicts
140 141 on saving a form
141 142 - fixes issue when owner of a repo couldn't revoke permissions for users
142 143 and groups
143 144 - fixes #271 rare JSON serialization problem with statistics
144 145 - fixes #337 missing validation check for conflicting names of a group with a
145 146 repositories group
146 147 - #340 fixed session problem for mysql and celery tasks
147 148 - fixed #331 RhodeCode mangles repository names if the a repository group
148 149 contains the "full path" to the repositories
149 150 - #355 RhodeCode doesn't store encrypted LDAP passwords
150 151
151 152 1.2.5 (**2012-01-28**)
152 153 ----------------------
153 154
154 155 news
155 156 ++++
156 157
157 158 fixes
158 159 +++++
159 160
160 161 - #340 Celery complains about MySQL server gone away, added session cleanup
161 162 for celery tasks
162 163 - #341 "scanning for repositories in None" log message during Rescan was missing
163 164 a parameter
164 165 - fixed creating archives with subrepos. Some hooks were triggered during that
165 166 operation leading to crash.
166 167 - fixed missing email in account page.
167 168 - Reverted Mercurial to 2.0.1 for windows due to bug in Mercurial that makes
168 169 forking on windows impossible
169 170
170 171 1.2.4 (**2012-01-19**)
171 172 ----------------------
172 173
173 174 news
174 175 ++++
175 176
176 177 - RhodeCode is bundled with mercurial series 2.0.X by default, with
177 178 full support to largefiles extension. Enabled by default in new installations
178 179 - #329 Ability to Add/Remove Groups to/from a Repository via AP
179 180 - added requires.txt file with requirements
180 181
181 182 fixes
182 183 +++++
183 184
184 185 - fixes db session issues with celery when emailing admins
185 186 - #331 RhodeCode mangles repository names if the a repository group
186 187 contains the "full path" to the repositories
187 188 - #298 Conflicting e-mail addresses for LDAP and RhodeCode users
188 189 - DB session cleanup after hg protocol operations, fixes issues with
189 190 `mysql has gone away` errors
190 191 - #333 doc fixes for get_repo api function
191 192 - #271 rare JSON serialization problem with statistics enabled
192 193 - #337 Fixes issues with validation of repository name conflicting with
193 194 a group name. A proper message is now displayed.
194 195 - #292 made ldap_dn in user edit readonly, to get rid of confusion that field
195 196 doesn't work
196 197 - #316 fixes issues with web description in hgrc files
197 198
198 199 1.2.3 (**2011-11-02**)
199 200 ----------------------
200 201
201 202 news
202 203 ++++
203 204
204 205 - added option to manage repos group for non admin users
205 206 - added following API methods for get_users, create_user, get_users_groups,
206 207 get_users_group, create_users_group, add_user_to_users_groups, get_repos,
207 208 get_repo, create_repo, add_user_to_repo
208 209 - implements #237 added password confirmation for my account
209 210 and admin edit user.
210 211 - implements #291 email notification for global events are now sent to all
211 212 administrator users, and global config email.
212 213
213 214 fixes
214 215 +++++
215 216
216 217 - added option for passing auth method for smtp mailer
217 218 - #276 issue with adding a single user with id>10 to usergroups
218 219 - #277 fixes windows LDAP settings in which missing values breaks the ldap auth
219 220 - #288 fixes managing of repos in a group for non admin user
220 221
221 222 1.2.2 (**2011-10-17**)
222 223 ----------------------
223 224
224 225 news
225 226 ++++
226 227
227 228 - #226 repo groups are available by path instead of numerical id
228 229
229 230 fixes
230 231 +++++
231 232
232 233 - #259 Groups with the same name but with different parent group
233 234 - #260 Put repo in group, then move group to another group -> repo becomes unavailable
234 235 - #258 RhodeCode 1.2 assumes egg folder is writable (lockfiles problems)
235 236 - #265 ldap save fails sometimes on converting attributes to booleans,
236 237 added getter and setter into model that will prevent from this on db model level
237 238 - fixed problems with timestamps issues #251 and #213
238 239 - fixes #266 RhodeCode allows to create repo with the same name and in
239 240 the same parent as group
240 241 - fixes #245 Rescan of the repositories on Windows
241 242 - fixes #248 cannot edit repos inside a group on windows
242 243 - fixes #219 forking problems on windows
243 244
244 245 1.2.1 (**2011-10-08**)
245 246 ----------------------
246 247
247 248 news
248 249 ++++
249 250
250 251
251 252 fixes
252 253 +++++
253 254
254 255 - fixed problems with basic auth and push problems
255 256 - gui fixes
256 257 - fixed logger
257 258
258 259 1.2.0 (**2011-10-07**)
259 260 ----------------------
260 261
261 262 news
262 263 ++++
263 264
264 265 - implemented #47 repository groups
265 266 - implemented #89 Can setup google analytics code from settings menu
266 267 - implemented #91 added nicer looking archive urls with more download options
267 268 like tags, branches
268 269 - implemented #44 into file browsing, and added follow branch option
269 270 - implemented #84 downloads can be enabled/disabled for each repository
270 271 - anonymous repository can be cloned without having to pass default:default
271 272 into clone url
272 273 - fixed #90 whoosh indexer can index chooses repositories passed in command
273 274 line
274 275 - extended journal with day aggregates and paging
275 276 - implemented #107 source code lines highlight ranges
276 277 - implemented #93 customizable changelog on combined revision ranges -
277 278 equivalent of githubs compare view
278 279 - implemented #108 extended and more powerful LDAP configuration
279 280 - implemented #56 users groups
280 281 - major code rewrites optimized codes for speed and memory usage
281 282 - raw and diff downloads are now in git format
282 283 - setup command checks for write access to given path
283 284 - fixed many issues with international characters and unicode. It uses utf8
284 285 decode with replace to provide less errors even with non utf8 encoded strings
285 286 - #125 added API KEY access to feeds
286 287 - #109 Repository can be created from external Mercurial link (aka. remote
287 288 repository, and manually updated (via pull) from admin panel
288 289 - beta git support - push/pull server + basic view for git repos
289 290 - added followers page and forks page
290 291 - server side file creation (with binary file upload interface)
291 292 and edition with commits powered by codemirror
292 293 - #111 file browser file finder, quick lookup files on whole file tree
293 294 - added quick login sliding menu into main page
294 295 - changelog uses lazy loading of affected files details, in some scenarios
295 296 this can improve speed of changelog page dramatically especially for
296 297 larger repositories.
297 298 - implements #214 added support for downloading subrepos in download menu.
298 299 - Added basic API for direct operations on rhodecode via JSON
299 300 - Implemented advanced hook management
300 301
301 302 fixes
302 303 +++++
303 304
304 305 - fixed file browser bug, when switching into given form revision the url was
305 306 not changing
306 307 - fixed propagation to error controller on simplehg and simplegit middlewares
307 308 - fixed error when trying to make a download on empty repository
308 309 - fixed problem with '[' chars in commit messages in journal
309 310 - fixed #99 Unicode errors, on file node paths with non utf-8 characters
310 311 - journal fork fixes
311 312 - removed issue with space inside renamed repository after deletion
312 313 - fixed strange issue on formencode imports
313 314 - fixed #126 Deleting repository on Windows, rename used incompatible chars.
314 315 - #150 fixes for errors on repositories mapped in db but corrupted in
315 316 filesystem
316 317 - fixed problem with ascendant characters in realm #181
317 318 - fixed problem with sqlite file based database connection pool
318 319 - whoosh indexer and code stats share the same dynamic extensions map
319 320 - fixes #188 - relationship delete of repo_to_perm entry on user removal
320 321 - fixes issue #189 Trending source files shows "show more" when no more exist
321 322 - fixes issue #197 Relative paths for pidlocks
322 323 - fixes issue #198 password will require only 3 chars now for login form
323 324 - fixes issue #199 wrong redirection for non admin users after creating a repository
324 325 - fixes issues #202, bad db constraint made impossible to attach same group
325 326 more than one time. Affects only mysql/postgres
326 327 - fixes #218 os.kill patch for windows was missing sig param
327 328 - improved rendering of dag (they are not trimmed anymore when number of
328 329 heads exceeds 5)
329 330
330 331 1.1.8 (**2011-04-12**)
331 332 ----------------------
332 333
333 334 news
334 335 ++++
335 336
336 337 - improved windows support
337 338
338 339 fixes
339 340 +++++
340 341
341 342 - fixed #140 freeze of python dateutil library, since new version is python2.x
342 343 incompatible
343 344 - setup-app will check for write permission in given path
344 345 - cleaned up license info issue #149
345 346 - fixes for issues #137,#116 and problems with unicode and accented characters.
346 347 - fixes crashes on gravatar, when passed in email as unicode
347 348 - fixed tooltip flickering problems
348 349 - fixed came_from redirection on windows
349 350 - fixed logging modules, and sql formatters
350 351 - windows fixes for os.kill issue #133
351 352 - fixes path splitting for windows issues #148
352 353 - fixed issue #143 wrong import on migration to 1.1.X
353 354 - fixed problems with displaying binary files, thanks to Thomas Waldmann
354 355 - removed name from archive files since it's breaking ui for long repo names
355 356 - fixed issue with archive headers sent to browser, thanks to Thomas Waldmann
356 357 - fixed compatibility for 1024px displays, and larger dpi settings, thanks to
357 358 Thomas Waldmann
358 359 - fixed issue #166 summary pager was skipping 10 revisions on second page
359 360
360 361
361 362 1.1.7 (**2011-03-23**)
362 363 ----------------------
363 364
364 365 news
365 366 ++++
366 367
367 368 fixes
368 369 +++++
369 370
370 371 - fixed (again) #136 installation support for FreeBSD
371 372
372 373
373 374 1.1.6 (**2011-03-21**)
374 375 ----------------------
375 376
376 377 news
377 378 ++++
378 379
379 380 fixes
380 381 +++++
381 382
382 383 - fixed #136 installation support for FreeBSD
383 384 - RhodeCode will check for python version during installation
384 385
385 386 1.1.5 (**2011-03-17**)
386 387 ----------------------
387 388
388 389 news
389 390 ++++
390 391
391 392 - basic windows support, by exchanging pybcrypt into sha256 for windows only
392 393 highly inspired by idea of mantis406
393 394
394 395 fixes
395 396 +++++
396 397
397 398 - fixed sorting by author in main page
398 399 - fixed crashes with diffs on binary files
399 400 - fixed #131 problem with boolean values for LDAP
400 401 - fixed #122 mysql problems thanks to striker69
401 402 - fixed problem with errors on calling raw/raw_files/annotate functions
402 403 with unknown revisions
403 404 - fixed returned rawfiles attachment names with international character
404 405 - cleaned out docs, big thanks to Jason Harris
405 406
406 407 1.1.4 (**2011-02-19**)
407 408 ----------------------
408 409
409 410 news
410 411 ++++
411 412
412 413 fixes
413 414 +++++
414 415
415 416 - fixed formencode import problem on settings page, that caused server crash
416 417 when that page was accessed as first after server start
417 418 - journal fixes
418 419 - fixed option to access repository just by entering http://server/<repo_name>
419 420
420 421 1.1.3 (**2011-02-16**)
421 422 ----------------------
422 423
423 424 news
424 425 ++++
425 426
426 427 - implemented #102 allowing the '.' character in username
427 428 - added option to access repository just by entering http://server/<repo_name>
428 429 - celery task ignores result for better performance
429 430
430 431 fixes
431 432 +++++
432 433
433 434 - fixed ehlo command and non auth mail servers on smtp_lib. Thanks to
434 435 apollo13 and Johan Walles
435 436 - small fixes in journal
436 437 - fixed problems with getting setting for celery from .ini files
437 438 - registration, password reset and login boxes share the same title as main
438 439 application now
439 440 - fixed #113: to high permissions to fork repository
440 441 - fixed problem with '[' chars in commit messages in journal
441 442 - removed issue with space inside renamed repository after deletion
442 443 - db transaction fixes when filesystem repository creation failed
443 444 - fixed #106 relation issues on databases different than sqlite
444 445 - fixed static files paths links to use of url() method
445 446
446 447 1.1.2 (**2011-01-12**)
447 448 ----------------------
448 449
449 450 news
450 451 ++++
451 452
452 453
453 454 fixes
454 455 +++++
455 456
456 457 - fixes #98 protection against float division of percentage stats
457 458 - fixed graph bug
458 459 - forced webhelpers version since it was making troubles during installation
459 460
460 461 1.1.1 (**2011-01-06**)
461 462 ----------------------
462 463
463 464 news
464 465 ++++
465 466
466 467 - added force https option into ini files for easier https usage (no need to
467 468 set server headers with this options)
468 469 - small css updates
469 470
470 471 fixes
471 472 +++++
472 473
473 474 - fixed #96 redirect loop on files view on repositories without changesets
474 475 - fixed #97 unicode string passed into server header in special cases (mod_wsgi)
475 476 and server crashed with errors
476 477 - fixed large tooltips problems on main page
477 478 - fixed #92 whoosh indexer is more error proof
478 479
479 480 1.1.0 (**2010-12-18**)
480 481 ----------------------
481 482
482 483 news
483 484 ++++
484 485
485 486 - rewrite of internals for vcs >=0.1.10
486 487 - uses mercurial 1.7 with dotencode disabled for maintaining compatibility
487 488 with older clients
488 489 - anonymous access, authentication via ldap
489 490 - performance upgrade for cached repos list - each repository has its own
490 491 cache that's invalidated when needed.
491 492 - performance upgrades on repositories with large amount of commits (20K+)
492 493 - main page quick filter for filtering repositories
493 494 - user dashboards with ability to follow chosen repositories actions
494 495 - sends email to admin on new user registration
495 496 - added cache/statistics reset options into repository settings
496 497 - more detailed action logger (based on hooks) with pushed changesets lists
497 498 and options to disable those hooks from admin panel
498 499 - introduced new enhanced changelog for merges that shows more accurate results
499 500 - new improved and faster code stats (based on pygments lexers mapping tables,
500 501 showing up to 10 trending sources for each repository. Additionally stats
501 502 can be disabled in repository settings.
502 503 - gui optimizations, fixed application width to 1024px
503 504 - added cut off (for large files/changesets) limit into config files
504 505 - whoosh, celeryd, upgrade moved to paster command
505 506 - other than sqlite database backends can be used
506 507
507 508 fixes
508 509 +++++
509 510
510 511 - fixes #61 forked repo was showing only after cache expired
511 512 - fixes #76 no confirmation on user deletes
512 513 - fixes #66 Name field misspelled
513 514 - fixes #72 block user removal when he owns repositories
514 515 - fixes #69 added password confirmation fields
515 516 - fixes #87 RhodeCode crashes occasionally on updating repository owner
516 517 - fixes #82 broken annotations on files with more than 1 blank line at the end
517 518 - a lot of fixes and tweaks for file browser
518 519 - fixed detached session issues
519 520 - fixed when user had no repos he would see all repos listed in my account
520 521 - fixed ui() instance bug when global hgrc settings was loaded for server
521 522 instance and all hgrc options were merged with our db ui() object
522 523 - numerous small bugfixes
523 524
524 525 (special thanks for TkSoh for detailed feedback)
525 526
526 527
527 528 1.0.2 (**2010-11-12**)
528 529 ----------------------
529 530
530 531 news
531 532 ++++
532 533
533 534 - tested under python2.7
534 535 - bumped sqlalchemy and celery versions
535 536
536 537 fixes
537 538 +++++
538 539
539 540 - fixed #59 missing graph.js
540 541 - fixed repo_size crash when repository had broken symlinks
541 542 - fixed python2.5 crashes.
542 543
543 544
544 545 1.0.1 (**2010-11-10**)
545 546 ----------------------
546 547
547 548 news
548 549 ++++
549 550
550 551 - small css updated
551 552
552 553 fixes
553 554 +++++
554 555
555 556 - fixed #53 python2.5 incompatible enumerate calls
556 557 - fixed #52 disable mercurial extension for web
557 558 - fixed #51 deleting repositories don't delete it's dependent objects
558 559
559 560
560 561 1.0.0 (**2010-11-02**)
561 562 ----------------------
562 563
563 564 - security bugfix simplehg wasn't checking for permissions on commands
564 565 other than pull or push.
565 566 - fixed doubled messages after push or pull in admin journal
566 567 - templating and css corrections, fixed repo switcher on chrome, updated titles
567 568 - admin menu accessible from options menu on repository view
568 569 - permissions cached queries
569 570
570 571 1.0.0rc4 (**2010-10-12**)
571 572 --------------------------
572 573
573 574 - fixed python2.5 missing simplejson imports (thanks to Jens BΓ€ckman)
574 575 - removed cache_manager settings from sqlalchemy meta
575 576 - added sqlalchemy cache settings to ini files
576 577 - validated password length and added second try of failure on paster setup-app
577 578 - fixed setup database destroy prompt even when there was no db
578 579
579 580
580 581 1.0.0rc3 (**2010-10-11**)
581 582 -------------------------
582 583
583 584 - fixed i18n during installation.
584 585
585 586 1.0.0rc2 (**2010-10-11**)
586 587 -------------------------
587 588
588 589 - Disabled dirsize in file browser, it's causing nasty bug when dir renames
589 590 occure. After vcs is fixed it'll be put back again.
590 591 - templating/css rewrites, optimized css. No newline at end of file
@@ -1,561 +1,588 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
29 29 from pylons import url
30 30 from pylons.i18n.translation import _
31 31
32 32 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
33 33 from rhodecode.lib.caching_query import FromCache
34 34
35 35 from rhodecode.model import BaseModel
36 36 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
37 37 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
38 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup
38 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroup,\
39 UsersGroupRepoGroupToPerm
39 40 from rhodecode.lib.exceptions import DefaultUserException, \
40 41 UserOwnsReposException
41 42
42 43 from sqlalchemy.exc import DatabaseError
43 44
44 45 from sqlalchemy.orm import joinedload
45 46
46 47 log = logging.getLogger(__name__)
47 48
48 49
49 50 PERM_WEIGHTS = {
50 51 'repository.none': 0,
51 52 'repository.read': 1,
52 53 'repository.write': 3,
53 54 'repository.admin': 4,
54 55 'group.none': 0,
55 56 'group.read': 1,
56 57 'group.write': 3,
57 58 'group.admin': 4,
58 59 }
59 60
60 61
61 62 class UserModel(BaseModel):
62 63
63 64 def __get_user(self, user):
64 65 return self._get_instance(User, user, callback=User.get_by_username)
65 66
66 67 def __get_perm(self, permission):
67 68 return self._get_instance(Permission, permission,
68 69 callback=Permission.get_by_key)
69 70
70 71 def get(self, user_id, cache=False):
71 72 user = self.sa.query(User)
72 73 if cache:
73 74 user = user.options(FromCache("sql_cache_short",
74 75 "get_user_%s" % user_id))
75 76 return user.get(user_id)
76 77
77 78 def get_user(self, user):
78 79 return self.__get_user(user)
79 80
80 81 def get_by_username(self, username, cache=False, case_insensitive=False):
81 82
82 83 if case_insensitive:
83 84 user = self.sa.query(User).filter(User.username.ilike(username))
84 85 else:
85 86 user = self.sa.query(User)\
86 87 .filter(User.username == username)
87 88 if cache:
88 89 user = user.options(FromCache("sql_cache_short",
89 90 "get_user_%s" % username))
90 91 return user.scalar()
91 92
92 93 def get_by_api_key(self, api_key, cache=False):
93 94 return User.get_by_api_key(api_key, cache)
94 95
95 96 def create(self, form_data):
96 97 try:
97 98 new_user = User()
98 99 for k, v in form_data.items():
99 100 setattr(new_user, k, v)
100 101
101 102 new_user.api_key = generate_api_key(form_data['username'])
102 103 self.sa.add(new_user)
103 104 return new_user
104 105 except:
105 106 log.error(traceback.format_exc())
106 107 raise
107 108
108 109 def create_or_update(self, username, password, email, name, lastname,
109 110 active=True, admin=False, ldap_dn=None):
110 111 """
111 112 Creates a new instance if not found, or updates current one
112 113
113 114 :param username:
114 115 :param password:
115 116 :param email:
116 117 :param active:
117 118 :param name:
118 119 :param lastname:
119 120 :param active:
120 121 :param admin:
121 122 :param ldap_dn:
122 123 """
123 124
124 125 from rhodecode.lib.auth import get_crypt_password
125 126
126 127 log.debug('Checking for %s account in RhodeCode database' % username)
127 128 user = User.get_by_username(username, case_insensitive=True)
128 129 if user is None:
129 130 log.debug('creating new user %s' % username)
130 131 new_user = User()
131 132 else:
132 133 log.debug('updating user %s' % username)
133 134 new_user = user
134 135
135 136 try:
136 137 new_user.username = username
137 138 new_user.admin = admin
138 139 new_user.password = get_crypt_password(password)
139 140 new_user.api_key = generate_api_key(username)
140 141 new_user.email = email
141 142 new_user.active = active
142 143 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
143 144 new_user.name = name
144 145 new_user.lastname = lastname
145 146 self.sa.add(new_user)
146 147 return new_user
147 148 except (DatabaseError,):
148 149 log.error(traceback.format_exc())
149 150 raise
150 151
151 152 def create_for_container_auth(self, username, attrs):
152 153 """
153 154 Creates the given user if it's not already in the database
154 155
155 156 :param username:
156 157 :param attrs:
157 158 """
158 159 if self.get_by_username(username, case_insensitive=True) is None:
159 160
160 161 # autogenerate email for container account without one
161 162 generate_email = lambda usr: '%s@container_auth.account' % usr
162 163
163 164 try:
164 165 new_user = User()
165 166 new_user.username = username
166 167 new_user.password = None
167 168 new_user.api_key = generate_api_key(username)
168 169 new_user.email = attrs['email']
169 170 new_user.active = attrs.get('active', True)
170 171 new_user.name = attrs['name'] or generate_email(username)
171 172 new_user.lastname = attrs['lastname']
172 173
173 174 self.sa.add(new_user)
174 175 return new_user
175 176 except (DatabaseError,):
176 177 log.error(traceback.format_exc())
177 178 self.sa.rollback()
178 179 raise
179 180 log.debug('User %s already exists. Skipping creation of account'
180 181 ' for container auth.', username)
181 182 return None
182 183
183 184 def create_ldap(self, username, password, user_dn, attrs):
184 185 """
185 186 Checks if user is in database, if not creates this user marked
186 187 as ldap user
187 188
188 189 :param username:
189 190 :param password:
190 191 :param user_dn:
191 192 :param attrs:
192 193 """
193 194 from rhodecode.lib.auth import get_crypt_password
194 195 log.debug('Checking for such ldap account in RhodeCode database')
195 196 if self.get_by_username(username, case_insensitive=True) is None:
196 197
197 198 # autogenerate email for ldap account without one
198 199 generate_email = lambda usr: '%s@ldap.account' % usr
199 200
200 201 try:
201 202 new_user = User()
202 203 username = username.lower()
203 204 # add ldap account always lowercase
204 205 new_user.username = username
205 206 new_user.password = get_crypt_password(password)
206 207 new_user.api_key = generate_api_key(username)
207 208 new_user.email = attrs['email'] or generate_email(username)
208 209 new_user.active = attrs.get('active', True)
209 210 new_user.ldap_dn = safe_unicode(user_dn)
210 211 new_user.name = attrs['name']
211 212 new_user.lastname = attrs['lastname']
212 213
213 214 self.sa.add(new_user)
214 215 return new_user
215 216 except (DatabaseError,):
216 217 log.error(traceback.format_exc())
217 218 self.sa.rollback()
218 219 raise
219 220 log.debug('this %s user exists skipping creation of ldap account',
220 221 username)
221 222 return None
222 223
223 224 def create_registration(self, form_data):
224 225 from rhodecode.model.notification import NotificationModel
225 226
226 227 try:
227 228 new_user = User()
228 229 for k, v in form_data.items():
229 230 if k != 'admin':
230 231 setattr(new_user, k, v)
231 232
232 233 self.sa.add(new_user)
233 234 self.sa.flush()
234 235
235 236 # notification to admins
236 237 subject = _('new user registration')
237 238 body = ('New user registration\n'
238 239 '---------------------\n'
239 240 '- Username: %s\n'
240 241 '- Full Name: %s\n'
241 242 '- Email: %s\n')
242 243 body = body % (new_user.username, new_user.full_name,
243 244 new_user.email)
244 245 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
245 246 kw = {'registered_user_url': edit_url}
246 247 NotificationModel().create(created_by=new_user, subject=subject,
247 248 body=body, recipients=None,
248 249 type_=Notification.TYPE_REGISTRATION,
249 250 email_kwargs=kw)
250 251
251 252 except:
252 253 log.error(traceback.format_exc())
253 254 raise
254 255
255 256 def update(self, user_id, form_data):
256 257 try:
257 258 user = self.get(user_id, cache=False)
258 259 if user.username == 'default':
259 260 raise DefaultUserException(
260 261 _("You can't Edit this user since it's"
261 262 " crucial for entire application"))
262 263
263 264 for k, v in form_data.items():
264 265 if k == 'new_password' and v != '':
265 266 user.password = v
266 267 user.api_key = generate_api_key(user.username)
267 268 else:
268 269 setattr(user, k, v)
269 270
270 271 self.sa.add(user)
271 272 except:
272 273 log.error(traceback.format_exc())
273 274 raise
274 275
275 276 def update_my_account(self, user_id, form_data):
276 277 try:
277 278 user = self.get(user_id, cache=False)
278 279 if user.username == 'default':
279 280 raise DefaultUserException(
280 281 _("You can't Edit this user since it's"
281 282 " crucial for entire application"))
282 283 for k, v in form_data.items():
283 284 if k == 'new_password' and v != '':
284 285 user.password = v
285 286 user.api_key = generate_api_key(user.username)
286 287 else:
287 288 if k not in ['admin', 'active']:
288 289 setattr(user, k, v)
289 290
290 291 self.sa.add(user)
291 292 except:
292 293 log.error(traceback.format_exc())
293 294 raise
294 295
295 296 def delete(self, user):
296 297 user = self.__get_user(user)
297 298
298 299 try:
299 300 if user.username == 'default':
300 301 raise DefaultUserException(
301 302 _("You can't remove this user since it's"
302 303 " crucial for entire application")
303 304 )
304 305 if user.repositories:
305 306 raise UserOwnsReposException(
306 307 _('user "%s" still owns %s repositories and cannot be '
307 308 'removed. Switch owners or remove those repositories')
308 309 % (user.username, user.repositories)
309 310 )
310 311 self.sa.delete(user)
311 312 except:
312 313 log.error(traceback.format_exc())
313 314 raise
314 315
315 316 def reset_password_link(self, data):
316 317 from rhodecode.lib.celerylib import tasks, run_task
317 318 run_task(tasks.send_password_link, data['email'])
318 319
319 320 def reset_password(self, data):
320 321 from rhodecode.lib.celerylib import tasks, run_task
321 322 run_task(tasks.reset_user_password, data['email'])
322 323
323 324 def fill_data(self, auth_user, user_id=None, api_key=None):
324 325 """
325 326 Fetches auth_user by user_id,or api_key if present.
326 327 Fills auth_user attributes with those taken from database.
327 328 Additionally set's is_authenitated if lookup fails
328 329 present in database
329 330
330 331 :param auth_user: instance of user to set attributes
331 332 :param user_id: user id to fetch by
332 333 :param api_key: api key to fetch by
333 334 """
334 335 if user_id is None and api_key is None:
335 336 raise Exception('You need to pass user_id or api_key')
336 337
337 338 try:
338 339 if api_key:
339 340 dbuser = self.get_by_api_key(api_key)
340 341 else:
341 342 dbuser = self.get(user_id)
342 343
343 344 if dbuser is not None and dbuser.active:
344 345 log.debug('filling %s data' % dbuser)
345 346 for k, v in dbuser.get_dict().items():
346 347 setattr(auth_user, k, v)
347 348 else:
348 349 return False
349 350
350 351 except:
351 352 log.error(traceback.format_exc())
352 353 auth_user.is_authenticated = False
353 354 return False
354 355
355 356 return True
356 357
357 358 def fill_perms(self, user):
358 359 """
359 360 Fills user permission attribute with permissions taken from database
360 361 works for permissions given for repositories, and for permissions that
361 362 are granted to groups
362 363
363 364 :param user: user instance to fill his perms
364 365 """
365 366 RK = 'repositories'
366 367 GK = 'repositories_groups'
367 368 GLOBAL = 'global'
368 369 user.permissions[RK] = {}
369 370 user.permissions[GK] = {}
370 371 user.permissions[GLOBAL] = set()
371 372
372 373 #======================================================================
373 374 # fetch default permissions
374 375 #======================================================================
375 376 default_user = User.get_by_username('default', cache=True)
376 377 default_user_id = default_user.user_id
377 378
378 379 default_repo_perms = Permission.get_default_perms(default_user_id)
379 380 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
380 381
381 382 if user.is_admin:
382 383 #==================================================================
383 384 # admin user have all default rights for repositories
384 385 # and groups set to admin
385 386 #==================================================================
386 387 user.permissions[GLOBAL].add('hg.admin')
387 388
388 389 # repositories
389 390 for perm in default_repo_perms:
390 391 r_k = perm.UserRepoToPerm.repository.repo_name
391 392 p = 'repository.admin'
392 393 user.permissions[RK][r_k] = p
393 394
394 395 # repositories groups
395 396 for perm in default_repo_groups_perms:
396 397 rg_k = perm.UserRepoGroupToPerm.group.group_name
397 398 p = 'group.admin'
398 399 user.permissions[GK][rg_k] = p
399 400
400 401 else:
401 402 #==================================================================
402 403 # set default permissions first for repositories and groups
403 404 #==================================================================
404 405 uid = user.user_id
405 406
406 407 # default global permissions
407 408 default_global_perms = self.sa.query(UserToPerm)\
408 409 .filter(UserToPerm.user_id == default_user_id)
409 410
410 411 for perm in default_global_perms:
411 412 user.permissions[GLOBAL].add(perm.permission.permission_name)
412 413
413 # default for repositories
414 # defaults for repositories, taken from default user
414 415 for perm in default_repo_perms:
415 416 r_k = perm.UserRepoToPerm.repository.repo_name
416 417 if perm.Repository.private and not (perm.Repository.user_id == uid):
417 418 # disable defaults for private repos,
418 419 p = 'repository.none'
419 420 elif perm.Repository.user_id == uid:
420 421 # set admin if owner
421 422 p = 'repository.admin'
422 423 else:
423 424 p = perm.Permission.permission_name
424 425
425 426 user.permissions[RK][r_k] = p
426 427
427 # default for repositories groups
428 # defaults for repositories groups taken from default user permission
429 # on given group
428 430 for perm in default_repo_groups_perms:
429 431 rg_k = perm.UserRepoGroupToPerm.group.group_name
430 432 p = perm.Permission.permission_name
431 433 user.permissions[GK][rg_k] = p
432 434
433 435 #==================================================================
434 # overwrite default with user permissions if any
436 # overwrite defaults with user permissions if any found
435 437 #==================================================================
436 438
437 # user global
439 # user global permissions
438 440 user_perms = self.sa.query(UserToPerm)\
439 441 .options(joinedload(UserToPerm.permission))\
440 442 .filter(UserToPerm.user_id == uid).all()
441 443
442 444 for perm in user_perms:
443 445 user.permissions[GLOBAL].add(perm.permission.permission_name)
444 446
445 # user repositories
447 # user explicit permissions for repositories
446 448 user_repo_perms = \
447 449 self.sa.query(UserRepoToPerm, Permission, Repository)\
448 450 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
449 451 .join((Permission, UserRepoToPerm.permission_id == Permission.permission_id))\
450 452 .filter(UserRepoToPerm.user_id == uid)\
451 453 .all()
452 454
453 455 for perm in user_repo_perms:
454 456 # set admin if owner
455 457 r_k = perm.UserRepoToPerm.repository.repo_name
456 458 if perm.Repository.user_id == uid:
457 459 p = 'repository.admin'
458 460 else:
459 461 p = perm.Permission.permission_name
460 462 user.permissions[RK][r_k] = p
461 463
462 464 #==================================================================
463 # check if user is part of groups for this repository and fill in
464 # (or replace with higher) permissions
465 # check if user is part of user groups for this repository and
466 # fill in (or replace with higher) permissions
465 467 #==================================================================
466 468
467 469 # users group global
468 470 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
469 471 .options(joinedload(UsersGroupToPerm.permission))\
470 472 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
471 473 UsersGroupMember.users_group_id))\
472 474 .filter(UsersGroupMember.user_id == uid).all()
473 475
474 476 for perm in user_perms_from_users_groups:
475 477 user.permissions[GLOBAL].add(perm.permission.permission_name)
476 478
477 # users group repositories
479 # users group for repositories permissions
478 480 user_repo_perms_from_users_groups = \
479 481 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
480 482 .join((Repository, UsersGroupRepoToPerm.repository_id == Repository.repo_id))\
481 483 .join((Permission, UsersGroupRepoToPerm.permission_id == Permission.permission_id))\
482 484 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id == UsersGroupMember.users_group_id))\
483 485 .filter(UsersGroupMember.user_id == uid)\
484 486 .all()
485 487
486 488 for perm in user_repo_perms_from_users_groups:
487 489 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
488 490 p = perm.Permission.permission_name
489 491 cur_perm = user.permissions[RK][r_k]
490 492 # overwrite permission only if it's greater than permission
491 493 # given from other sources
492 494 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
493 495 user.permissions[RK][r_k] = p
494 496
495 497 #==================================================================
496 498 # get access for this user for repos group and override defaults
497 499 #==================================================================
498 500
499 # user repositories groups
501 # user explicit permissions for repository
500 502 user_repo_groups_perms = \
501 503 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
502 504 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
503 505 .join((Permission, UserRepoGroupToPerm.permission_id == Permission.permission_id))\
504 506 .filter(UserRepoGroupToPerm.user_id == uid)\
505 507 .all()
506 508
507 509 for perm in user_repo_groups_perms:
508 510 rg_k = perm.UserRepoGroupToPerm.group.group_name
509 511 p = perm.Permission.permission_name
510 512 cur_perm = user.permissions[GK][rg_k]
511 513 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
512 514 user.permissions[GK][rg_k] = p
515
516 #==================================================================
517 # check if user is part of user groups for this repo group and
518 # fill in (or replace with higher) permissions
519 #==================================================================
520
521 # users group for repositories permissions
522 user_repo_group_perms_from_users_groups = \
523 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
524 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
525 .join((Permission, UsersGroupRepoGroupToPerm.permission_id == Permission.permission_id))\
526 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id == UsersGroupMember.users_group_id))\
527 .filter(UsersGroupMember.user_id == uid)\
528 .all()
529
530 for perm in user_repo_group_perms_from_users_groups:
531 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
532 print perm, g_k
533 p = perm.Permission.permission_name
534 cur_perm = user.permissions[GK][g_k]
535 # overwrite permission only if it's greater than permission
536 # given from other sources
537 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
538 user.permissions[GK][g_k] = p
539
513 540 return user
514 541
515 542 def has_perm(self, user, perm):
516 543 if not isinstance(perm, Permission):
517 544 raise Exception('perm needs to be an instance of Permission class '
518 545 'got %s instead' % type(perm))
519 546
520 547 user = self.__get_user(user)
521 548
522 549 return UserToPerm.query().filter(UserToPerm.user == user)\
523 550 .filter(UserToPerm.permission == perm).scalar() is not None
524 551
525 552 def grant_perm(self, user, perm):
526 553 """
527 554 Grant user global permissions
528 555
529 556 :param user:
530 557 :param perm:
531 558 """
532 559 user = self.__get_user(user)
533 560 perm = self.__get_perm(perm)
534 561 # if this permission is already granted skip it
535 562 _perm = UserToPerm.query()\
536 563 .filter(UserToPerm.user == user)\
537 564 .filter(UserToPerm.permission == perm)\
538 565 .scalar()
539 566 if _perm:
540 567 return
541 568 new = UserToPerm()
542 569 new.user = user
543 570 new.permission = perm
544 571 self.sa.add(new)
545 572
546 573 def revoke_perm(self, user, perm):
547 574 """
548 575 Revoke users global permissions
549 576
550 577 :param user:
551 578 :param perm:
552 579 """
553 580 user = self.__get_user(user)
554 581 perm = self.__get_perm(perm)
555 582
556 583 obj = UserToPerm.query()\
557 584 .filter(UserToPerm.user == user)\
558 585 .filter(UserToPerm.permission == perm)\
559 586 .scalar()
560 587 if obj:
561 588 self.sa.delete(obj)
@@ -1,660 +1,715 b''
1 1 import os
2 2 import unittest
3 3 from rhodecode.tests import *
4 4
5 5 from rhodecode.model.repos_group import ReposGroupModel
6 6 from rhodecode.model.repo import RepoModel
7 7 from rhodecode.model.db import RepoGroup, User, Notification, UserNotification, \
8 UsersGroup, UsersGroupMember, Permission
8 UsersGroup, UsersGroupMember, Permission, UsersGroupRepoGroupToPerm
9 9 from sqlalchemy.exc import IntegrityError
10 10 from rhodecode.model.user import UserModel
11 11
12 12 from rhodecode.model.meta import Session
13 13 from rhodecode.model.notification import NotificationModel
14 14 from rhodecode.model.users_group import UsersGroupModel
15 15 from rhodecode.lib.auth import AuthUser
16 16
17 17
18 18 def _make_group(path, desc='desc', parent_id=None,
19 19 skip_if_exists=False):
20 20
21 21 gr = RepoGroup.get_by_group_name(path)
22 22 if gr and skip_if_exists:
23 23 return gr
24 24
25 25 gr = ReposGroupModel().create(path, desc, parent_id)
26 26 return gr
27 27
28 28
29 29 class TestReposGroups(unittest.TestCase):
30 30
31 31 def setUp(self):
32 32 self.g1 = _make_group('test1', skip_if_exists=True)
33 33 Session.commit()
34 34 self.g2 = _make_group('test2', skip_if_exists=True)
35 35 Session.commit()
36 36 self.g3 = _make_group('test3', skip_if_exists=True)
37 37 Session.commit()
38 38
39 39 def tearDown(self):
40 40 print 'out'
41 41
42 42 def __check_path(self, *path):
43 43 """
44 44 Checks the path for existance !
45 45 """
46 46 path = [TESTS_TMP_PATH] + list(path)
47 47 path = os.path.join(*path)
48 48 return os.path.isdir(path)
49 49
50 50 def _check_folders(self):
51 51 print os.listdir(TESTS_TMP_PATH)
52 52
53 53 def __delete_group(self, id_):
54 54 ReposGroupModel().delete(id_)
55 55
56 56 def __update_group(self, id_, path, desc='desc', parent_id=None):
57 57 form_data = dict(
58 58 group_name=path,
59 59 group_description=desc,
60 60 group_parent_id=parent_id,
61 61 perms_updates=[],
62 62 perms_new=[]
63 63 )
64 64 gr = ReposGroupModel().update(id_, form_data)
65 65 return gr
66 66
67 67 def test_create_group(self):
68 68 g = _make_group('newGroup')
69 69 self.assertEqual(g.full_path, 'newGroup')
70 70
71 71 self.assertTrue(self.__check_path('newGroup'))
72 72
73 73 def test_create_same_name_group(self):
74 74 self.assertRaises(IntegrityError, lambda:_make_group('newGroup'))
75 75 Session.rollback()
76 76
77 77 def test_same_subgroup(self):
78 78 sg1 = _make_group('sub1', parent_id=self.g1.group_id)
79 79 self.assertEqual(sg1.parent_group, self.g1)
80 80 self.assertEqual(sg1.full_path, 'test1/sub1')
81 81 self.assertTrue(self.__check_path('test1', 'sub1'))
82 82
83 83 ssg1 = _make_group('subsub1', parent_id=sg1.group_id)
84 84 self.assertEqual(ssg1.parent_group, sg1)
85 85 self.assertEqual(ssg1.full_path, 'test1/sub1/subsub1')
86 86 self.assertTrue(self.__check_path('test1', 'sub1', 'subsub1'))
87 87
88 88 def test_remove_group(self):
89 89 sg1 = _make_group('deleteme')
90 90 self.__delete_group(sg1.group_id)
91 91
92 92 self.assertEqual(RepoGroup.get(sg1.group_id), None)
93 93 self.assertFalse(self.__check_path('deteteme'))
94 94
95 95 sg1 = _make_group('deleteme', parent_id=self.g1.group_id)
96 96 self.__delete_group(sg1.group_id)
97 97
98 98 self.assertEqual(RepoGroup.get(sg1.group_id), None)
99 99 self.assertFalse(self.__check_path('test1', 'deteteme'))
100 100
101 101 def test_rename_single_group(self):
102 102 sg1 = _make_group('initial')
103 103
104 104 new_sg1 = self.__update_group(sg1.group_id, 'after')
105 105 self.assertTrue(self.__check_path('after'))
106 106 self.assertEqual(RepoGroup.get_by_group_name('initial'), None)
107 107
108 108 def test_update_group_parent(self):
109 109
110 110 sg1 = _make_group('initial', parent_id=self.g1.group_id)
111 111
112 112 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g1.group_id)
113 113 self.assertTrue(self.__check_path('test1', 'after'))
114 114 self.assertEqual(RepoGroup.get_by_group_name('test1/initial'), None)
115 115
116 116 new_sg1 = self.__update_group(sg1.group_id, 'after', parent_id=self.g3.group_id)
117 117 self.assertTrue(self.__check_path('test3', 'after'))
118 118 self.assertEqual(RepoGroup.get_by_group_name('test3/initial'), None)
119 119
120 120 new_sg1 = self.__update_group(sg1.group_id, 'hello')
121 121 self.assertTrue(self.__check_path('hello'))
122 122
123 123 self.assertEqual(RepoGroup.get_by_group_name('hello'), new_sg1)
124 124
125 125 def test_subgrouping_with_repo(self):
126 126
127 127 g1 = _make_group('g1')
128 128 g2 = _make_group('g2')
129 129
130 130 # create new repo
131 131 form_data = dict(repo_name='john',
132 132 repo_name_full='john',
133 133 fork_name=None,
134 134 description=None,
135 135 repo_group=None,
136 136 private=False,
137 137 repo_type='hg',
138 138 clone_uri=None)
139 139 cur_user = User.get_by_username(TEST_USER_ADMIN_LOGIN)
140 140 r = RepoModel().create(form_data, cur_user)
141 141
142 142 self.assertEqual(r.repo_name, 'john')
143 143
144 144 # put repo into group
145 145 form_data = form_data
146 146 form_data['repo_group'] = g1.group_id
147 147 form_data['perms_new'] = []
148 148 form_data['perms_updates'] = []
149 149 RepoModel().update(r.repo_name, form_data)
150 150 self.assertEqual(r.repo_name, 'g1/john')
151 151
152 152 self.__update_group(g1.group_id, 'g1', parent_id=g2.group_id)
153 153 self.assertTrue(self.__check_path('g2', 'g1'))
154 154
155 155 # test repo
156 156 self.assertEqual(r.repo_name, os.path.join('g2', 'g1', r.just_name))
157 157
158 158
159 159 def test_move_to_root(self):
160 160 g1 = _make_group('t11')
161 161 Session.commit()
162 162 g2 = _make_group('t22',parent_id=g1.group_id)
163 163 Session.commit()
164 164
165 165 self.assertEqual(g2.full_path,'t11/t22')
166 166 self.assertTrue(self.__check_path('t11', 't22'))
167 167
168 168 g2 = self.__update_group(g2.group_id, 'g22', parent_id=None)
169 169 Session.commit()
170 170
171 171 self.assertEqual(g2.group_name,'g22')
172 172 # we moved out group from t1 to '' so it's full path should be 'g2'
173 173 self.assertEqual(g2.full_path,'g22')
174 174 self.assertFalse(self.__check_path('t11', 't22'))
175 175 self.assertTrue(self.__check_path('g22'))
176 176
177 177
178 178 class TestUser(unittest.TestCase):
179 179 def __init__(self, methodName='runTest'):
180 180 Session.remove()
181 181 super(TestUser, self).__init__(methodName=methodName)
182 182
183 183 def test_create_and_remove(self):
184 184 usr = UserModel().create_or_update(username=u'test_user', password=u'qweqwe',
185 185 email=u'u232@rhodecode.org',
186 186 name=u'u1', lastname=u'u1')
187 187 Session.commit()
188 188 self.assertEqual(User.get_by_username(u'test_user'), usr)
189 189
190 190 # make users group
191 191 users_group = UsersGroupModel().create('some_example_group')
192 192 Session.commit()
193 193
194 194 UsersGroupModel().add_user_to_group(users_group, usr)
195 195 Session.commit()
196 196
197 197 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
198 198 self.assertEqual(UsersGroupMember.query().count(), 1)
199 199 UserModel().delete(usr.user_id)
200 200 Session.commit()
201 201
202 202 self.assertEqual(UsersGroupMember.query().all(), [])
203 203
204 204
205 205 class TestNotifications(unittest.TestCase):
206 206
207 207 def __init__(self, methodName='runTest'):
208 208 Session.remove()
209 209 self.u1 = UserModel().create_or_update(username=u'u1',
210 210 password=u'qweqwe',
211 211 email=u'u1@rhodecode.org',
212 212 name=u'u1', lastname=u'u1')
213 213 Session.commit()
214 214 self.u1 = self.u1.user_id
215 215
216 216 self.u2 = UserModel().create_or_update(username=u'u2',
217 217 password=u'qweqwe',
218 218 email=u'u2@rhodecode.org',
219 219 name=u'u2', lastname=u'u3')
220 220 Session.commit()
221 221 self.u2 = self.u2.user_id
222 222
223 223 self.u3 = UserModel().create_or_update(username=u'u3',
224 224 password=u'qweqwe',
225 225 email=u'u3@rhodecode.org',
226 226 name=u'u3', lastname=u'u3')
227 227 Session.commit()
228 228 self.u3 = self.u3.user_id
229 229
230 230 super(TestNotifications, self).__init__(methodName=methodName)
231 231
232 232 def _clean_notifications(self):
233 233 for n in Notification.query().all():
234 234 Session.delete(n)
235 235
236 236 Session.commit()
237 237 self.assertEqual(Notification.query().all(), [])
238 238
239 239 def tearDown(self):
240 240 self._clean_notifications()
241 241
242 242 def test_create_notification(self):
243 243 self.assertEqual([], Notification.query().all())
244 244 self.assertEqual([], UserNotification.query().all())
245 245
246 246 usrs = [self.u1, self.u2]
247 247 notification = NotificationModel().create(created_by=self.u1,
248 248 subject=u'subj', body=u'hi there',
249 249 recipients=usrs)
250 250 Session.commit()
251 251 u1 = User.get(self.u1)
252 252 u2 = User.get(self.u2)
253 253 u3 = User.get(self.u3)
254 254 notifications = Notification.query().all()
255 255 self.assertEqual(len(notifications), 1)
256 256
257 257 unotification = UserNotification.query()\
258 258 .filter(UserNotification.notification == notification).all()
259 259
260 260 self.assertEqual(notifications[0].recipients, [u1, u2])
261 261 self.assertEqual(notification.notification_id,
262 262 notifications[0].notification_id)
263 263 self.assertEqual(len(unotification), len(usrs))
264 264 self.assertEqual([x.user.user_id for x in unotification], usrs)
265 265
266 266 def test_user_notifications(self):
267 267 self.assertEqual([], Notification.query().all())
268 268 self.assertEqual([], UserNotification.query().all())
269 269
270 270 notification1 = NotificationModel().create(created_by=self.u1,
271 271 subject=u'subj', body=u'hi there1',
272 272 recipients=[self.u3])
273 273 Session.commit()
274 274 notification2 = NotificationModel().create(created_by=self.u1,
275 275 subject=u'subj', body=u'hi there2',
276 276 recipients=[self.u3])
277 277 Session.commit()
278 278 u3 = Session.query(User).get(self.u3)
279 279
280 280 self.assertEqual(sorted([x.notification for x in u3.notifications]),
281 281 sorted([notification2, notification1]))
282 282
283 283 def test_delete_notifications(self):
284 284 self.assertEqual([], Notification.query().all())
285 285 self.assertEqual([], UserNotification.query().all())
286 286
287 287 notification = NotificationModel().create(created_by=self.u1,
288 288 subject=u'title', body=u'hi there3',
289 289 recipients=[self.u3, self.u1, self.u2])
290 290 Session.commit()
291 291 notifications = Notification.query().all()
292 292 self.assertTrue(notification in notifications)
293 293
294 294 Notification.delete(notification.notification_id)
295 295 Session.commit()
296 296
297 297 notifications = Notification.query().all()
298 298 self.assertFalse(notification in notifications)
299 299
300 300 un = UserNotification.query().filter(UserNotification.notification
301 301 == notification).all()
302 302 self.assertEqual(un, [])
303 303
304 304 def test_delete_association(self):
305 305
306 306 self.assertEqual([], Notification.query().all())
307 307 self.assertEqual([], UserNotification.query().all())
308 308
309 309 notification = NotificationModel().create(created_by=self.u1,
310 310 subject=u'title', body=u'hi there3',
311 311 recipients=[self.u3, self.u1, self.u2])
312 312 Session.commit()
313 313
314 314 unotification = UserNotification.query()\
315 315 .filter(UserNotification.notification ==
316 316 notification)\
317 317 .filter(UserNotification.user_id == self.u3)\
318 318 .scalar()
319 319
320 320 self.assertEqual(unotification.user_id, self.u3)
321 321
322 322 NotificationModel().delete(self.u3,
323 323 notification.notification_id)
324 324 Session.commit()
325 325
326 326 u3notification = UserNotification.query()\
327 327 .filter(UserNotification.notification ==
328 328 notification)\
329 329 .filter(UserNotification.user_id == self.u3)\
330 330 .scalar()
331 331
332 332 self.assertEqual(u3notification, None)
333 333
334 334 # notification object is still there
335 335 self.assertEqual(Notification.query().all(), [notification])
336 336
337 337 #u1 and u2 still have assignments
338 338 u1notification = UserNotification.query()\
339 339 .filter(UserNotification.notification ==
340 340 notification)\
341 341 .filter(UserNotification.user_id == self.u1)\
342 342 .scalar()
343 343 self.assertNotEqual(u1notification, None)
344 344 u2notification = UserNotification.query()\
345 345 .filter(UserNotification.notification ==
346 346 notification)\
347 347 .filter(UserNotification.user_id == self.u2)\
348 348 .scalar()
349 349 self.assertNotEqual(u2notification, None)
350 350
351 351 def test_notification_counter(self):
352 352 self._clean_notifications()
353 353 self.assertEqual([], Notification.query().all())
354 354 self.assertEqual([], UserNotification.query().all())
355 355
356 356 NotificationModel().create(created_by=self.u1,
357 357 subject=u'title', body=u'hi there_delete',
358 358 recipients=[self.u3, self.u1])
359 359 Session.commit()
360 360
361 361 self.assertEqual(NotificationModel()
362 362 .get_unread_cnt_for_user(self.u1), 1)
363 363 self.assertEqual(NotificationModel()
364 364 .get_unread_cnt_for_user(self.u2), 0)
365 365 self.assertEqual(NotificationModel()
366 366 .get_unread_cnt_for_user(self.u3), 1)
367 367
368 368 notification = NotificationModel().create(created_by=self.u1,
369 369 subject=u'title', body=u'hi there3',
370 370 recipients=[self.u3, self.u1, self.u2])
371 371 Session.commit()
372 372
373 373 self.assertEqual(NotificationModel()
374 374 .get_unread_cnt_for_user(self.u1), 2)
375 375 self.assertEqual(NotificationModel()
376 376 .get_unread_cnt_for_user(self.u2), 1)
377 377 self.assertEqual(NotificationModel()
378 378 .get_unread_cnt_for_user(self.u3), 2)
379 379
380 380
381 381 class TestUsers(unittest.TestCase):
382 382
383 383 def __init__(self, methodName='runTest'):
384 384 super(TestUsers, self).__init__(methodName=methodName)
385 385
386 386 def setUp(self):
387 387 self.u1 = UserModel().create_or_update(username=u'u1',
388 388 password=u'qweqwe',
389 389 email=u'u1@rhodecode.org',
390 390 name=u'u1', lastname=u'u1')
391 391
392 392 def tearDown(self):
393 393 perm = Permission.query().all()
394 394 for p in perm:
395 395 UserModel().revoke_perm(self.u1, p)
396 396
397 397 UserModel().delete(self.u1)
398 398 Session.commit()
399 399
400 400 def test_add_perm(self):
401 401 perm = Permission.query().all()[0]
402 402 UserModel().grant_perm(self.u1, perm)
403 403 Session.commit()
404 404 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
405 405
406 406 def test_has_perm(self):
407 407 perm = Permission.query().all()
408 408 for p in perm:
409 409 has_p = UserModel().has_perm(self.u1, p)
410 410 self.assertEqual(False, has_p)
411 411
412 412 def test_revoke_perm(self):
413 413 perm = Permission.query().all()[0]
414 414 UserModel().grant_perm(self.u1, perm)
415 415 Session.commit()
416 416 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
417 417
418 418 #revoke
419 419 UserModel().revoke_perm(self.u1, perm)
420 420 Session.commit()
421 421 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
422 422
423 423
424 424 class TestPermissions(unittest.TestCase):
425 425 def __init__(self, methodName='runTest'):
426 426 super(TestPermissions, self).__init__(methodName=methodName)
427 427
428 428 def setUp(self):
429 429 self.u1 = UserModel().create_or_update(
430 430 username=u'u1', password=u'qweqwe',
431 431 email=u'u1@rhodecode.org', name=u'u1', lastname=u'u1'
432 432 )
433 433 self.u2 = UserModel().create_or_update(
434 434 username=u'u2', password=u'qweqwe',
435 435 email=u'u2@rhodecode.org', name=u'u2', lastname=u'u2'
436 436 )
437 437 self.anon = User.get_by_username('default')
438 438 self.a1 = UserModel().create_or_update(
439 439 username=u'a1', password=u'qweqwe',
440 440 email=u'a1@rhodecode.org', name=u'a1', lastname=u'a1', admin=True
441 441 )
442 442 Session.commit()
443 443
444 444 def tearDown(self):
445 445 if hasattr(self, 'test_repo'):
446 446 RepoModel().delete(repo=self.test_repo)
447 447 UserModel().delete(self.u1)
448 448 UserModel().delete(self.u2)
449 449 UserModel().delete(self.a1)
450 450 if hasattr(self, 'g1'):
451 451 ReposGroupModel().delete(self.g1.group_id)
452 452 if hasattr(self, 'g2'):
453 453 ReposGroupModel().delete(self.g2.group_id)
454 454
455 455 if hasattr(self, 'ug1'):
456 456 UsersGroupModel().delete(self.ug1, force=True)
457 457
458 458 Session.commit()
459 459
460 460 def test_default_perms_set(self):
461 461 u1_auth = AuthUser(user_id=self.u1.user_id)
462 462 perms = {
463 463 'repositories_groups': {},
464 464 'global': set([u'hg.create.repository', u'repository.read',
465 465 u'hg.register.manual_activate']),
466 466 'repositories': {u'vcs_test_hg': u'repository.read'}
467 467 }
468 468 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
469 469 perms['repositories'][HG_REPO])
470 470 new_perm = 'repository.write'
471 471 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
472 472 Session.commit()
473 473
474 474 u1_auth = AuthUser(user_id=self.u1.user_id)
475 475 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO], new_perm)
476 476
477 477 def test_default_admin_perms_set(self):
478 478 a1_auth = AuthUser(user_id=self.a1.user_id)
479 479 perms = {
480 480 'repositories_groups': {},
481 481 'global': set([u'hg.admin']),
482 482 'repositories': {u'vcs_test_hg': u'repository.admin'}
483 483 }
484 484 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
485 485 perms['repositories'][HG_REPO])
486 486 new_perm = 'repository.write'
487 487 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1, perm=new_perm)
488 488 Session.commit()
489 489 # cannot really downgrade admins permissions !? they still get's set as
490 490 # admin !
491 491 u1_auth = AuthUser(user_id=self.a1.user_id)
492 492 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
493 493 perms['repositories'][HG_REPO])
494 494
495 495 def test_default_group_perms(self):
496 496 self.g1 = _make_group('test1', skip_if_exists=True)
497 497 self.g2 = _make_group('test2', skip_if_exists=True)
498 498 u1_auth = AuthUser(user_id=self.u1.user_id)
499 499 perms = {
500 500 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
501 501 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
502 502 'repositories': {u'vcs_test_hg': u'repository.read'}
503 503 }
504 504 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
505 505 perms['repositories'][HG_REPO])
506 506 self.assertEqual(u1_auth.permissions['repositories_groups'],
507 507 perms['repositories_groups'])
508 508
509 509 def test_default_admin_group_perms(self):
510 510 self.g1 = _make_group('test1', skip_if_exists=True)
511 511 self.g2 = _make_group('test2', skip_if_exists=True)
512 512 a1_auth = AuthUser(user_id=self.a1.user_id)
513 513 perms = {
514 514 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
515 515 'global': set(['hg.admin']),
516 516 'repositories': {u'vcs_test_hg': 'repository.admin'}
517 517 }
518 518
519 519 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
520 520 perms['repositories'][HG_REPO])
521 521 self.assertEqual(a1_auth.permissions['repositories_groups'],
522 522 perms['repositories_groups'])
523 523
524 524 def test_propagated_permission_from_users_group(self):
525 525 # make group
526 526 self.ug1 = UsersGroupModel().create('G1')
527 527 # add user to group
528 528 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
529 529
530 530 # set permission to lower
531 531 new_perm = 'repository.none'
532 532 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
533 533 Session.commit()
534 534 u1_auth = AuthUser(user_id=self.u1.user_id)
535 535 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
536 536 new_perm)
537 537
538 538 # grant perm for group this should override permission from user
539 539 new_perm = 'repository.write'
540 540 RepoModel().grant_users_group_permission(repo=HG_REPO,
541 541 group_name=self.ug1,
542 542 perm=new_perm)
543 543 # check perms
544 544 u1_auth = AuthUser(user_id=self.u1.user_id)
545 545 perms = {
546 546 'repositories_groups': {},
547 547 'global': set([u'hg.create.repository', u'repository.read',
548 548 u'hg.register.manual_activate']),
549 549 'repositories': {u'vcs_test_hg': u'repository.read'}
550 550 }
551 551 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
552 552 new_perm)
553 553 self.assertEqual(u1_auth.permissions['repositories_groups'],
554 554 perms['repositories_groups'])
555 555
556 556 def test_propagated_permission_from_users_group_lower_weight(self):
557 557 # make group
558 558 self.ug1 = UsersGroupModel().create('G1')
559 559 # add user to group
560 560 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
561 561
562 562 # set permission to lower
563 563 new_perm_h = 'repository.write'
564 564 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
565 565 perm=new_perm_h)
566 566 Session.commit()
567 567 u1_auth = AuthUser(user_id=self.u1.user_id)
568 568 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
569 569 new_perm_h)
570 570
571 571 # grant perm for group this should NOT override permission from user
572 572 # since it's lower than granted
573 573 new_perm_l = 'repository.read'
574 574 RepoModel().grant_users_group_permission(repo=HG_REPO,
575 575 group_name=self.ug1,
576 576 perm=new_perm_l)
577 577 # check perms
578 578 u1_auth = AuthUser(user_id=self.u1.user_id)
579 579 perms = {
580 580 'repositories_groups': {},
581 581 'global': set([u'hg.create.repository', u'repository.read',
582 582 u'hg.register.manual_activate']),
583 583 'repositories': {u'vcs_test_hg': u'repository.write'}
584 584 }
585 585 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
586 586 new_perm_h)
587 587 self.assertEqual(u1_auth.permissions['repositories_groups'],
588 588 perms['repositories_groups'])
589 589
590 590 def test_repo_in_group_permissions(self):
591 591 self.g1 = _make_group('group1', skip_if_exists=True)
592 592 self.g2 = _make_group('group2', skip_if_exists=True)
593 593 Session.commit()
594 594 # both perms should be read !
595 595 u1_auth = AuthUser(user_id=self.u1.user_id)
596 596 self.assertEqual(u1_auth.permissions['repositories_groups'],
597 597 {u'group1': u'group.read', u'group2': u'group.read'})
598 598
599 599 a1_auth = AuthUser(user_id=self.anon.user_id)
600 600 self.assertEqual(a1_auth.permissions['repositories_groups'],
601 601 {u'group1': u'group.read', u'group2': u'group.read'})
602 602
603 603 #Change perms to none for both groups
604 604 ReposGroupModel().grant_user_permission(repos_group=self.g1,
605 605 user=self.anon,
606 606 perm='group.none')
607 607 ReposGroupModel().grant_user_permission(repos_group=self.g2,
608 608 user=self.anon,
609 609 perm='group.none')
610 610
611
611 612 u1_auth = AuthUser(user_id=self.u1.user_id)
612 613 self.assertEqual(u1_auth.permissions['repositories_groups'],
613 614 {u'group1': u'group.none', u'group2': u'group.none'})
614 615
615 616 a1_auth = AuthUser(user_id=self.anon.user_id)
616 617 self.assertEqual(a1_auth.permissions['repositories_groups'],
617 618 {u'group1': u'group.none', u'group2': u'group.none'})
618 619
619 620 # add repo to group
620 621 form_data = {
621 622 'repo_name':HG_REPO,
622 623 'repo_name_full':os.path.join(self.g1.group_name,HG_REPO),
623 624 'repo_type':'hg',
624 625 'clone_uri':'',
625 626 'repo_group':self.g1.group_id,
626 627 'description':'desc',
627 628 'private':False
628 629 }
629 630 self.test_repo = RepoModel().create(form_data, cur_user=self.u1)
630 631 Session.commit()
631 632
632 633 u1_auth = AuthUser(user_id=self.u1.user_id)
633 634 self.assertEqual(u1_auth.permissions['repositories_groups'],
634 635 {u'group1': u'group.none', u'group2': u'group.none'})
635 636
636 637 a1_auth = AuthUser(user_id=self.anon.user_id)
637 638 self.assertEqual(a1_auth.permissions['repositories_groups'],
638 639 {u'group1': u'group.none', u'group2': u'group.none'})
639 640
640 641 #grant permission for u2 !
641 642 ReposGroupModel().grant_user_permission(repos_group=self.g1,
642 643 user=self.u2,
643 644 perm='group.read')
644 645 ReposGroupModel().grant_user_permission(repos_group=self.g2,
645 646 user=self.u2,
646 647 perm='group.read')
647 648 Session.commit()
648 649 self.assertNotEqual(self.u1, self.u2)
649 650 #u1 and anon should have not change perms while u2 should !
650 651 u1_auth = AuthUser(user_id=self.u1.user_id)
651 652 self.assertEqual(u1_auth.permissions['repositories_groups'],
652 653 {u'group1': u'group.none', u'group2': u'group.none'})
653 654
654 655 u2_auth = AuthUser(user_id=self.u2.user_id)
655 656 self.assertEqual(u2_auth.permissions['repositories_groups'],
656 657 {u'group1': u'group.read', u'group2': u'group.read'})
657 658
658 659 a1_auth = AuthUser(user_id=self.anon.user_id)
659 660 self.assertEqual(a1_auth.permissions['repositories_groups'],
660 661 {u'group1': u'group.none', u'group2': u'group.none'})
662
663 def test_repo_group_user_as_user_group_member(self):
664 # create Group1
665 self.g1 = _make_group('group1', skip_if_exists=True)
666 Session.commit()
667 a1_auth = AuthUser(user_id=self.anon.user_id)
668
669 self.assertEqual(a1_auth.permissions['repositories_groups'],
670 {u'group1': u'group.read'})
671
672 # set default permission to none
673 ReposGroupModel().grant_user_permission(repos_group=self.g1,
674 user=self.anon,
675 perm='group.none')
676 # make group
677 self.ug1 = UsersGroupModel().create('G1')
678 # add user to group
679 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
680 Session.commit()
681
682 # check if user is in the group
683 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
684 self.assertEqual(membrs, [self.u1.user_id])
685 # add some user to that group
686
687 # check his permissions
688 a1_auth = AuthUser(user_id=self.anon.user_id)
689 self.assertEqual(a1_auth.permissions['repositories_groups'],
690 {u'group1': u'group.none'})
691
692 u1_auth = AuthUser(user_id=self.u1.user_id)
693 self.assertEqual(u1_auth.permissions['repositories_groups'],
694 {u'group1': u'group.none'})
695
696 # grant ug1 read permissions for
697 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
698 group_name=self.ug1,
699 perm='group.read')
700 Session.commit()
701 # check if the
702 obj = Session.query(UsersGroupRepoGroupToPerm)\
703 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
704 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
705 .scalar()
706 self.assertEqual(obj.permission.permission_name, 'group.read')
707
708 a1_auth = AuthUser(user_id=self.anon.user_id)
709
710 self.assertEqual(a1_auth.permissions['repositories_groups'],
711 {u'group1': u'group.none'})
712
713 u1_auth = AuthUser(user_id=self.u1.user_id)
714 self.assertEqual(u1_auth.permissions['repositories_groups'],
715 {u'group1': u'group.read'})
General Comments 0
You need to be logged in to leave comments. Login now