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