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