##// END OF EJS Templates
locking API returns objects instead of string messages....
marcink -
r3808:2feb5849 beta
parent child Browse files
Show More
@@ -1,1001 +1,1007 b''
1 .. _api:
1 .. _api:
2
2
3 ===
3 ===
4 API
4 API
5 ===
5 ===
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's a single schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways. An url to send API request to RhodeCode is
10 with JSON protocol both ways. An url to send API request to RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13 API ACCESS FOR WEB VIEWS
13 API ACCESS FOR WEB VIEWS
14 ++++++++++++++++++++++++
14 ++++++++++++++++++++++++
15
15
16 API access can also be turned on for each web view in RhodeCode that is
16 API access can also be turned on for each web view in RhodeCode that is
17 decorated with `@LoginRequired` decorator. To enable API access simple change
17 decorated with `@LoginRequired` decorator. To enable API access simple change
18 the standard login decorator to `@LoginRequired(api_access=True)`.
18 the standard login decorator to `@LoginRequired(api_access=True)`.
19
19
20 To make this operation easier, starting from version 1.7.0 there's a white list
20 To make this operation easier, starting from version 1.7.0 there's a white list
21 of views that will have API access enabled. Simply edit `api_access_controllers_whitelist`
21 of views that will have API access enabled. Simply edit `api_access_controllers_whitelist`
22 option in your .ini file, and define views that should have API access enabled.
22 option in your .ini file, and define views that should have API access enabled.
23 Following example shows how to enable API access to patch/diff raw file and archive
23 Following example shows how to enable API access to patch/diff raw file and archive
24 in RhodeCode::
24 in RhodeCode::
25
25
26 api_access_controllers_whitelist =
26 api_access_controllers_whitelist =
27 ChangesetController:changeset_patch,
27 ChangesetController:changeset_patch,
28 ChangesetController:changeset_raw,
28 ChangesetController:changeset_raw,
29 FilesController:raw,
29 FilesController:raw,
30 FilesController:archivefile
30 FilesController:archivefile
31
31
32
32
33 After this change, a rhodecode view can be accessed without login by adding a
33 After this change, a rhodecode view can be accessed without login by adding a
34 GET parameter `?api_key=<api_key>` to url. By default this is only
34 GET parameter `?api_key=<api_key>` to url. By default this is only
35 enabled on RSS/ATOM feed views. Exposing raw diffs is a good way to integrate with
35 enabled on RSS/ATOM feed views. Exposing raw diffs is a good way to integrate with
36 3rd party services like code review, or build farms that could download archives.
36 3rd party services like code review, or build farms that could download archives.
37
37
38
38
39 API ACCESS
39 API ACCESS
40 ++++++++++
40 ++++++++++
41
41
42 All clients are required to send JSON-RPC spec JSON data::
42 All clients are required to send JSON-RPC spec JSON data::
43
43
44 {
44 {
45 "id:"<id>",
45 "id:"<id>",
46 "api_key":"<api_key>",
46 "api_key":"<api_key>",
47 "method":"<method_name>",
47 "method":"<method_name>",
48 "args":{"<arg_key>":"<arg_val>"}
48 "args":{"<arg_key>":"<arg_val>"}
49 }
49 }
50
50
51 Example call for autopulling remotes repos using curl::
51 Example call for autopulling remotes repos using curl::
52 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
52 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
53
53
54 Simply provide
54 Simply provide
55 - *id* A value of any type, which is used to match the response with the request that it is replying to.
55 - *id* A value of any type, which is used to match the response with the request that it is replying to.
56 - *api_key* for access and permission validation.
56 - *api_key* for access and permission validation.
57 - *method* is name of method to call
57 - *method* is name of method to call
58 - *args* is an key:value list of arguments to pass to method
58 - *args* is an key:value list of arguments to pass to method
59
59
60 .. note::
60 .. note::
61
61
62 api_key can be found in your user account page
62 api_key can be found in your user account page
63
63
64
64
65 RhodeCode API will return always a JSON-RPC response::
65 RhodeCode API will return always a JSON-RPC response::
66
66
67 {
67 {
68 "id":<id>, # matching id sent by request
68 "id":<id>, # matching id sent by request
69 "result": "<result>"|null, # JSON formatted result, null if any errors
69 "result": "<result>"|null, # JSON formatted result, null if any errors
70 "error": "null"|<error_message> # JSON formatted error (if any)
70 "error": "null"|<error_message> # JSON formatted error (if any)
71 }
71 }
72
72
73 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
73 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
74 calling api *error* key from response will contain failure description
74 calling api *error* key from response will contain failure description
75 and result will be null.
75 and result will be null.
76
76
77
77
78 API CLIENT
78 API CLIENT
79 ++++++++++
79 ++++++++++
80
80
81 From version 1.4 RhodeCode adds a script that allows to easily
81 From version 1.4 RhodeCode adds a script that allows to easily
82 communicate with API. After installing RhodeCode a `rhodecode-api` script
82 communicate with API. After installing RhodeCode a `rhodecode-api` script
83 will be available.
83 will be available.
84
84
85 To get started quickly simply run::
85 To get started quickly simply run::
86
86
87 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
87 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
88
88
89 This will create a file named .config in the directory you executed it storing
89 This will create a file named .config in the directory you executed it storing
90 json config file with credentials. You can skip this step and always provide
90 json config file with credentials. You can skip this step and always provide
91 both of the arguments to be able to communicate with server
91 both of the arguments to be able to communicate with server
92
92
93
93
94 after that simply run any api command for example get_repo::
94 after that simply run any api command for example get_repo::
95
95
96 rhodecode-api get_repo
96 rhodecode-api get_repo
97
97
98 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
98 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
99 rhodecode said:
99 rhodecode said:
100 {'error': 'Missing non optional `repoid` arg in JSON DATA',
100 {'error': 'Missing non optional `repoid` arg in JSON DATA',
101 'id': 75,
101 'id': 75,
102 'result': None}
102 'result': None}
103
103
104 Ups looks like we forgot to add an argument
104 Ups looks like we forgot to add an argument
105
105
106 Let's try again now giving the repoid as parameters::
106 Let's try again now giving the repoid as parameters::
107
107
108 rhodecode-api get_repo repoid:rhodecode
108 rhodecode-api get_repo repoid:rhodecode
109
109
110 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
110 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
111 rhodecode said:
111 rhodecode said:
112 {'error': None,
112 {'error': None,
113 'id': 39,
113 'id': 39,
114 'result': <json data...>}
114 'result': <json data...>}
115
115
116
116
117
117
118 API METHODS
118 API METHODS
119 +++++++++++
119 +++++++++++
120
120
121
121
122 pull
122 pull
123 ----
123 ----
124
124
125 Pulls given repo from remote location. Can be used to automatically keep
125 Pulls given repo from remote location. Can be used to automatically keep
126 remote repos up to date. This command can be executed only using api_key
126 remote repos up to date. This command can be executed only using api_key
127 belonging to user with admin rights
127 belonging to user with admin rights
128
128
129 INPUT::
129 INPUT::
130
130
131 id : <id_for_response>
131 id : <id_for_response>
132 api_key : "<api_key>"
132 api_key : "<api_key>"
133 method : "pull"
133 method : "pull"
134 args : {
134 args : {
135 "repoid" : "<reponame or repo_id>"
135 "repoid" : "<reponame or repo_id>"
136 }
136 }
137
137
138 OUTPUT::
138 OUTPUT::
139
139
140 id : <id_given_in_input>
140 id : <id_given_in_input>
141 result : "Pulled from `<reponame>`"
141 result : "Pulled from `<reponame>`"
142 error : null
142 error : null
143
143
144
144
145 rescan_repos
145 rescan_repos
146 ------------
146 ------------
147
147
148 Dispatch rescan repositories action. If remove_obsolete is set
148 Dispatch rescan repositories action. If remove_obsolete is set
149 RhodeCode will delete repos that are in database but not in the filesystem.
149 RhodeCode will delete repos that are in database but not in the filesystem.
150 This command can be executed only using api_key belonging to user with admin
150 This command can be executed only using api_key belonging to user with admin
151 rights.
151 rights.
152
152
153 INPUT::
153 INPUT::
154
154
155 id : <id_for_response>
155 id : <id_for_response>
156 api_key : "<api_key>"
156 api_key : "<api_key>"
157 method : "rescan_repos"
157 method : "rescan_repos"
158 args : {
158 args : {
159 "remove_obsolete" : "<boolean = Optional(False)>"
159 "remove_obsolete" : "<boolean = Optional(False)>"
160 }
160 }
161
161
162 OUTPUT::
162 OUTPUT::
163
163
164 id : <id_given_in_input>
164 id : <id_given_in_input>
165 result : "{'added': [<list of names of added repos>],
165 result : "{'added': [<list of names of added repos>],
166 'removed': [<list of names of removed repos>]}"
166 'removed': [<list of names of removed repos>]}"
167 error : null
167 error : null
168
168
169
169
170 invalidate_cache
170 invalidate_cache
171 ----------------
171 ----------------
172
172
173 Invalidate cache for repository.
173 Invalidate cache for repository.
174 This command can be executed only using api_key belonging to user with admin
174 This command can be executed only using api_key belonging to user with admin
175 rights or regular user that have write or admin or write access to repository.
175 rights or regular user that have write or admin or write access to repository.
176
176
177 INPUT::
177 INPUT::
178
178
179 id : <id_for_response>
179 id : <id_for_response>
180 api_key : "<api_key>"
180 api_key : "<api_key>"
181 method : "invalidate_cache"
181 method : "invalidate_cache"
182 args : {
182 args : {
183 "repoid" : "<reponame or repo_id>"
183 "repoid" : "<reponame or repo_id>"
184 }
184 }
185
185
186 OUTPUT::
186 OUTPUT::
187
187
188 id : <id_given_in_input>
188 id : <id_given_in_input>
189 result : "Caches of repository `<reponame>`"
189 result : "Caches of repository `<reponame>`"
190 error : null
190 error : null
191
191
192 lock
192 lock
193 ----
193 ----
194
194
195 Set locking state on given repository by given user. If userid param is skipped
195 Set locking state on given repository by given user. If userid param is skipped
196 , then it is set to id of user whos calling this method. If locked param is skipped
196 , then it is set to id of user whos calling this method. If locked param is skipped
197 then function shows current lock state of given repo.
197 then function shows current lock state of given repo.
198 This command can be executed only using api_key belonging to user with admin
198 This command can be executed only using api_key belonging to user with admin
199 rights or regular user that have admin or write access to repository.
199 rights or regular user that have admin or write access to repository.
200
200
201 INPUT::
201 INPUT::
202
202
203 id : <id_for_response>
203 id : <id_for_response>
204 api_key : "<api_key>"
204 api_key : "<api_key>"
205 method : "lock"
205 method : "lock"
206 args : {
206 args : {
207 "repoid" : "<reponame or repo_id>"
207 "repoid" : "<reponame or repo_id>"
208 "userid" : "<user_id or username = Optional(=apiuser)>",
208 "userid" : "<user_id or username = Optional(=apiuser)>",
209 "locked" : "<bool true|false = Optional(=None)>"
209 "locked" : "<bool true|false = Optional(=None)>"
210 }
210 }
211
211
212 OUTPUT::
212 OUTPUT::
213
213
214 id : <id_given_in_input>
214 id : <id_given_in_input>
215 result : "User `<username>` set lock state for repo `<reponame>` to `true|false`"
215 result : {
216 "repo": "<reponame>",
217 "locked": "<bool true|false>",
218 "locked_since": "<float lock_time>",
219 "locked_by": "<username>",
220 "msg": "User `<username>` set lock state for repo `<reponame>` to `<false|true>`"
221 }
216 error : null
222 error : null
217
223
218
224
219 show_ip
225 show_ip
220 -------
226 -------
221
227
222 Shows IP address as seen from RhodeCode server, together with all
228 Shows IP address as seen from RhodeCode server, together with all
223 defined IP addresses for given user.
229 defined IP addresses for given user.
224 This command can be executed only using api_key belonging to user with admin
230 This command can be executed only using api_key belonging to user with admin
225 rights.
231 rights.
226
232
227 INPUT::
233 INPUT::
228
234
229 id : <id_for_response>
235 id : <id_for_response>
230 api_key : "<api_key>"
236 api_key : "<api_key>"
231 method : "show_ip"
237 method : "show_ip"
232 args : {
238 args : {
233 "userid" : "<user_id or username>",
239 "userid" : "<user_id or username>",
234 }
240 }
235
241
236 OUTPUT::
242 OUTPUT::
237
243
238 id : <id_given_in_input>
244 id : <id_given_in_input>
239 result : {
245 result : {
240 "ip_addr_server": <ip_from_clien>",
246 "ip_addr_server": <ip_from_clien>",
241 "user_ips": [
247 "user_ips": [
242 {
248 {
243 "ip_addr": "<ip_with_mask>",
249 "ip_addr": "<ip_with_mask>",
244 "ip_range": ["<start_ip>", "<end_ip>"],
250 "ip_range": ["<start_ip>", "<end_ip>"],
245 },
251 },
246 ...
252 ...
247 ]
253 ]
248 }
254 }
249
255
250 error : null
256 error : null
251
257
252
258
253 get_user
259 get_user
254 --------
260 --------
255
261
256 Get's an user by username or user_id, Returns empty result if user is not found.
262 Get's an user by username or user_id, Returns empty result if user is not found.
257 If userid param is skipped it is set to id of user who is calling this method.
263 If userid param is skipped it is set to id of user who is calling this method.
258 This command can be executed only using api_key belonging to user with admin
264 This command can be executed only using api_key belonging to user with admin
259 rights, or regular users that cannot specify different userid than theirs
265 rights, or regular users that cannot specify different userid than theirs
260
266
261
267
262 INPUT::
268 INPUT::
263
269
264 id : <id_for_response>
270 id : <id_for_response>
265 api_key : "<api_key>"
271 api_key : "<api_key>"
266 method : "get_user"
272 method : "get_user"
267 args : {
273 args : {
268 "userid" : "<username or user_id Optional(=apiuser)>"
274 "userid" : "<username or user_id Optional(=apiuser)>"
269 }
275 }
270
276
271 OUTPUT::
277 OUTPUT::
272
278
273 id : <id_given_in_input>
279 id : <id_given_in_input>
274 result: None if user does not exist or
280 result: None if user does not exist or
275 {
281 {
276 "user_id" : "<user_id>",
282 "user_id" : "<user_id>",
277 "api_key" : "<api_key>",
283 "api_key" : "<api_key>",
278 "username" : "<username>",
284 "username" : "<username>",
279 "firstname": "<firstname>",
285 "firstname": "<firstname>",
280 "lastname" : "<lastname>",
286 "lastname" : "<lastname>",
281 "email" : "<email>",
287 "email" : "<email>",
282 "emails": "<list_of_all_additional_emails>",
288 "emails": "<list_of_all_additional_emails>",
283 "ip_addresses": "<list_of_ip_addresses_for_user>",
289 "ip_addresses": "<list_of_ip_addresses_for_user>",
284 "active" : "<bool>",
290 "active" : "<bool>",
285 "admin" :Β  "<bool>",
291 "admin" :Β  "<bool>",
286 "ldap_dn" : "<ldap_dn>",
292 "ldap_dn" : "<ldap_dn>",
287 "last_login": "<last_login>",
293 "last_login": "<last_login>",
288 "permissions": {
294 "permissions": {
289 "global": ["hg.create.repository",
295 "global": ["hg.create.repository",
290 "repository.read",
296 "repository.read",
291 "hg.register.manual_activate"],
297 "hg.register.manual_activate"],
292 "repositories": {"repo1": "repository.none"},
298 "repositories": {"repo1": "repository.none"},
293 "repositories_groups": {"Group1": "group.read"}
299 "repositories_groups": {"Group1": "group.read"}
294 },
300 },
295 }
301 }
296
302
297 error: null
303 error: null
298
304
299
305
300 get_users
306 get_users
301 ---------
307 ---------
302
308
303 Lists all existing users. This command can be executed only using api_key
309 Lists all existing users. This command can be executed only using api_key
304 belonging to user with admin rights.
310 belonging to user with admin rights.
305
311
306
312
307 INPUT::
313 INPUT::
308
314
309 id : <id_for_response>
315 id : <id_for_response>
310 api_key : "<api_key>"
316 api_key : "<api_key>"
311 method : "get_users"
317 method : "get_users"
312 args : { }
318 args : { }
313
319
314 OUTPUT::
320 OUTPUT::
315
321
316 id : <id_given_in_input>
322 id : <id_given_in_input>
317 result: [
323 result: [
318 {
324 {
319 "user_id" : "<user_id>",
325 "user_id" : "<user_id>",
320 "username" : "<username>",
326 "username" : "<username>",
321 "firstname": "<firstname>",
327 "firstname": "<firstname>",
322 "lastname" : "<lastname>",
328 "lastname" : "<lastname>",
323 "email" : "<email>",
329 "email" : "<email>",
324 "emails": "<list_of_all_additional_emails>",
330 "emails": "<list_of_all_additional_emails>",
325 "ip_addresses": "<list_of_ip_addresses_for_user>",
331 "ip_addresses": "<list_of_ip_addresses_for_user>",
326 "active" : "<bool>",
332 "active" : "<bool>",
327 "admin" :Β  "<bool>",
333 "admin" :Β  "<bool>",
328 "ldap_dn" : "<ldap_dn>",
334 "ldap_dn" : "<ldap_dn>",
329 "last_login": "<last_login>",
335 "last_login": "<last_login>",
330 },
336 },
331 …
337 …
332 ]
338 ]
333 error: null
339 error: null
334
340
335
341
336 create_user
342 create_user
337 -----------
343 -----------
338
344
339 Creates new user. This command can
345 Creates new user. This command can
340 be executed only using api_key belonging to user with admin rights.
346 be executed only using api_key belonging to user with admin rights.
341
347
342
348
343 INPUT::
349 INPUT::
344
350
345 id : <id_for_response>
351 id : <id_for_response>
346 api_key : "<api_key>"
352 api_key : "<api_key>"
347 method : "create_user"
353 method : "create_user"
348 args : {
354 args : {
349 "username" : "<username>",
355 "username" : "<username>",
350 "email" : "<useremail>",
356 "email" : "<useremail>",
351 "password" : "<password>",
357 "password" : "<password>",
352 "firstname" : "<firstname> = Optional(None)",
358 "firstname" : "<firstname> = Optional(None)",
353 "lastname" : "<lastname> = Optional(None)",
359 "lastname" : "<lastname> = Optional(None)",
354 "active" : "<bool> = Optional(True)",
360 "active" : "<bool> = Optional(True)",
355 "admin" : "<bool> = Optional(False)",
361 "admin" : "<bool> = Optional(False)",
356 "ldap_dn" : "<ldap_dn> = Optional(None)"
362 "ldap_dn" : "<ldap_dn> = Optional(None)"
357 }
363 }
358
364
359 OUTPUT::
365 OUTPUT::
360
366
361 id : <id_given_in_input>
367 id : <id_given_in_input>
362 result: {
368 result: {
363 "msg" : "created new user `<username>`",
369 "msg" : "created new user `<username>`",
364 "user": {
370 "user": {
365 "user_id" : "<user_id>",
371 "user_id" : "<user_id>",
366 "username" : "<username>",
372 "username" : "<username>",
367 "firstname": "<firstname>",
373 "firstname": "<firstname>",
368 "lastname" : "<lastname>",
374 "lastname" : "<lastname>",
369 "email" : "<email>",
375 "email" : "<email>",
370 "emails": "<list_of_all_additional_emails>",
376 "emails": "<list_of_all_additional_emails>",
371 "active" : "<bool>",
377 "active" : "<bool>",
372 "admin" :Β  "<bool>",
378 "admin" :Β  "<bool>",
373 "ldap_dn" : "<ldap_dn>",
379 "ldap_dn" : "<ldap_dn>",
374 "last_login": "<last_login>",
380 "last_login": "<last_login>",
375 },
381 },
376 }
382 }
377 error: null
383 error: null
378
384
379
385
380 update_user
386 update_user
381 -----------
387 -----------
382
388
383 updates given user if such user exists. This command can
389 updates given user if such user exists. This command can
384 be executed only using api_key belonging to user with admin rights.
390 be executed only using api_key belonging to user with admin rights.
385
391
386
392
387 INPUT::
393 INPUT::
388
394
389 id : <id_for_response>
395 id : <id_for_response>
390 api_key : "<api_key>"
396 api_key : "<api_key>"
391 method : "update_user"
397 method : "update_user"
392 args : {
398 args : {
393 "userid" : "<user_id or username>",
399 "userid" : "<user_id or username>",
394 "username" : "<username> = Optional(None)",
400 "username" : "<username> = Optional(None)",
395 "email" : "<useremail> = Optional(None)",
401 "email" : "<useremail> = Optional(None)",
396 "password" : "<password> = Optional(None)",
402 "password" : "<password> = Optional(None)",
397 "firstname" : "<firstname> = Optional(None)",
403 "firstname" : "<firstname> = Optional(None)",
398 "lastname" : "<lastname> = Optional(None)",
404 "lastname" : "<lastname> = Optional(None)",
399 "active" : "<bool> = Optional(None)",
405 "active" : "<bool> = Optional(None)",
400 "admin" : "<bool> = Optional(None)",
406 "admin" : "<bool> = Optional(None)",
401 "ldap_dn" : "<ldap_dn> = Optional(None)"
407 "ldap_dn" : "<ldap_dn> = Optional(None)"
402 }
408 }
403
409
404 OUTPUT::
410 OUTPUT::
405
411
406 id : <id_given_in_input>
412 id : <id_given_in_input>
407 result: {
413 result: {
408 "msg" : "updated user ID:<userid> <username>",
414 "msg" : "updated user ID:<userid> <username>",
409 "user": {
415 "user": {
410 "user_id" : "<user_id>",
416 "user_id" : "<user_id>",
411 "username" : "<username>",
417 "username" : "<username>",
412 "firstname": "<firstname>",
418 "firstname": "<firstname>",
413 "lastname" : "<lastname>",
419 "lastname" : "<lastname>",
414 "email" : "<email>",
420 "email" : "<email>",
415 "emails": "<list_of_all_additional_emails>",
421 "emails": "<list_of_all_additional_emails>",
416 "active" : "<bool>",
422 "active" : "<bool>",
417 "admin" :Β  "<bool>",
423 "admin" :Β  "<bool>",
418 "ldap_dn" : "<ldap_dn>",
424 "ldap_dn" : "<ldap_dn>",
419 "last_login": "<last_login>",
425 "last_login": "<last_login>",
420 },
426 },
421 }
427 }
422 error: null
428 error: null
423
429
424
430
425 delete_user
431 delete_user
426 -----------
432 -----------
427
433
428
434
429 deletes givenuser if such user exists. This command can
435 deletes givenuser if such user exists. This command can
430 be executed only using api_key belonging to user with admin rights.
436 be executed only using api_key belonging to user with admin rights.
431
437
432
438
433 INPUT::
439 INPUT::
434
440
435 id : <id_for_response>
441 id : <id_for_response>
436 api_key : "<api_key>"
442 api_key : "<api_key>"
437 method : "delete_user"
443 method : "delete_user"
438 args : {
444 args : {
439 "userid" : "<user_id or username>",
445 "userid" : "<user_id or username>",
440 }
446 }
441
447
442 OUTPUT::
448 OUTPUT::
443
449
444 id : <id_given_in_input>
450 id : <id_given_in_input>
445 result: {
451 result: {
446 "msg" : "deleted user ID:<userid> <username>",
452 "msg" : "deleted user ID:<userid> <username>",
447 "user": null
453 "user": null
448 }
454 }
449 error: null
455 error: null
450
456
451
457
452 get_users_group
458 get_users_group
453 ---------------
459 ---------------
454
460
455 Gets an existing user group. This command can be executed only using api_key
461 Gets an existing user group. This command can be executed only using api_key
456 belonging to user with admin rights.
462 belonging to user with admin rights.
457
463
458
464
459 INPUT::
465 INPUT::
460
466
461 id : <id_for_response>
467 id : <id_for_response>
462 api_key : "<api_key>"
468 api_key : "<api_key>"
463 method : "get_users_group"
469 method : "get_users_group"
464 args : {
470 args : {
465 "usersgroupid" : "<user group id or name>"
471 "usersgroupid" : "<user group id or name>"
466 }
472 }
467
473
468 OUTPUT::
474 OUTPUT::
469
475
470 id : <id_given_in_input>
476 id : <id_given_in_input>
471 result : None if group not exist
477 result : None if group not exist
472 {
478 {
473 "users_group_id" : "<id>",
479 "users_group_id" : "<id>",
474 "group_name" : "<groupname>",
480 "group_name" : "<groupname>",
475 "active": "<bool>",
481 "active": "<bool>",
476 "members" : [
482 "members" : [
477 {
483 {
478 "user_id" : "<user_id>",
484 "user_id" : "<user_id>",
479 "username" : "<username>",
485 "username" : "<username>",
480 "firstname": "<firstname>",
486 "firstname": "<firstname>",
481 "lastname" : "<lastname>",
487 "lastname" : "<lastname>",
482 "email" : "<email>",
488 "email" : "<email>",
483 "emails": "<list_of_all_additional_emails>",
489 "emails": "<list_of_all_additional_emails>",
484 "active" : "<bool>",
490 "active" : "<bool>",
485 "admin" :Β  "<bool>",
491 "admin" :Β  "<bool>",
486 "ldap_dn" : "<ldap_dn>",
492 "ldap_dn" : "<ldap_dn>",
487 "last_login": "<last_login>",
493 "last_login": "<last_login>",
488 },
494 },
489 …
495 …
490 ]
496 ]
491 }
497 }
492 error : null
498 error : null
493
499
494
500
495 get_users_groups
501 get_users_groups
496 ----------------
502 ----------------
497
503
498 Lists all existing user groups. This command can be executed only using
504 Lists all existing user groups. This command can be executed only using
499 api_key belonging to user with admin rights.
505 api_key belonging to user with admin rights.
500
506
501
507
502 INPUT::
508 INPUT::
503
509
504 id : <id_for_response>
510 id : <id_for_response>
505 api_key : "<api_key>"
511 api_key : "<api_key>"
506 method : "get_users_groups"
512 method : "get_users_groups"
507 args : { }
513 args : { }
508
514
509 OUTPUT::
515 OUTPUT::
510
516
511 id : <id_given_in_input>
517 id : <id_given_in_input>
512 result : [
518 result : [
513 {
519 {
514 "users_group_id" : "<id>",
520 "users_group_id" : "<id>",
515 "group_name" : "<groupname>",
521 "group_name" : "<groupname>",
516 "active": "<bool>",
522 "active": "<bool>",
517 },
523 },
518 …
524 …
519 ]
525 ]
520 error : null
526 error : null
521
527
522
528
523 create_users_group
529 create_users_group
524 ------------------
530 ------------------
525
531
526 Creates new user group. This command can be executed only using api_key
532 Creates new user group. This command can be executed only using api_key
527 belonging to user with admin rights
533 belonging to user with admin rights
528
534
529
535
530 INPUT::
536 INPUT::
531
537
532 id : <id_for_response>
538 id : <id_for_response>
533 api_key : "<api_key>"
539 api_key : "<api_key>"
534 method : "create_users_group"
540 method : "create_users_group"
535 args: {
541 args: {
536 "group_name": "<groupname>",
542 "group_name": "<groupname>",
537 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
543 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
538 "active": "<bool> = Optional(True)"
544 "active": "<bool> = Optional(True)"
539 }
545 }
540
546
541 OUTPUT::
547 OUTPUT::
542
548
543 id : <id_given_in_input>
549 id : <id_given_in_input>
544 result: {
550 result: {
545 "msg": "created new user group `<groupname>`",
551 "msg": "created new user group `<groupname>`",
546 "users_group": {
552 "users_group": {
547 "users_group_id" : "<id>",
553 "users_group_id" : "<id>",
548 "group_name" : "<groupname>",
554 "group_name" : "<groupname>",
549 "active": "<bool>",
555 "active": "<bool>",
550 },
556 },
551 }
557 }
552 error: null
558 error: null
553
559
554
560
555 add_user_to_users_group
561 add_user_to_users_group
556 -----------------------
562 -----------------------
557
563
558 Adds a user to a user group. If user exists in that group success will be
564 Adds a user to a user group. If user exists in that group success will be
559 `false`. This command can be executed only using api_key
565 `false`. This command can be executed only using api_key
560 belonging to user with admin rights
566 belonging to user with admin rights
561
567
562
568
563 INPUT::
569 INPUT::
564
570
565 id : <id_for_response>
571 id : <id_for_response>
566 api_key : "<api_key>"
572 api_key : "<api_key>"
567 method : "add_user_users_group"
573 method : "add_user_users_group"
568 args: {
574 args: {
569 "usersgroupid" : "<user group id or name>",
575 "usersgroupid" : "<user group id or name>",
570 "userid" : "<user_id or username>",
576 "userid" : "<user_id or username>",
571 }
577 }
572
578
573 OUTPUT::
579 OUTPUT::
574
580
575 id : <id_given_in_input>
581 id : <id_given_in_input>
576 result: {
582 result: {
577 "success": True|False # depends on if member is in group
583 "success": True|False # depends on if member is in group
578 "msg": "added member `<username>` to user group `<groupname>` |
584 "msg": "added member `<username>` to user group `<groupname>` |
579 User is already in that group"
585 User is already in that group"
580 }
586 }
581 error: null
587 error: null
582
588
583
589
584 remove_user_from_users_group
590 remove_user_from_users_group
585 ----------------------------
591 ----------------------------
586
592
587 Removes a user from a user group. If user is not in given group success will
593 Removes a user from a user group. If user is not in given group success will
588 be `false`. This command can be executed only
594 be `false`. This command can be executed only
589 using api_key belonging to user with admin rights
595 using api_key belonging to user with admin rights
590
596
591
597
592 INPUT::
598 INPUT::
593
599
594 id : <id_for_response>
600 id : <id_for_response>
595 api_key : "<api_key>"
601 api_key : "<api_key>"
596 method : "remove_user_from_users_group"
602 method : "remove_user_from_users_group"
597 args: {
603 args: {
598 "usersgroupid" : "<user group id or name>",
604 "usersgroupid" : "<user group id or name>",
599 "userid" : "<user_id or username>",
605 "userid" : "<user_id or username>",
600 }
606 }
601
607
602 OUTPUT::
608 OUTPUT::
603
609
604 id : <id_given_in_input>
610 id : <id_given_in_input>
605 result: {
611 result: {
606 "success": True|False, # depends on if member is in group
612 "success": True|False, # depends on if member is in group
607 "msg": "removed member <username> from user group <groupname> |
613 "msg": "removed member <username> from user group <groupname> |
608 User wasn't in group"
614 User wasn't in group"
609 }
615 }
610 error: null
616 error: null
611
617
612
618
613 get_repo
619 get_repo
614 --------
620 --------
615
621
616 Gets an existing repository by it's name or repository_id. Members will return
622 Gets an existing repository by it's name or repository_id. Members will return
617 either users_group or user associated to that repository. This command can be
623 either users_group or user associated to that repository. This command can be
618 executed only using api_key belonging to user with admin
624 executed only using api_key belonging to user with admin
619 rights or regular user that have at least read access to repository.
625 rights or regular user that have at least read access to repository.
620
626
621
627
622 INPUT::
628 INPUT::
623
629
624 id : <id_for_response>
630 id : <id_for_response>
625 api_key : "<api_key>"
631 api_key : "<api_key>"
626 method : "get_repo"
632 method : "get_repo"
627 args: {
633 args: {
628 "repoid" : "<reponame or repo_id>"
634 "repoid" : "<reponame or repo_id>"
629 }
635 }
630
636
631 OUTPUT::
637 OUTPUT::
632
638
633 id : <id_given_in_input>
639 id : <id_given_in_input>
634 result: None if repository does not exist or
640 result: None if repository does not exist or
635 {
641 {
636 "repo_id" : "<repo_id>",
642 "repo_id" : "<repo_id>",
637 "repo_name" : "<reponame>"
643 "repo_name" : "<reponame>"
638 "repo_type" : "<repo_type>",
644 "repo_type" : "<repo_type>",
639 "clone_uri" : "<clone_uri>",
645 "clone_uri" : "<clone_uri>",
640 "enable_downloads": "<bool>",
646 "enable_downloads": "<bool>",
641 "enable_locking": "<bool>",
647 "enable_locking": "<bool>",
642 "enable_statistics": "<bool>",
648 "enable_statistics": "<bool>",
643 "private": "<bool>",
649 "private": "<bool>",
644 "created_on" : "<date_time_created>",
650 "created_on" : "<date_time_created>",
645 "description" : "<description>",
651 "description" : "<description>",
646 "landing_rev": "<landing_rev>",
652 "landing_rev": "<landing_rev>",
647 "last_changeset": {
653 "last_changeset": {
648 "author": "<full_author>",
654 "author": "<full_author>",
649 "date": "<date_time_of_commit>",
655 "date": "<date_time_of_commit>",
650 "message": "<commit_message>",
656 "message": "<commit_message>",
651 "raw_id": "<raw_id>",
657 "raw_id": "<raw_id>",
652 "revision": "<numeric_revision>",
658 "revision": "<numeric_revision>",
653 "short_id": "<short_id>"
659 "short_id": "<short_id>"
654 }
660 }
655 "owner": "<repo_owner>",
661 "owner": "<repo_owner>",
656 "fork_of": "<name_of_fork_parent>",
662 "fork_of": "<name_of_fork_parent>",
657 "members" : [
663 "members" : [
658 {
664 {
659 "type": "user",
665 "type": "user",
660 "user_id" : "<user_id>",
666 "user_id" : "<user_id>",
661 "username" : "<username>",
667 "username" : "<username>",
662 "firstname": "<firstname>",
668 "firstname": "<firstname>",
663 "lastname" : "<lastname>",
669 "lastname" : "<lastname>",
664 "email" : "<email>",
670 "email" : "<email>",
665 "emails": "<list_of_all_additional_emails>",
671 "emails": "<list_of_all_additional_emails>",
666 "active" : "<bool>",
672 "active" : "<bool>",
667 "admin" :Β  "<bool>",
673 "admin" :Β  "<bool>",
668 "ldap_dn" : "<ldap_dn>",
674 "ldap_dn" : "<ldap_dn>",
669 "last_login": "<last_login>",
675 "last_login": "<last_login>",
670 "permission" : "repository.(read|write|admin)"
676 "permission" : "repository.(read|write|admin)"
671 },
677 },
672 …
678 …
673 {
679 {
674 "type": "users_group",
680 "type": "users_group",
675 "id" : "<usersgroupid>",
681 "id" : "<usersgroupid>",
676 "name" : "<usersgroupname>",
682 "name" : "<usersgroupname>",
677 "active": "<bool>",
683 "active": "<bool>",
678 "permission" : "repository.(read|write|admin)"
684 "permission" : "repository.(read|write|admin)"
679 },
685 },
680 …
686 …
681 ]
687 ]
682 "followers": [
688 "followers": [
683 {
689 {
684 "user_id" : "<user_id>",
690 "user_id" : "<user_id>",
685 "username" : "<username>",
691 "username" : "<username>",
686 "firstname": "<firstname>",
692 "firstname": "<firstname>",
687 "lastname" : "<lastname>",
693 "lastname" : "<lastname>",
688 "email" : "<email>",
694 "email" : "<email>",
689 "emails": "<list_of_all_additional_emails>",
695 "emails": "<list_of_all_additional_emails>",
690 "ip_addresses": "<list_of_ip_addresses_for_user>",
696 "ip_addresses": "<list_of_ip_addresses_for_user>",
691 "active" : "<bool>",
697 "active" : "<bool>",
692 "admin" :Β  "<bool>",
698 "admin" :Β  "<bool>",
693 "ldap_dn" : "<ldap_dn>",
699 "ldap_dn" : "<ldap_dn>",
694 "last_login": "<last_login>",
700 "last_login": "<last_login>",
695 },
701 },
696 …
702 …
697 ]
703 ]
698 }
704 }
699 error: null
705 error: null
700
706
701
707
702 get_repos
708 get_repos
703 ---------
709 ---------
704
710
705 Lists all existing repositories. This command can be executed only using
711 Lists all existing repositories. This command can be executed only using
706 api_key belonging to user with admin rights or regular user that have
712 api_key belonging to user with admin rights or regular user that have
707 admin, write or read access to repository.
713 admin, write or read access to repository.
708
714
709
715
710 INPUT::
716 INPUT::
711
717
712 id : <id_for_response>
718 id : <id_for_response>
713 api_key : "<api_key>"
719 api_key : "<api_key>"
714 method : "get_repos"
720 method : "get_repos"
715 args: { }
721 args: { }
716
722
717 OUTPUT::
723 OUTPUT::
718
724
719 id : <id_given_in_input>
725 id : <id_given_in_input>
720 result: [
726 result: [
721 {
727 {
722 "repo_id" : "<repo_id>",
728 "repo_id" : "<repo_id>",
723 "repo_name" : "<reponame>"
729 "repo_name" : "<reponame>"
724 "repo_type" : "<repo_type>",
730 "repo_type" : "<repo_type>",
725 "clone_uri" : "<clone_uri>",
731 "clone_uri" : "<clone_uri>",
726 "private": : "<bool>",
732 "private": : "<bool>",
727 "created_on" : "<datetimecreated>",
733 "created_on" : "<datetimecreated>",
728 "description" : "<description>",
734 "description" : "<description>",
729 "landing_rev": "<landing_rev>",
735 "landing_rev": "<landing_rev>",
730 "owner": "<repo_owner>",
736 "owner": "<repo_owner>",
731 "fork_of": "<name_of_fork_parent>",
737 "fork_of": "<name_of_fork_parent>",
732 "enable_downloads": "<bool>",
738 "enable_downloads": "<bool>",
733 "enable_locking": "<bool>",
739 "enable_locking": "<bool>",
734 "enable_statistics": "<bool>",
740 "enable_statistics": "<bool>",
735 },
741 },
736 …
742 …
737 ]
743 ]
738 error: null
744 error: null
739
745
740
746
741 get_repo_nodes
747 get_repo_nodes
742 --------------
748 --------------
743
749
744 returns a list of nodes and it's children in a flat list for a given path
750 returns a list of nodes and it's children in a flat list for a given path
745 at given revision. It's possible to specify ret_type to show only `files` or
751 at given revision. It's possible to specify ret_type to show only `files` or
746 `dirs`. This command can be executed only using api_key belonging to user
752 `dirs`. This command can be executed only using api_key belonging to user
747 with admin rights
753 with admin rights
748
754
749
755
750 INPUT::
756 INPUT::
751
757
752 id : <id_for_response>
758 id : <id_for_response>
753 api_key : "<api_key>"
759 api_key : "<api_key>"
754 method : "get_repo_nodes"
760 method : "get_repo_nodes"
755 args: {
761 args: {
756 "repoid" : "<reponame or repo_id>"
762 "repoid" : "<reponame or repo_id>"
757 "revision" : "<revision>",
763 "revision" : "<revision>",
758 "root_path" : "<root_path>",
764 "root_path" : "<root_path>",
759 "ret_type" : "<ret_type> = Optional('all')"
765 "ret_type" : "<ret_type> = Optional('all')"
760 }
766 }
761
767
762 OUTPUT::
768 OUTPUT::
763
769
764 id : <id_given_in_input>
770 id : <id_given_in_input>
765 result: [
771 result: [
766 {
772 {
767 "name" : "<name>"
773 "name" : "<name>"
768 "type" : "<type>",
774 "type" : "<type>",
769 },
775 },
770 …
776 …
771 ]
777 ]
772 error: null
778 error: null
773
779
774
780
775 create_repo
781 create_repo
776 -----------
782 -----------
777
783
778 Creates a repository. If repository name contains "/", all needed repository
784 Creates a repository. If repository name contains "/", all needed repository
779 groups will be created. For example "foo/bar/baz" will create groups
785 groups will be created. For example "foo/bar/baz" will create groups
780 "foo", "bar" (with "foo" as parent), and create "baz" repository with
786 "foo", "bar" (with "foo" as parent), and create "baz" repository with
781 "bar" as group. This command can be executed only using api_key belonging to user with admin
787 "bar" as group. This command can be executed only using api_key belonging to user with admin
782 rights or regular user that have create repository permission. Regular users
788 rights or regular user that have create repository permission. Regular users
783 cannot specify owner parameter
789 cannot specify owner parameter
784
790
785
791
786 INPUT::
792 INPUT::
787
793
788 id : <id_for_response>
794 id : <id_for_response>
789 api_key : "<api_key>"
795 api_key : "<api_key>"
790 method : "create_repo"
796 method : "create_repo"
791 args: {
797 args: {
792 "repo_name" : "<reponame>",
798 "repo_name" : "<reponame>",
793 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
799 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
794 "repo_type" : "<repo_type> = Optional('hg')",
800 "repo_type" : "<repo_type> = Optional('hg')",
795 "description" : "<description> = Optional('')",
801 "description" : "<description> = Optional('')",
796 "private" : "<bool> = Optional(False)",
802 "private" : "<bool> = Optional(False)",
797 "clone_uri" : "<clone_uri> = Optional(None)",
803 "clone_uri" : "<clone_uri> = Optional(None)",
798 "landing_rev" : "<landing_rev> = Optional('tip')",
804 "landing_rev" : "<landing_rev> = Optional('tip')",
799 "enable_downloads": "<bool> = Optional(False)",
805 "enable_downloads": "<bool> = Optional(False)",
800 "enable_locking": "<bool> = Optional(False)",
806 "enable_locking": "<bool> = Optional(False)",
801 "enable_statistics": "<bool> = Optional(False)",
807 "enable_statistics": "<bool> = Optional(False)",
802 }
808 }
803
809
804 OUTPUT::
810 OUTPUT::
805
811
806 id : <id_given_in_input>
812 id : <id_given_in_input>
807 result: {
813 result: {
808 "msg": "Created new repository `<reponame>`",
814 "msg": "Created new repository `<reponame>`",
809 "repo": {
815 "repo": {
810 "repo_id" : "<repo_id>",
816 "repo_id" : "<repo_id>",
811 "repo_name" : "<reponame>"
817 "repo_name" : "<reponame>"
812 "repo_type" : "<repo_type>",
818 "repo_type" : "<repo_type>",
813 "clone_uri" : "<clone_uri>",
819 "clone_uri" : "<clone_uri>",
814 "private": : "<bool>",
820 "private": : "<bool>",
815 "created_on" : "<datetimecreated>",
821 "created_on" : "<datetimecreated>",
816 "description" : "<description>",
822 "description" : "<description>",
817 "landing_rev": "<landing_rev>",
823 "landing_rev": "<landing_rev>",
818 "owner": "<username or user_id>",
824 "owner": "<username or user_id>",
819 "fork_of": "<name_of_fork_parent>",
825 "fork_of": "<name_of_fork_parent>",
820 "enable_downloads": "<bool>",
826 "enable_downloads": "<bool>",
821 "enable_locking": "<bool>",
827 "enable_locking": "<bool>",
822 "enable_statistics": "<bool>",
828 "enable_statistics": "<bool>",
823 },
829 },
824 }
830 }
825 error: null
831 error: null
826
832
827
833
828 fork_repo
834 fork_repo
829 ---------
835 ---------
830
836
831 Creates a fork of given repo. In case of using celery this will
837 Creates a fork of given repo. In case of using celery this will
832 immidiatelly return success message, while fork is going to be created
838 immidiatelly return success message, while fork is going to be created
833 asynchronous. This command can be executed only using api_key belonging to
839 asynchronous. This command can be executed only using api_key belonging to
834 user with admin rights or regular user that have fork permission, and at least
840 user with admin rights or regular user that have fork permission, and at least
835 read access to forking repository. Regular users cannot specify owner parameter.
841 read access to forking repository. Regular users cannot specify owner parameter.
836
842
837
843
838 INPUT::
844 INPUT::
839
845
840 id : <id_for_response>
846 id : <id_for_response>
841 api_key : "<api_key>"
847 api_key : "<api_key>"
842 method : "fork_repo"
848 method : "fork_repo"
843 args: {
849 args: {
844 "repoid" : "<reponame or repo_id>",
850 "repoid" : "<reponame or repo_id>",
845 "fork_name": "<forkname>",
851 "fork_name": "<forkname>",
846 "owner": "<username or user_id = Optional(=apiuser)>",
852 "owner": "<username or user_id = Optional(=apiuser)>",
847 "description": "<description>",
853 "description": "<description>",
848 "copy_permissions": "<bool>",
854 "copy_permissions": "<bool>",
849 "private": "<bool>",
855 "private": "<bool>",
850 "landing_rev": "<landing_rev>"
856 "landing_rev": "<landing_rev>"
851
857
852 }
858 }
853
859
854 OUTPUT::
860 OUTPUT::
855
861
856 id : <id_given_in_input>
862 id : <id_given_in_input>
857 result: {
863 result: {
858 "msg": "Created fork of `<reponame>` as `<forkname>`",
864 "msg": "Created fork of `<reponame>` as `<forkname>`",
859 "success": true
865 "success": true
860 }
866 }
861 error: null
867 error: null
862
868
863
869
864 delete_repo
870 delete_repo
865 -----------
871 -----------
866
872
867 Deletes a repository. This command can be executed only using api_key belonging
873 Deletes a repository. This command can be executed only using api_key belonging
868 to user with admin rights or regular user that have admin access to repository.
874 to user with admin rights or regular user that have admin access to repository.
869 When `forks` param is set it's possible to detach or delete forks of deleting
875 When `forks` param is set it's possible to detach or delete forks of deleting
870 repository
876 repository
871
877
872
878
873 INPUT::
879 INPUT::
874
880
875 id : <id_for_response>
881 id : <id_for_response>
876 api_key : "<api_key>"
882 api_key : "<api_key>"
877 method : "delete_repo"
883 method : "delete_repo"
878 args: {
884 args: {
879 "repoid" : "<reponame or repo_id>",
885 "repoid" : "<reponame or repo_id>",
880 "forks" : "`delete` or `detach` = Optional(None)"
886 "forks" : "`delete` or `detach` = Optional(None)"
881 }
887 }
882
888
883 OUTPUT::
889 OUTPUT::
884
890
885 id : <id_given_in_input>
891 id : <id_given_in_input>
886 result: {
892 result: {
887 "msg": "Deleted repository `<reponame>`",
893 "msg": "Deleted repository `<reponame>`",
888 "success": true
894 "success": true
889 }
895 }
890 error: null
896 error: null
891
897
892
898
893 grant_user_permission
899 grant_user_permission
894 ---------------------
900 ---------------------
895
901
896 Grant permission for user on given repository, or update existing one
902 Grant permission for user on given repository, or update existing one
897 if found. This command can be executed only using api_key belonging to user
903 if found. This command can be executed only using api_key belonging to user
898 with admin rights.
904 with admin rights.
899
905
900
906
901 INPUT::
907 INPUT::
902
908
903 id : <id_for_response>
909 id : <id_for_response>
904 api_key : "<api_key>"
910 api_key : "<api_key>"
905 method : "grant_user_permission"
911 method : "grant_user_permission"
906 args: {
912 args: {
907 "repoid" : "<reponame or repo_id>"
913 "repoid" : "<reponame or repo_id>"
908 "userid" : "<username or user_id>"
914 "userid" : "<username or user_id>"
909 "perm" : "(repository.(none|read|write|admin))",
915 "perm" : "(repository.(none|read|write|admin))",
910 }
916 }
911
917
912 OUTPUT::
918 OUTPUT::
913
919
914 id : <id_given_in_input>
920 id : <id_given_in_input>
915 result: {
921 result: {
916 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
922 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
917 "success": true
923 "success": true
918 }
924 }
919 error: null
925 error: null
920
926
921
927
922 revoke_user_permission
928 revoke_user_permission
923 ----------------------
929 ----------------------
924
930
925 Revoke permission for user on given repository. This command can be executed
931 Revoke permission for user on given repository. This command can be executed
926 only using api_key belonging to user with admin rights.
932 only using api_key belonging to user with admin rights.
927
933
928
934
929 INPUT::
935 INPUT::
930
936
931 id : <id_for_response>
937 id : <id_for_response>
932 api_key : "<api_key>"
938 api_key : "<api_key>"
933 method : "revoke_user_permission"
939 method : "revoke_user_permission"
934 args: {
940 args: {
935 "repoid" : "<reponame or repo_id>"
941 "repoid" : "<reponame or repo_id>"
936 "userid" : "<username or user_id>"
942 "userid" : "<username or user_id>"
937 }
943 }
938
944
939 OUTPUT::
945 OUTPUT::
940
946
941 id : <id_given_in_input>
947 id : <id_given_in_input>
942 result: {
948 result: {
943 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
949 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
944 "success": true
950 "success": true
945 }
951 }
946 error: null
952 error: null
947
953
948
954
949 grant_users_group_permission
955 grant_users_group_permission
950 ----------------------------
956 ----------------------------
951
957
952 Grant permission for user group on given repository, or update
958 Grant permission for user group on given repository, or update
953 existing one if found. This command can be executed only using
959 existing one if found. This command can be executed only using
954 api_key belonging to user with admin rights.
960 api_key belonging to user with admin rights.
955
961
956
962
957 INPUT::
963 INPUT::
958
964
959 id : <id_for_response>
965 id : <id_for_response>
960 api_key : "<api_key>"
966 api_key : "<api_key>"
961 method : "grant_users_group_permission"
967 method : "grant_users_group_permission"
962 args: {
968 args: {
963 "repoid" : "<reponame or repo_id>"
969 "repoid" : "<reponame or repo_id>"
964 "usersgroupid" : "<user group id or name>"
970 "usersgroupid" : "<user group id or name>"
965 "perm" : "(repository.(none|read|write|admin))",
971 "perm" : "(repository.(none|read|write|admin))",
966 }
972 }
967
973
968 OUTPUT::
974 OUTPUT::
969
975
970 id : <id_given_in_input>
976 id : <id_given_in_input>
971 result: {
977 result: {
972 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
978 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
973 "success": true
979 "success": true
974 }
980 }
975 error: null
981 error: null
976
982
977
983
978 revoke_users_group_permission
984 revoke_users_group_permission
979 -----------------------------
985 -----------------------------
980
986
981 Revoke permission for user group on given repository.This command can be
987 Revoke permission for user group on given repository.This command can be
982 executed only using api_key belonging to user with admin rights.
988 executed only using api_key belonging to user with admin rights.
983
989
984 INPUT::
990 INPUT::
985
991
986 id : <id_for_response>
992 id : <id_for_response>
987 api_key : "<api_key>"
993 api_key : "<api_key>"
988 method : "revoke_users_group_permission"
994 method : "revoke_users_group_permission"
989 args: {
995 args: {
990 "repoid" : "<reponame or repo_id>"
996 "repoid" : "<reponame or repo_id>"
991 "usersgroupid" : "<user group id or name>"
997 "usersgroupid" : "<user group id or name>"
992 }
998 }
993
999
994 OUTPUT::
1000 OUTPUT::
995
1001
996 id : <id_given_in_input>
1002 id : <id_given_in_input>
997 result: {
1003 result: {
998 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
1004 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
999 "success": true
1005 "success": true
1000 }
1006 }
1001 error: null
1007 error: null
@@ -1,1045 +1,1066 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.api
3 rhodecode.controllers.api
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 API controller for RhodeCode
6 API controller for RhodeCode
7
7
8 :created_on: Aug 20, 2011
8 :created_on: Aug 20, 2011
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2011-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2011-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
13 # This program is free software; you can redistribute it and/or
14 # modify it under the terms of the GNU General Public License
14 # modify it under the terms of the GNU General Public License
15 # as published by the Free Software Foundation; version 2
15 # as published by the Free Software Foundation; version 2
16 # of the License or (at your opinion) any later version of the license.
16 # of the License or (at your opinion) any later version of the license.
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, write to the Free Software
24 # along with this program; if not, write to the Free Software
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
25 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
26 # MA 02110-1301, USA.
26 # MA 02110-1301, USA.
27
27
28 import time
28 import traceback
29 import traceback
29 import logging
30 import logging
30
31
31 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 from rhodecode.controllers.api import JSONRPCController, JSONRPCError
32 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
33 from rhodecode.lib.auth import PasswordGenerator, AuthUser, \
33 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
34 HasPermissionAllDecorator, HasPermissionAnyDecorator, \
34 HasPermissionAnyApi, HasRepoPermissionAnyApi
35 HasPermissionAnyApi, HasRepoPermissionAnyApi
35 from rhodecode.lib.utils import map_groups, repo2db_mapper
36 from rhodecode.lib.utils import map_groups, repo2db_mapper
36 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_int
37 from rhodecode.lib.utils2 import str2bool, time_to_datetime, safe_int
37 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
38 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
39 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.scm import ScmModel
40 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.repo import RepoModel
41 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.users_group import UserGroupModel
43 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap,\
44 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap,\
44 Permission, User
45 Permission, User
45 from rhodecode.lib.compat import json
46 from rhodecode.lib.compat import json
46 from rhodecode.lib.exceptions import DefaultUserException
47 from rhodecode.lib.exceptions import DefaultUserException
47
48
48 log = logging.getLogger(__name__)
49 log = logging.getLogger(__name__)
49
50
50
51
51 class OptionalAttr(object):
52 class OptionalAttr(object):
52 """
53 """
53 Special Optional Option that defines other attribute
54 Special Optional Option that defines other attribute
54 """
55 """
55 def __init__(self, attr_name):
56 def __init__(self, attr_name):
56 self.attr_name = attr_name
57 self.attr_name = attr_name
57
58
58 def __repr__(self):
59 def __repr__(self):
59 return '<OptionalAttr:%s>' % self.attr_name
60 return '<OptionalAttr:%s>' % self.attr_name
60
61
61 def __call__(self):
62 def __call__(self):
62 return self
63 return self
63 #alias
64 #alias
64 OAttr = OptionalAttr
65 OAttr = OptionalAttr
65
66
66
67
67 class Optional(object):
68 class Optional(object):
68 """
69 """
69 Defines an optional parameter::
70 Defines an optional parameter::
70
71
71 param = param.getval() if isinstance(param, Optional) else param
72 param = param.getval() if isinstance(param, Optional) else param
72 param = param() if isinstance(param, Optional) else param
73 param = param() if isinstance(param, Optional) else param
73
74
74 is equivalent of::
75 is equivalent of::
75
76
76 param = Optional.extract(param)
77 param = Optional.extract(param)
77
78
78 """
79 """
79 def __init__(self, type_):
80 def __init__(self, type_):
80 self.type_ = type_
81 self.type_ = type_
81
82
82 def __repr__(self):
83 def __repr__(self):
83 return '<Optional:%s>' % self.type_.__repr__()
84 return '<Optional:%s>' % self.type_.__repr__()
84
85
85 def __call__(self):
86 def __call__(self):
86 return self.getval()
87 return self.getval()
87
88
88 def getval(self):
89 def getval(self):
89 """
90 """
90 returns value from this Optional instance
91 returns value from this Optional instance
91 """
92 """
92 return self.type_
93 return self.type_
93
94
94 @classmethod
95 @classmethod
95 def extract(cls, val):
96 def extract(cls, val):
96 if isinstance(val, cls):
97 if isinstance(val, cls):
97 return val.getval()
98 return val.getval()
98 return val
99 return val
99
100
100
101
101 def get_user_or_error(userid):
102 def get_user_or_error(userid):
102 """
103 """
103 Get user by id or name or return JsonRPCError if not found
104 Get user by id or name or return JsonRPCError if not found
104
105
105 :param userid:
106 :param userid:
106 """
107 """
107 user = UserModel().get_user(userid)
108 user = UserModel().get_user(userid)
108 if user is None:
109 if user is None:
109 raise JSONRPCError("user `%s` does not exist" % userid)
110 raise JSONRPCError("user `%s` does not exist" % userid)
110 return user
111 return user
111
112
112
113
113 def get_repo_or_error(repoid):
114 def get_repo_or_error(repoid):
114 """
115 """
115 Get repo by id or name or return JsonRPCError if not found
116 Get repo by id or name or return JsonRPCError if not found
116
117
117 :param userid:
118 :param userid:
118 """
119 """
119 repo = RepoModel().get_repo(repoid)
120 repo = RepoModel().get_repo(repoid)
120 if repo is None:
121 if repo is None:
121 raise JSONRPCError('repository `%s` does not exist' % (repoid))
122 raise JSONRPCError('repository `%s` does not exist' % (repoid))
122 return repo
123 return repo
123
124
124
125
125 def get_users_group_or_error(usersgroupid):
126 def get_users_group_or_error(usersgroupid):
126 """
127 """
127 Get user group by id or name or return JsonRPCError if not found
128 Get user group by id or name or return JsonRPCError if not found
128
129
129 :param userid:
130 :param userid:
130 """
131 """
131 users_group = UserGroupModel().get_group(usersgroupid)
132 users_group = UserGroupModel().get_group(usersgroupid)
132 if users_group is None:
133 if users_group is None:
133 raise JSONRPCError('user group `%s` does not exist' % usersgroupid)
134 raise JSONRPCError('user group `%s` does not exist' % usersgroupid)
134 return users_group
135 return users_group
135
136
136
137
137 def get_perm_or_error(permid):
138 def get_perm_or_error(permid):
138 """
139 """
139 Get permission by id or name or return JsonRPCError if not found
140 Get permission by id or name or return JsonRPCError if not found
140
141
141 :param userid:
142 :param userid:
142 """
143 """
143 perm = Permission.get_by_key(permid)
144 perm = Permission.get_by_key(permid)
144 if perm is None:
145 if perm is None:
145 raise JSONRPCError('permission `%s` does not exist' % (permid))
146 raise JSONRPCError('permission `%s` does not exist' % (permid))
146 return perm
147 return perm
147
148
148
149
149 class ApiController(JSONRPCController):
150 class ApiController(JSONRPCController):
150 """
151 """
151 API Controller
152 API Controller
152
153
153
154
154 Each method needs to have USER as argument this is then based on given
155 Each method needs to have USER as argument this is then based on given
155 API_KEY propagated as instance of user object
156 API_KEY propagated as instance of user object
156
157
157 Preferably this should be first argument also
158 Preferably this should be first argument also
158
159
159
160
160 Each function should also **raise** JSONRPCError for any
161 Each function should also **raise** JSONRPCError for any
161 errors that happens
162 errors that happens
162
163
163 """
164 """
164
165
165 @HasPermissionAllDecorator('hg.admin')
166 @HasPermissionAllDecorator('hg.admin')
166 def pull(self, apiuser, repoid):
167 def pull(self, apiuser, repoid):
167 """
168 """
168 Dispatch pull action on given repo
169 Dispatch pull action on given repo
169
170
170 :param apiuser:
171 :param apiuser:
171 :param repoid:
172 :param repoid:
172 """
173 """
173
174
174 repo = get_repo_or_error(repoid)
175 repo = get_repo_or_error(repoid)
175
176
176 try:
177 try:
177 ScmModel().pull_changes(repo.repo_name,
178 ScmModel().pull_changes(repo.repo_name,
178 self.rhodecode_user.username)
179 self.rhodecode_user.username)
179 return 'Pulled from `%s`' % repo.repo_name
180 return 'Pulled from `%s`' % repo.repo_name
180 except Exception:
181 except Exception:
181 log.error(traceback.format_exc())
182 log.error(traceback.format_exc())
182 raise JSONRPCError(
183 raise JSONRPCError(
183 'Unable to pull changes from `%s`' % repo.repo_name
184 'Unable to pull changes from `%s`' % repo.repo_name
184 )
185 )
185
186
186 @HasPermissionAllDecorator('hg.admin')
187 @HasPermissionAllDecorator('hg.admin')
187 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
188 def rescan_repos(self, apiuser, remove_obsolete=Optional(False)):
188 """
189 """
189 Dispatch rescan repositories action. If remove_obsolete is set
190 Dispatch rescan repositories action. If remove_obsolete is set
190 than also delete repos that are in database but not in the filesystem.
191 than also delete repos that are in database but not in the filesystem.
191 aka "clean zombies"
192 aka "clean zombies"
192
193
193 :param apiuser:
194 :param apiuser:
194 :param remove_obsolete:
195 :param remove_obsolete:
195 """
196 """
196
197
197 try:
198 try:
198 rm_obsolete = Optional.extract(remove_obsolete)
199 rm_obsolete = Optional.extract(remove_obsolete)
199 added, removed = repo2db_mapper(ScmModel().repo_scan(),
200 added, removed = repo2db_mapper(ScmModel().repo_scan(),
200 remove_obsolete=rm_obsolete)
201 remove_obsolete=rm_obsolete)
201 return {'added': added, 'removed': removed}
202 return {'added': added, 'removed': removed}
202 except Exception:
203 except Exception:
203 log.error(traceback.format_exc())
204 log.error(traceback.format_exc())
204 raise JSONRPCError(
205 raise JSONRPCError(
205 'Error occurred during rescan repositories action'
206 'Error occurred during rescan repositories action'
206 )
207 )
207
208
208 def invalidate_cache(self, apiuser, repoid):
209 def invalidate_cache(self, apiuser, repoid):
209 """
210 """
210 Dispatch cache invalidation action on given repo
211 Dispatch cache invalidation action on given repo
211
212
212 :param apiuser:
213 :param apiuser:
213 :param repoid:
214 :param repoid:
214 """
215 """
215 repo = get_repo_or_error(repoid)
216 repo = get_repo_or_error(repoid)
216 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
217 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
217 # check if we have admin permission for this repo !
218 # check if we have admin permission for this repo !
218 if HasRepoPermissionAnyApi('repository.admin',
219 if HasRepoPermissionAnyApi('repository.admin',
219 'repository.write')(user=apiuser,
220 'repository.write')(user=apiuser,
220 repo_name=repo.repo_name) is False:
221 repo_name=repo.repo_name) is False:
221 raise JSONRPCError('repository `%s` does not exist' % (repoid))
222 raise JSONRPCError('repository `%s` does not exist' % (repoid))
222
223
223 try:
224 try:
224 ScmModel().mark_for_invalidation(repo.repo_name)
225 ScmModel().mark_for_invalidation(repo.repo_name)
225 return ('Caches of repository `%s` was invalidated' % repoid)
226 return ('Caches of repository `%s` was invalidated' % repoid)
226 except Exception:
227 except Exception:
227 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
228 raise JSONRPCError(
229 raise JSONRPCError(
229 'Error occurred during cache invalidation action'
230 'Error occurred during cache invalidation action'
230 )
231 )
231
232
232 def lock(self, apiuser, repoid, locked=Optional(None),
233 def lock(self, apiuser, repoid, locked=Optional(None),
233 userid=Optional(OAttr('apiuser'))):
234 userid=Optional(OAttr('apiuser'))):
234 """
235 """
235 Set locking state on particular repository by given user, if
236 Set locking state on particular repository by given user, if
236 this command is runned by non-admin account userid is set to user
237 this command is runned by non-admin account userid is set to user
237 who is calling this method
238 who is calling this method
238
239
239 :param apiuser:
240 :param apiuser:
240 :param repoid:
241 :param repoid:
241 :param userid:
242 :param userid:
242 :param locked:
243 :param locked:
243 """
244 """
244 repo = get_repo_or_error(repoid)
245 repo = get_repo_or_error(repoid)
245 if HasPermissionAnyApi('hg.admin')(user=apiuser):
246 if HasPermissionAnyApi('hg.admin')(user=apiuser):
246 pass
247 pass
247 elif HasRepoPermissionAnyApi('repository.admin',
248 elif HasRepoPermissionAnyApi('repository.admin',
248 'repository.write')(user=apiuser,
249 'repository.write')(user=apiuser,
249 repo_name=repo.repo_name):
250 repo_name=repo.repo_name):
250 #make sure normal user does not pass someone else userid,
251 #make sure normal user does not pass someone else userid,
251 #he is not allowed to do that
252 #he is not allowed to do that
252 if not isinstance(userid, Optional) and userid != apiuser.user_id:
253 if not isinstance(userid, Optional) and userid != apiuser.user_id:
253 raise JSONRPCError(
254 raise JSONRPCError(
254 'userid is not the same as your user'
255 'userid is not the same as your user'
255 )
256 )
256 else:
257 else:
257 raise JSONRPCError('repository `%s` does not exist' % (repoid))
258 raise JSONRPCError('repository `%s` does not exist' % (repoid))
258
259
259 if isinstance(userid, Optional):
260 if isinstance(userid, Optional):
260 userid = apiuser.user_id
261 userid = apiuser.user_id
261
262
262 user = get_user_or_error(userid)
263 user = get_user_or_error(userid)
263
264
264 if isinstance(locked, Optional):
265 if isinstance(locked, Optional):
265 lockobj = Repository.getlock(repo)
266 lockobj = Repository.getlock(repo)
266
267
267 if lockobj[0] is None:
268 if lockobj[0] is None:
268 return ('Repo `%s` not locked. Locked=`False`.'
269 _d = {
269 % (repo.repo_name))
270 'repo': repo.repo_name,
271 'locked': False,
272 'locked_since': None,
273 'locked_by': None,
274 'msg': 'Repo `%s` not locked.' % repo.repo_name
275 }
276 return _d
270 else:
277 else:
271 userid, time_ = lockobj
278 userid, time_ = lockobj
272 user = get_user_or_error(userid)
279 lock_user = get_user_or_error(userid)
280 _d = {
281 'repo': repo.repo_name,
282 'locked': True,
283 'locked_since': time_,
284 'locked_by': lock_user.username,
285 'msg': ('Repo `%s` locked by `%s`. '
286 % (repo.repo_name,
287 json.dumps(time_to_datetime(time_))))
288 }
289 return _d
273
290
274 return ('Repo `%s` locked by `%s`. Locked=`True`. '
291 # force locked state through a flag
275 'Locked since: `%s`'
276 % (repo.repo_name, user.username,
277 json.dumps(time_to_datetime(time_))))
278
279 else:
292 else:
280 locked = str2bool(locked)
293 locked = str2bool(locked)
281 try:
294 try:
282 if locked:
295 if locked:
283 Repository.lock(repo, user.user_id)
296 lock_time = time.time()
297 Repository.lock(repo, user.user_id, lock_time)
284 else:
298 else:
299 lock_time = None
285 Repository.unlock(repo)
300 Repository.unlock(repo)
286
301 _d = {
287 return ('User `%s` set lock state for repo `%s` to `%s`'
302 'repo': repo.repo_name,
288 % (user.username, repo.repo_name, locked))
303 'locked': locked,
304 'locked_since': lock_time,
305 'locked_by': user.username,
306 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
307 % (user.username, repo.repo_name, locked))
308 }
309 return _d
289 except Exception:
310 except Exception:
290 log.error(traceback.format_exc())
311 log.error(traceback.format_exc())
291 raise JSONRPCError(
312 raise JSONRPCError(
292 'Error occurred locking repository `%s`' % repo.repo_name
313 'Error occurred locking repository `%s`' % repo.repo_name
293 )
314 )
294
315
295 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
316 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
296 """
317 """
297 Get all locks for given userid, if
318 Get all locks for given userid, if
298 this command is runned by non-admin account userid is set to user
319 this command is runned by non-admin account userid is set to user
299 who is calling this method, thus returning locks for himself
320 who is calling this method, thus returning locks for himself
300
321
301 :param apiuser:
322 :param apiuser:
302 :param userid:
323 :param userid:
303 """
324 """
304 if HasPermissionAnyApi('hg.admin')(user=apiuser):
325 if HasPermissionAnyApi('hg.admin')(user=apiuser):
305 pass
326 pass
306 else:
327 else:
307 #make sure normal user does not pass someone else userid,
328 #make sure normal user does not pass someone else userid,
308 #he is not allowed to do that
329 #he is not allowed to do that
309 if not isinstance(userid, Optional) and userid != apiuser.user_id:
330 if not isinstance(userid, Optional) and userid != apiuser.user_id:
310 raise JSONRPCError(
331 raise JSONRPCError(
311 'userid is not the same as your user'
332 'userid is not the same as your user'
312 )
333 )
313 ret = []
334 ret = []
314 if isinstance(userid, Optional):
335 if isinstance(userid, Optional):
315 user = None
336 user = None
316 else:
337 else:
317 user = get_user_or_error(userid)
338 user = get_user_or_error(userid)
318
339
319 #show all locks
340 #show all locks
320 for r in Repository.getAll():
341 for r in Repository.getAll():
321 userid, time_ = r.locked
342 userid, time_ = r.locked
322 if time_:
343 if time_:
323 _api_data = r.get_api_data()
344 _api_data = r.get_api_data()
324 # if we use userfilter just show the locks for this user
345 # if we use userfilter just show the locks for this user
325 if user:
346 if user:
326 if safe_int(userid) == user.user_id:
347 if safe_int(userid) == user.user_id:
327 ret.append(_api_data)
348 ret.append(_api_data)
328 else:
349 else:
329 ret.append(_api_data)
350 ret.append(_api_data)
330
351
331 return ret
352 return ret
332
353
333 @HasPermissionAllDecorator('hg.admin')
354 @HasPermissionAllDecorator('hg.admin')
334 def show_ip(self, apiuser, userid):
355 def show_ip(self, apiuser, userid):
335 """
356 """
336 Shows IP address as seen from RhodeCode server, together with all
357 Shows IP address as seen from RhodeCode server, together with all
337 defined IP addresses for given user
358 defined IP addresses for given user
338
359
339 :param apiuser:
360 :param apiuser:
340 :param userid:
361 :param userid:
341 """
362 """
342 user = get_user_or_error(userid)
363 user = get_user_or_error(userid)
343 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
364 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
344 return dict(
365 return dict(
345 ip_addr_server=self.ip_addr,
366 ip_addr_server=self.ip_addr,
346 user_ips=ips
367 user_ips=ips
347 )
368 )
348
369
349 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
370 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
350 """"
371 """"
351 Get a user by username, or userid, if userid is given
372 Get a user by username, or userid, if userid is given
352
373
353 :param apiuser:
374 :param apiuser:
354 :param userid:
375 :param userid:
355 """
376 """
356 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
377 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
357 #make sure normal user does not pass someone else userid,
378 #make sure normal user does not pass someone else userid,
358 #he is not allowed to do that
379 #he is not allowed to do that
359 if not isinstance(userid, Optional) and userid != apiuser.user_id:
380 if not isinstance(userid, Optional) and userid != apiuser.user_id:
360 raise JSONRPCError(
381 raise JSONRPCError(
361 'userid is not the same as your user'
382 'userid is not the same as your user'
362 )
383 )
363
384
364 if isinstance(userid, Optional):
385 if isinstance(userid, Optional):
365 userid = apiuser.user_id
386 userid = apiuser.user_id
366
387
367 user = get_user_or_error(userid)
388 user = get_user_or_error(userid)
368 data = user.get_api_data()
389 data = user.get_api_data()
369 data['permissions'] = AuthUser(user_id=user.user_id).permissions
390 data['permissions'] = AuthUser(user_id=user.user_id).permissions
370 return data
391 return data
371
392
372 @HasPermissionAllDecorator('hg.admin')
393 @HasPermissionAllDecorator('hg.admin')
373 def get_users(self, apiuser):
394 def get_users(self, apiuser):
374 """"
395 """"
375 Get all users
396 Get all users
376
397
377 :param apiuser:
398 :param apiuser:
378 """
399 """
379
400
380 result = []
401 result = []
381 users_list = User.query().order_by(User.username)\
402 users_list = User.query().order_by(User.username)\
382 .filter(User.username != User.DEFAULT_USER)\
403 .filter(User.username != User.DEFAULT_USER)\
383 .all()
404 .all()
384 for user in users_list:
405 for user in users_list:
385 result.append(user.get_api_data())
406 result.append(user.get_api_data())
386 return result
407 return result
387
408
388 @HasPermissionAllDecorator('hg.admin')
409 @HasPermissionAllDecorator('hg.admin')
389 def create_user(self, apiuser, username, email, password,
410 def create_user(self, apiuser, username, email, password,
390 firstname=Optional(None), lastname=Optional(None),
411 firstname=Optional(None), lastname=Optional(None),
391 active=Optional(True), admin=Optional(False),
412 active=Optional(True), admin=Optional(False),
392 ldap_dn=Optional(None)):
413 ldap_dn=Optional(None)):
393 """
414 """
394 Create new user
415 Create new user
395
416
396 :param apiuser:
417 :param apiuser:
397 :param username:
418 :param username:
398 :param email:
419 :param email:
399 :param password:
420 :param password:
400 :param firstname:
421 :param firstname:
401 :param lastname:
422 :param lastname:
402 :param active:
423 :param active:
403 :param admin:
424 :param admin:
404 :param ldap_dn:
425 :param ldap_dn:
405 """
426 """
406
427
407 if UserModel().get_by_username(username):
428 if UserModel().get_by_username(username):
408 raise JSONRPCError("user `%s` already exist" % username)
429 raise JSONRPCError("user `%s` already exist" % username)
409
430
410 if UserModel().get_by_email(email, case_insensitive=True):
431 if UserModel().get_by_email(email, case_insensitive=True):
411 raise JSONRPCError("email `%s` already exist" % email)
432 raise JSONRPCError("email `%s` already exist" % email)
412
433
413 if Optional.extract(ldap_dn):
434 if Optional.extract(ldap_dn):
414 # generate temporary password if ldap_dn
435 # generate temporary password if ldap_dn
415 password = PasswordGenerator().gen_password(length=8)
436 password = PasswordGenerator().gen_password(length=8)
416
437
417 try:
438 try:
418 user = UserModel().create_or_update(
439 user = UserModel().create_or_update(
419 username=Optional.extract(username),
440 username=Optional.extract(username),
420 password=Optional.extract(password),
441 password=Optional.extract(password),
421 email=Optional.extract(email),
442 email=Optional.extract(email),
422 firstname=Optional.extract(firstname),
443 firstname=Optional.extract(firstname),
423 lastname=Optional.extract(lastname),
444 lastname=Optional.extract(lastname),
424 active=Optional.extract(active),
445 active=Optional.extract(active),
425 admin=Optional.extract(admin),
446 admin=Optional.extract(admin),
426 ldap_dn=Optional.extract(ldap_dn)
447 ldap_dn=Optional.extract(ldap_dn)
427 )
448 )
428 Session().commit()
449 Session().commit()
429 return dict(
450 return dict(
430 msg='created new user `%s`' % username,
451 msg='created new user `%s`' % username,
431 user=user.get_api_data()
452 user=user.get_api_data()
432 )
453 )
433 except Exception:
454 except Exception:
434 log.error(traceback.format_exc())
455 log.error(traceback.format_exc())
435 raise JSONRPCError('failed to create user `%s`' % username)
456 raise JSONRPCError('failed to create user `%s`' % username)
436
457
437 @HasPermissionAllDecorator('hg.admin')
458 @HasPermissionAllDecorator('hg.admin')
438 def update_user(self, apiuser, userid, username=Optional(None),
459 def update_user(self, apiuser, userid, username=Optional(None),
439 email=Optional(None), firstname=Optional(None),
460 email=Optional(None), firstname=Optional(None),
440 lastname=Optional(None), active=Optional(None),
461 lastname=Optional(None), active=Optional(None),
441 admin=Optional(None), ldap_dn=Optional(None),
462 admin=Optional(None), ldap_dn=Optional(None),
442 password=Optional(None)):
463 password=Optional(None)):
443 """
464 """
444 Updates given user
465 Updates given user
445
466
446 :param apiuser:
467 :param apiuser:
447 :param userid:
468 :param userid:
448 :param username:
469 :param username:
449 :param email:
470 :param email:
450 :param firstname:
471 :param firstname:
451 :param lastname:
472 :param lastname:
452 :param active:
473 :param active:
453 :param admin:
474 :param admin:
454 :param ldap_dn:
475 :param ldap_dn:
455 :param password:
476 :param password:
456 """
477 """
457
478
458 user = get_user_or_error(userid)
479 user = get_user_or_error(userid)
459
480
460 # call function and store only updated arguments
481 # call function and store only updated arguments
461 updates = {}
482 updates = {}
462
483
463 def store_update(attr, name):
484 def store_update(attr, name):
464 if not isinstance(attr, Optional):
485 if not isinstance(attr, Optional):
465 updates[name] = attr
486 updates[name] = attr
466
487
467 try:
488 try:
468
489
469 store_update(username, 'username')
490 store_update(username, 'username')
470 store_update(password, 'password')
491 store_update(password, 'password')
471 store_update(email, 'email')
492 store_update(email, 'email')
472 store_update(firstname, 'name')
493 store_update(firstname, 'name')
473 store_update(lastname, 'lastname')
494 store_update(lastname, 'lastname')
474 store_update(active, 'active')
495 store_update(active, 'active')
475 store_update(admin, 'admin')
496 store_update(admin, 'admin')
476 store_update(ldap_dn, 'ldap_dn')
497 store_update(ldap_dn, 'ldap_dn')
477
498
478 user = UserModel().update_user(user, **updates)
499 user = UserModel().update_user(user, **updates)
479 Session().commit()
500 Session().commit()
480 return dict(
501 return dict(
481 msg='updated user ID:%s %s' % (user.user_id, user.username),
502 msg='updated user ID:%s %s' % (user.user_id, user.username),
482 user=user.get_api_data()
503 user=user.get_api_data()
483 )
504 )
484 except DefaultUserException:
505 except DefaultUserException:
485 log.error(traceback.format_exc())
506 log.error(traceback.format_exc())
486 raise JSONRPCError('editing default user is forbidden')
507 raise JSONRPCError('editing default user is forbidden')
487 except Exception:
508 except Exception:
488 log.error(traceback.format_exc())
509 log.error(traceback.format_exc())
489 raise JSONRPCError('failed to update user `%s`' % userid)
510 raise JSONRPCError('failed to update user `%s`' % userid)
490
511
491 @HasPermissionAllDecorator('hg.admin')
512 @HasPermissionAllDecorator('hg.admin')
492 def delete_user(self, apiuser, userid):
513 def delete_user(self, apiuser, userid):
493 """"
514 """"
494 Deletes an user
515 Deletes an user
495
516
496 :param apiuser:
517 :param apiuser:
497 :param userid:
518 :param userid:
498 """
519 """
499 user = get_user_or_error(userid)
520 user = get_user_or_error(userid)
500
521
501 try:
522 try:
502 UserModel().delete(userid)
523 UserModel().delete(userid)
503 Session().commit()
524 Session().commit()
504 return dict(
525 return dict(
505 msg='deleted user ID:%s %s' % (user.user_id, user.username),
526 msg='deleted user ID:%s %s' % (user.user_id, user.username),
506 user=None
527 user=None
507 )
528 )
508 except Exception:
529 except Exception:
509 log.error(traceback.format_exc())
530 log.error(traceback.format_exc())
510 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
531 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
511 user.username))
532 user.username))
512
533
513 @HasPermissionAllDecorator('hg.admin')
534 @HasPermissionAllDecorator('hg.admin')
514 def get_users_group(self, apiuser, usersgroupid):
535 def get_users_group(self, apiuser, usersgroupid):
515 """"
536 """"
516 Get user group by name or id
537 Get user group by name or id
517
538
518 :param apiuser:
539 :param apiuser:
519 :param usersgroupid:
540 :param usersgroupid:
520 """
541 """
521 users_group = get_users_group_or_error(usersgroupid)
542 users_group = get_users_group_or_error(usersgroupid)
522
543
523 data = users_group.get_api_data()
544 data = users_group.get_api_data()
524
545
525 members = []
546 members = []
526 for user in users_group.members:
547 for user in users_group.members:
527 user = user.user
548 user = user.user
528 members.append(user.get_api_data())
549 members.append(user.get_api_data())
529 data['members'] = members
550 data['members'] = members
530 return data
551 return data
531
552
532 @HasPermissionAllDecorator('hg.admin')
553 @HasPermissionAllDecorator('hg.admin')
533 def get_users_groups(self, apiuser):
554 def get_users_groups(self, apiuser):
534 """"
555 """"
535 Get all user groups
556 Get all user groups
536
557
537 :param apiuser:
558 :param apiuser:
538 """
559 """
539
560
540 result = []
561 result = []
541 for users_group in UserGroupModel().get_all():
562 for users_group in UserGroupModel().get_all():
542 result.append(users_group.get_api_data())
563 result.append(users_group.get_api_data())
543 return result
564 return result
544
565
545 @HasPermissionAllDecorator('hg.admin')
566 @HasPermissionAllDecorator('hg.admin')
546 def create_users_group(self, apiuser, group_name,
567 def create_users_group(self, apiuser, group_name,
547 owner=Optional(OAttr('apiuser')),
568 owner=Optional(OAttr('apiuser')),
548 active=Optional(True)):
569 active=Optional(True)):
549 """
570 """
550 Creates an new usergroup
571 Creates an new usergroup
551
572
552 :param apiuser:
573 :param apiuser:
553 :param group_name:
574 :param group_name:
554 :param owner:
575 :param owner:
555 :param active:
576 :param active:
556 """
577 """
557
578
558 if UserGroupModel().get_by_name(group_name):
579 if UserGroupModel().get_by_name(group_name):
559 raise JSONRPCError("user group `%s` already exist" % group_name)
580 raise JSONRPCError("user group `%s` already exist" % group_name)
560
581
561 try:
582 try:
562 if isinstance(owner, Optional):
583 if isinstance(owner, Optional):
563 owner = apiuser.user_id
584 owner = apiuser.user_id
564
585
565 owner = get_user_or_error(owner)
586 owner = get_user_or_error(owner)
566 active = Optional.extract(active)
587 active = Optional.extract(active)
567 ug = UserGroupModel().create(name=group_name,
588 ug = UserGroupModel().create(name=group_name,
568 owner=owner,
589 owner=owner,
569 active=active)
590 active=active)
570 Session().commit()
591 Session().commit()
571 return dict(
592 return dict(
572 msg='created new user group `%s`' % group_name,
593 msg='created new user group `%s`' % group_name,
573 users_group=ug.get_api_data()
594 users_group=ug.get_api_data()
574 )
595 )
575 except Exception:
596 except Exception:
576 log.error(traceback.format_exc())
597 log.error(traceback.format_exc())
577 raise JSONRPCError('failed to create group `%s`' % group_name)
598 raise JSONRPCError('failed to create group `%s`' % group_name)
578
599
579 @HasPermissionAllDecorator('hg.admin')
600 @HasPermissionAllDecorator('hg.admin')
580 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
601 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
581 """"
602 """"
582 Add a user to a user group
603 Add a user to a user group
583
604
584 :param apiuser:
605 :param apiuser:
585 :param usersgroupid:
606 :param usersgroupid:
586 :param userid:
607 :param userid:
587 """
608 """
588 user = get_user_or_error(userid)
609 user = get_user_or_error(userid)
589 users_group = get_users_group_or_error(usersgroupid)
610 users_group = get_users_group_or_error(usersgroupid)
590
611
591 try:
612 try:
592 ugm = UserGroupModel().add_user_to_group(users_group, user)
613 ugm = UserGroupModel().add_user_to_group(users_group, user)
593 success = True if ugm != True else False
614 success = True if ugm != True else False
594 msg = 'added member `%s` to user group `%s`' % (
615 msg = 'added member `%s` to user group `%s`' % (
595 user.username, users_group.users_group_name
616 user.username, users_group.users_group_name
596 )
617 )
597 msg = msg if success else 'User is already in that group'
618 msg = msg if success else 'User is already in that group'
598 Session().commit()
619 Session().commit()
599
620
600 return dict(
621 return dict(
601 success=success,
622 success=success,
602 msg=msg
623 msg=msg
603 )
624 )
604 except Exception:
625 except Exception:
605 log.error(traceback.format_exc())
626 log.error(traceback.format_exc())
606 raise JSONRPCError(
627 raise JSONRPCError(
607 'failed to add member to user group `%s`' % (
628 'failed to add member to user group `%s`' % (
608 users_group.users_group_name
629 users_group.users_group_name
609 )
630 )
610 )
631 )
611
632
612 @HasPermissionAllDecorator('hg.admin')
633 @HasPermissionAllDecorator('hg.admin')
613 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
634 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
614 """
635 """
615 Remove user from a group
636 Remove user from a group
616
637
617 :param apiuser:
638 :param apiuser:
618 :param usersgroupid:
639 :param usersgroupid:
619 :param userid:
640 :param userid:
620 """
641 """
621 user = get_user_or_error(userid)
642 user = get_user_or_error(userid)
622 users_group = get_users_group_or_error(usersgroupid)
643 users_group = get_users_group_or_error(usersgroupid)
623
644
624 try:
645 try:
625 success = UserGroupModel().remove_user_from_group(users_group,
646 success = UserGroupModel().remove_user_from_group(users_group,
626 user)
647 user)
627 msg = 'removed member `%s` from user group `%s`' % (
648 msg = 'removed member `%s` from user group `%s`' % (
628 user.username, users_group.users_group_name
649 user.username, users_group.users_group_name
629 )
650 )
630 msg = msg if success else "User wasn't in group"
651 msg = msg if success else "User wasn't in group"
631 Session().commit()
652 Session().commit()
632 return dict(success=success, msg=msg)
653 return dict(success=success, msg=msg)
633 except Exception:
654 except Exception:
634 log.error(traceback.format_exc())
655 log.error(traceback.format_exc())
635 raise JSONRPCError(
656 raise JSONRPCError(
636 'failed to remove member from user group `%s`' % (
657 'failed to remove member from user group `%s`' % (
637 users_group.users_group_name
658 users_group.users_group_name
638 )
659 )
639 )
660 )
640
661
641 def get_repo(self, apiuser, repoid):
662 def get_repo(self, apiuser, repoid):
642 """"
663 """"
643 Get repository by name
664 Get repository by name
644
665
645 :param apiuser:
666 :param apiuser:
646 :param repoid:
667 :param repoid:
647 """
668 """
648 repo = get_repo_or_error(repoid)
669 repo = get_repo_or_error(repoid)
649
670
650 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
671 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
651 # check if we have admin permission for this repo !
672 # check if we have admin permission for this repo !
652 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
673 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
653 repo_name=repo.repo_name) is False:
674 repo_name=repo.repo_name) is False:
654 raise JSONRPCError('repository `%s` does not exist' % (repoid))
675 raise JSONRPCError('repository `%s` does not exist' % (repoid))
655
676
656 members = []
677 members = []
657 followers = []
678 followers = []
658 for user in repo.repo_to_perm:
679 for user in repo.repo_to_perm:
659 perm = user.permission.permission_name
680 perm = user.permission.permission_name
660 user = user.user
681 user = user.user
661 user_data = user.get_api_data()
682 user_data = user.get_api_data()
662 user_data['type'] = "user"
683 user_data['type'] = "user"
663 user_data['permission'] = perm
684 user_data['permission'] = perm
664 members.append(user_data)
685 members.append(user_data)
665
686
666 for users_group in repo.users_group_to_perm:
687 for users_group in repo.users_group_to_perm:
667 perm = users_group.permission.permission_name
688 perm = users_group.permission.permission_name
668 users_group = users_group.users_group
689 users_group = users_group.users_group
669 users_group_data = users_group.get_api_data()
690 users_group_data = users_group.get_api_data()
670 users_group_data['type'] = "users_group"
691 users_group_data['type'] = "users_group"
671 users_group_data['permission'] = perm
692 users_group_data['permission'] = perm
672 members.append(users_group_data)
693 members.append(users_group_data)
673
694
674 for user in repo.followers:
695 for user in repo.followers:
675 followers.append(user.user.get_api_data())
696 followers.append(user.user.get_api_data())
676
697
677 data = repo.get_api_data()
698 data = repo.get_api_data()
678 data['members'] = members
699 data['members'] = members
679 data['followers'] = followers
700 data['followers'] = followers
680 return data
701 return data
681
702
682 def get_repos(self, apiuser):
703 def get_repos(self, apiuser):
683 """"
704 """"
684 Get all repositories
705 Get all repositories
685
706
686 :param apiuser:
707 :param apiuser:
687 """
708 """
688 result = []
709 result = []
689 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
710 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
690 repos = RepoModel().get_all_user_repos(user=apiuser)
711 repos = RepoModel().get_all_user_repos(user=apiuser)
691 else:
712 else:
692 repos = RepoModel().get_all()
713 repos = RepoModel().get_all()
693
714
694 for repo in repos:
715 for repo in repos:
695 result.append(repo.get_api_data())
716 result.append(repo.get_api_data())
696 return result
717 return result
697
718
698 @HasPermissionAllDecorator('hg.admin')
719 @HasPermissionAllDecorator('hg.admin')
699 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
720 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
700 ret_type='all'):
721 ret_type='all'):
701 """
722 """
702 returns a list of nodes and it's children
723 returns a list of nodes and it's children
703 for a given path at given revision. It's possible to specify ret_type
724 for a given path at given revision. It's possible to specify ret_type
704 to show only files or dirs
725 to show only files or dirs
705
726
706 :param apiuser:
727 :param apiuser:
707 :param repoid: name or id of repository
728 :param repoid: name or id of repository
708 :param revision: revision for which listing should be done
729 :param revision: revision for which listing should be done
709 :param root_path: path from which start displaying
730 :param root_path: path from which start displaying
710 :param ret_type: return type 'all|files|dirs' nodes
731 :param ret_type: return type 'all|files|dirs' nodes
711 """
732 """
712 repo = get_repo_or_error(repoid)
733 repo = get_repo_or_error(repoid)
713 try:
734 try:
714 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
735 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
715 flat=False)
736 flat=False)
716 _map = {
737 _map = {
717 'all': _d + _f,
738 'all': _d + _f,
718 'files': _f,
739 'files': _f,
719 'dirs': _d,
740 'dirs': _d,
720 }
741 }
721 return _map[ret_type]
742 return _map[ret_type]
722 except KeyError:
743 except KeyError:
723 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
744 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
724 except Exception:
745 except Exception:
725 log.error(traceback.format_exc())
746 log.error(traceback.format_exc())
726 raise JSONRPCError(
747 raise JSONRPCError(
727 'failed to get repo: `%s` nodes' % repo.repo_name
748 'failed to get repo: `%s` nodes' % repo.repo_name
728 )
749 )
729
750
730 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
751 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
731 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
752 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
732 repo_type=Optional('hg'),
753 repo_type=Optional('hg'),
733 description=Optional(''), private=Optional(False),
754 description=Optional(''), private=Optional(False),
734 clone_uri=Optional(None), landing_rev=Optional('tip'),
755 clone_uri=Optional(None), landing_rev=Optional('tip'),
735 enable_statistics=Optional(False),
756 enable_statistics=Optional(False),
736 enable_locking=Optional(False),
757 enable_locking=Optional(False),
737 enable_downloads=Optional(False)):
758 enable_downloads=Optional(False)):
738 """
759 """
739 Create repository, if clone_url is given it makes a remote clone
760 Create repository, if clone_url is given it makes a remote clone
740 if repo_name is within a group name the groups will be created
761 if repo_name is within a group name the groups will be created
741 automatically if they aren't present
762 automatically if they aren't present
742
763
743 :param apiuser:
764 :param apiuser:
744 :param repo_name:
765 :param repo_name:
745 :param onwer:
766 :param onwer:
746 :param repo_type:
767 :param repo_type:
747 :param description:
768 :param description:
748 :param private:
769 :param private:
749 :param clone_uri:
770 :param clone_uri:
750 :param landing_rev:
771 :param landing_rev:
751 """
772 """
752 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
773 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
753 if not isinstance(owner, Optional):
774 if not isinstance(owner, Optional):
754 #forbid setting owner for non-admins
775 #forbid setting owner for non-admins
755 raise JSONRPCError(
776 raise JSONRPCError(
756 'Only RhodeCode admin can specify `owner` param'
777 'Only RhodeCode admin can specify `owner` param'
757 )
778 )
758 if isinstance(owner, Optional):
779 if isinstance(owner, Optional):
759 owner = apiuser.user_id
780 owner = apiuser.user_id
760
781
761 owner = get_user_or_error(owner)
782 owner = get_user_or_error(owner)
762
783
763 if RepoModel().get_by_repo_name(repo_name):
784 if RepoModel().get_by_repo_name(repo_name):
764 raise JSONRPCError("repo `%s` already exist" % repo_name)
785 raise JSONRPCError("repo `%s` already exist" % repo_name)
765
786
766 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
787 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
767 if isinstance(private, Optional):
788 if isinstance(private, Optional):
768 private = defs.get('repo_private') or Optional.extract(private)
789 private = defs.get('repo_private') or Optional.extract(private)
769 if isinstance(repo_type, Optional):
790 if isinstance(repo_type, Optional):
770 repo_type = defs.get('repo_type')
791 repo_type = defs.get('repo_type')
771 if isinstance(enable_statistics, Optional):
792 if isinstance(enable_statistics, Optional):
772 enable_statistics = defs.get('repo_enable_statistics')
793 enable_statistics = defs.get('repo_enable_statistics')
773 if isinstance(enable_locking, Optional):
794 if isinstance(enable_locking, Optional):
774 enable_locking = defs.get('repo_enable_locking')
795 enable_locking = defs.get('repo_enable_locking')
775 if isinstance(enable_downloads, Optional):
796 if isinstance(enable_downloads, Optional):
776 enable_downloads = defs.get('repo_enable_downloads')
797 enable_downloads = defs.get('repo_enable_downloads')
777
798
778 clone_uri = Optional.extract(clone_uri)
799 clone_uri = Optional.extract(clone_uri)
779 description = Optional.extract(description)
800 description = Optional.extract(description)
780 landing_rev = Optional.extract(landing_rev)
801 landing_rev = Optional.extract(landing_rev)
781
802
782 try:
803 try:
783 # create structure of groups and return the last group
804 # create structure of groups and return the last group
784 group = map_groups(repo_name)
805 group = map_groups(repo_name)
785
806
786 repo = RepoModel().create_repo(
807 repo = RepoModel().create_repo(
787 repo_name=repo_name,
808 repo_name=repo_name,
788 repo_type=repo_type,
809 repo_type=repo_type,
789 description=description,
810 description=description,
790 owner=owner,
811 owner=owner,
791 private=private,
812 private=private,
792 clone_uri=clone_uri,
813 clone_uri=clone_uri,
793 repos_group=group,
814 repos_group=group,
794 landing_rev=landing_rev,
815 landing_rev=landing_rev,
795 enable_statistics=enable_statistics,
816 enable_statistics=enable_statistics,
796 enable_downloads=enable_downloads,
817 enable_downloads=enable_downloads,
797 enable_locking=enable_locking
818 enable_locking=enable_locking
798 )
819 )
799
820
800 Session().commit()
821 Session().commit()
801 return dict(
822 return dict(
802 msg="Created new repository `%s`" % (repo.repo_name),
823 msg="Created new repository `%s`" % (repo.repo_name),
803 repo=repo.get_api_data()
824 repo=repo.get_api_data()
804 )
825 )
805 except Exception:
826 except Exception:
806 log.error(traceback.format_exc())
827 log.error(traceback.format_exc())
807 raise JSONRPCError('failed to create repository `%s`' % repo_name)
828 raise JSONRPCError('failed to create repository `%s`' % repo_name)
808
829
809 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
830 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
810 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
831 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
811 description=Optional(''), copy_permissions=Optional(False),
832 description=Optional(''), copy_permissions=Optional(False),
812 private=Optional(False), landing_rev=Optional('tip')):
833 private=Optional(False), landing_rev=Optional('tip')):
813 repo = get_repo_or_error(repoid)
834 repo = get_repo_or_error(repoid)
814 repo_name = repo.repo_name
835 repo_name = repo.repo_name
815
836
816 _repo = RepoModel().get_by_repo_name(fork_name)
837 _repo = RepoModel().get_by_repo_name(fork_name)
817 if _repo:
838 if _repo:
818 type_ = 'fork' if _repo.fork else 'repo'
839 type_ = 'fork' if _repo.fork else 'repo'
819 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
840 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
820
841
821 if HasPermissionAnyApi('hg.admin')(user=apiuser):
842 if HasPermissionAnyApi('hg.admin')(user=apiuser):
822 pass
843 pass
823 elif HasRepoPermissionAnyApi('repository.admin',
844 elif HasRepoPermissionAnyApi('repository.admin',
824 'repository.write',
845 'repository.write',
825 'repository.read')(user=apiuser,
846 'repository.read')(user=apiuser,
826 repo_name=repo.repo_name):
847 repo_name=repo.repo_name):
827 if not isinstance(owner, Optional):
848 if not isinstance(owner, Optional):
828 #forbid setting owner for non-admins
849 #forbid setting owner for non-admins
829 raise JSONRPCError(
850 raise JSONRPCError(
830 'Only RhodeCode admin can specify `owner` param'
851 'Only RhodeCode admin can specify `owner` param'
831 )
852 )
832 else:
853 else:
833 raise JSONRPCError('repository `%s` does not exist' % (repoid))
854 raise JSONRPCError('repository `%s` does not exist' % (repoid))
834
855
835 if isinstance(owner, Optional):
856 if isinstance(owner, Optional):
836 owner = apiuser.user_id
857 owner = apiuser.user_id
837
858
838 owner = get_user_or_error(owner)
859 owner = get_user_or_error(owner)
839
860
840 try:
861 try:
841 # create structure of groups and return the last group
862 # create structure of groups and return the last group
842 group = map_groups(fork_name)
863 group = map_groups(fork_name)
843
864
844 form_data = dict(
865 form_data = dict(
845 repo_name=fork_name,
866 repo_name=fork_name,
846 repo_name_full=fork_name,
867 repo_name_full=fork_name,
847 repo_group=group,
868 repo_group=group,
848 repo_type=repo.repo_type,
869 repo_type=repo.repo_type,
849 description=Optional.extract(description),
870 description=Optional.extract(description),
850 private=Optional.extract(private),
871 private=Optional.extract(private),
851 copy_permissions=Optional.extract(copy_permissions),
872 copy_permissions=Optional.extract(copy_permissions),
852 landing_rev=Optional.extract(landing_rev),
873 landing_rev=Optional.extract(landing_rev),
853 update_after_clone=False,
874 update_after_clone=False,
854 fork_parent_id=repo.repo_id,
875 fork_parent_id=repo.repo_id,
855 )
876 )
856 RepoModel().create_fork(form_data, cur_user=owner)
877 RepoModel().create_fork(form_data, cur_user=owner)
857 return dict(
878 return dict(
858 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
879 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
859 fork_name),
880 fork_name),
860 success=True # cannot return the repo data here since fork
881 success=True # cannot return the repo data here since fork
861 # cann be done async
882 # cann be done async
862 )
883 )
863 except Exception:
884 except Exception:
864 log.error(traceback.format_exc())
885 log.error(traceback.format_exc())
865 raise JSONRPCError(
886 raise JSONRPCError(
866 'failed to fork repository `%s` as `%s`' % (repo_name,
887 'failed to fork repository `%s` as `%s`' % (repo_name,
867 fork_name)
888 fork_name)
868 )
889 )
869
890
870 def delete_repo(self, apiuser, repoid, forks=Optional(None)):
891 def delete_repo(self, apiuser, repoid, forks=Optional(None)):
871 """
892 """
872 Deletes a given repository
893 Deletes a given repository
873
894
874 :param apiuser:
895 :param apiuser:
875 :param repoid:
896 :param repoid:
876 :param forks: detach or delete, what do do with attached forks for repo
897 :param forks: detach or delete, what do do with attached forks for repo
877 """
898 """
878 repo = get_repo_or_error(repoid)
899 repo = get_repo_or_error(repoid)
879
900
880 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
901 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
881 # check if we have admin permission for this repo !
902 # check if we have admin permission for this repo !
882 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
903 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
883 repo_name=repo.repo_name) is False:
904 repo_name=repo.repo_name) is False:
884 raise JSONRPCError('repository `%s` does not exist' % (repoid))
905 raise JSONRPCError('repository `%s` does not exist' % (repoid))
885
906
886 try:
907 try:
887 handle_forks = Optional.extract(forks)
908 handle_forks = Optional.extract(forks)
888 _forks_msg = ''
909 _forks_msg = ''
889 _forks = [f for f in repo.forks]
910 _forks = [f for f in repo.forks]
890 if handle_forks == 'detach':
911 if handle_forks == 'detach':
891 _forks_msg = ' ' + _('Detached %s forks') % len(_forks)
912 _forks_msg = ' ' + _('Detached %s forks') % len(_forks)
892 elif handle_forks == 'delete':
913 elif handle_forks == 'delete':
893 _forks_msg = ' ' + _('Deleted %s forks') % len(_forks)
914 _forks_msg = ' ' + _('Deleted %s forks') % len(_forks)
894 elif _forks:
915 elif _forks:
895 raise JSONRPCError(
916 raise JSONRPCError(
896 'Cannot delete `%s` it still contains attached forks'
917 'Cannot delete `%s` it still contains attached forks'
897 % repo.repo_name
918 % repo.repo_name
898 )
919 )
899
920
900 RepoModel().delete(repo, forks=forks)
921 RepoModel().delete(repo, forks=forks)
901 Session().commit()
922 Session().commit()
902 return dict(
923 return dict(
903 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
924 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
904 success=True
925 success=True
905 )
926 )
906 except Exception:
927 except Exception:
907 log.error(traceback.format_exc())
928 log.error(traceback.format_exc())
908 raise JSONRPCError(
929 raise JSONRPCError(
909 'failed to delete repository `%s`' % repo.repo_name
930 'failed to delete repository `%s`' % repo.repo_name
910 )
931 )
911
932
912 @HasPermissionAllDecorator('hg.admin')
933 @HasPermissionAllDecorator('hg.admin')
913 def grant_user_permission(self, apiuser, repoid, userid, perm):
934 def grant_user_permission(self, apiuser, repoid, userid, perm):
914 """
935 """
915 Grant permission for user on given repository, or update existing one
936 Grant permission for user on given repository, or update existing one
916 if found
937 if found
917
938
918 :param repoid:
939 :param repoid:
919 :param userid:
940 :param userid:
920 :param perm:
941 :param perm:
921 """
942 """
922 repo = get_repo_or_error(repoid)
943 repo = get_repo_or_error(repoid)
923 user = get_user_or_error(userid)
944 user = get_user_or_error(userid)
924 perm = get_perm_or_error(perm)
945 perm = get_perm_or_error(perm)
925
946
926 try:
947 try:
927
948
928 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
949 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
929
950
930 Session().commit()
951 Session().commit()
931 return dict(
952 return dict(
932 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
953 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
933 perm.permission_name, user.username, repo.repo_name
954 perm.permission_name, user.username, repo.repo_name
934 ),
955 ),
935 success=True
956 success=True
936 )
957 )
937 except Exception:
958 except Exception:
938 log.error(traceback.format_exc())
959 log.error(traceback.format_exc())
939 raise JSONRPCError(
960 raise JSONRPCError(
940 'failed to edit permission for user: `%s` in repo: `%s`' % (
961 'failed to edit permission for user: `%s` in repo: `%s`' % (
941 userid, repoid
962 userid, repoid
942 )
963 )
943 )
964 )
944
965
945 @HasPermissionAllDecorator('hg.admin')
966 @HasPermissionAllDecorator('hg.admin')
946 def revoke_user_permission(self, apiuser, repoid, userid):
967 def revoke_user_permission(self, apiuser, repoid, userid):
947 """
968 """
948 Revoke permission for user on given repository
969 Revoke permission for user on given repository
949
970
950 :param apiuser:
971 :param apiuser:
951 :param repoid:
972 :param repoid:
952 :param userid:
973 :param userid:
953 """
974 """
954
975
955 repo = get_repo_or_error(repoid)
976 repo = get_repo_or_error(repoid)
956 user = get_user_or_error(userid)
977 user = get_user_or_error(userid)
957 try:
978 try:
958
979
959 RepoModel().revoke_user_permission(repo=repo, user=user)
980 RepoModel().revoke_user_permission(repo=repo, user=user)
960
981
961 Session().commit()
982 Session().commit()
962 return dict(
983 return dict(
963 msg='Revoked perm for user: `%s` in repo: `%s`' % (
984 msg='Revoked perm for user: `%s` in repo: `%s`' % (
964 user.username, repo.repo_name
985 user.username, repo.repo_name
965 ),
986 ),
966 success=True
987 success=True
967 )
988 )
968 except Exception:
989 except Exception:
969 log.error(traceback.format_exc())
990 log.error(traceback.format_exc())
970 raise JSONRPCError(
991 raise JSONRPCError(
971 'failed to edit permission for user: `%s` in repo: `%s`' % (
992 'failed to edit permission for user: `%s` in repo: `%s`' % (
972 userid, repoid
993 userid, repoid
973 )
994 )
974 )
995 )
975
996
976 @HasPermissionAllDecorator('hg.admin')
997 @HasPermissionAllDecorator('hg.admin')
977 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
998 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
978 perm):
999 perm):
979 """
1000 """
980 Grant permission for user group on given repository, or update
1001 Grant permission for user group on given repository, or update
981 existing one if found
1002 existing one if found
982
1003
983 :param apiuser:
1004 :param apiuser:
984 :param repoid:
1005 :param repoid:
985 :param usersgroupid:
1006 :param usersgroupid:
986 :param perm:
1007 :param perm:
987 """
1008 """
988 repo = get_repo_or_error(repoid)
1009 repo = get_repo_or_error(repoid)
989 perm = get_perm_or_error(perm)
1010 perm = get_perm_or_error(perm)
990 users_group = get_users_group_or_error(usersgroupid)
1011 users_group = get_users_group_or_error(usersgroupid)
991
1012
992 try:
1013 try:
993 RepoModel().grant_users_group_permission(repo=repo,
1014 RepoModel().grant_users_group_permission(repo=repo,
994 group_name=users_group,
1015 group_name=users_group,
995 perm=perm)
1016 perm=perm)
996
1017
997 Session().commit()
1018 Session().commit()
998 return dict(
1019 return dict(
999 msg='Granted perm: `%s` for user group: `%s` in '
1020 msg='Granted perm: `%s` for user group: `%s` in '
1000 'repo: `%s`' % (
1021 'repo: `%s`' % (
1001 perm.permission_name, users_group.users_group_name,
1022 perm.permission_name, users_group.users_group_name,
1002 repo.repo_name
1023 repo.repo_name
1003 ),
1024 ),
1004 success=True
1025 success=True
1005 )
1026 )
1006 except Exception:
1027 except Exception:
1007 log.error(traceback.format_exc())
1028 log.error(traceback.format_exc())
1008 raise JSONRPCError(
1029 raise JSONRPCError(
1009 'failed to edit permission for user group: `%s` in '
1030 'failed to edit permission for user group: `%s` in '
1010 'repo: `%s`' % (
1031 'repo: `%s`' % (
1011 usersgroupid, repo.repo_name
1032 usersgroupid, repo.repo_name
1012 )
1033 )
1013 )
1034 )
1014
1035
1015 @HasPermissionAllDecorator('hg.admin')
1036 @HasPermissionAllDecorator('hg.admin')
1016 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
1037 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
1017 """
1038 """
1018 Revoke permission for user group on given repository
1039 Revoke permission for user group on given repository
1019
1040
1020 :param apiuser:
1041 :param apiuser:
1021 :param repoid:
1042 :param repoid:
1022 :param usersgroupid:
1043 :param usersgroupid:
1023 """
1044 """
1024 repo = get_repo_or_error(repoid)
1045 repo = get_repo_or_error(repoid)
1025 users_group = get_users_group_or_error(usersgroupid)
1046 users_group = get_users_group_or_error(usersgroupid)
1026
1047
1027 try:
1048 try:
1028 RepoModel().revoke_users_group_permission(repo=repo,
1049 RepoModel().revoke_users_group_permission(repo=repo,
1029 group_name=users_group)
1050 group_name=users_group)
1030
1051
1031 Session().commit()
1052 Session().commit()
1032 return dict(
1053 return dict(
1033 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1054 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1034 users_group.users_group_name, repo.repo_name
1055 users_group.users_group_name, repo.repo_name
1035 ),
1056 ),
1036 success=True
1057 success=True
1037 )
1058 )
1038 except Exception:
1059 except Exception:
1039 log.error(traceback.format_exc())
1060 log.error(traceback.format_exc())
1040 raise JSONRPCError(
1061 raise JSONRPCError(
1041 'failed to edit permission for user group: `%s` in '
1062 'failed to edit permission for user group: `%s` in '
1042 'repo: `%s`' % (
1063 'repo: `%s`' % (
1043 users_group.users_group_name, repo.repo_name
1064 users_group.users_group_name, repo.repo_name
1044 )
1065 )
1045 )
1066 )
@@ -1,2131 +1,2133 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.db
3 rhodecode.model.db
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 Database Models for RhodeCode
6 Database Models for RhodeCode
7
7
8 :created_on: Apr 08, 2010
8 :created_on: Apr 08, 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 os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import hashlib
30 import hashlib
31 import time
31 import time
32 from collections import defaultdict
32 from collections import defaultdict
33
33
34 from sqlalchemy import *
34 from sqlalchemy import *
35 from sqlalchemy.ext.hybrid import hybrid_property
35 from sqlalchemy.ext.hybrid import hybrid_property
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
36 from sqlalchemy.orm import relationship, joinedload, class_mapper, validates
37 from sqlalchemy.exc import DatabaseError
37 from sqlalchemy.exc import DatabaseError
38 from beaker.cache import cache_region, region_invalidate
38 from beaker.cache import cache_region, region_invalidate
39 from webob.exc import HTTPNotFound
39 from webob.exc import HTTPNotFound
40
40
41 from pylons.i18n.translation import lazy_ugettext as _
41 from pylons.i18n.translation import lazy_ugettext as _
42
42
43 from rhodecode.lib.vcs import get_backend
43 from rhodecode.lib.vcs import get_backend
44 from rhodecode.lib.vcs.utils.helpers import get_scm
44 from rhodecode.lib.vcs.utils.helpers import get_scm
45 from rhodecode.lib.vcs.exceptions import VCSError
45 from rhodecode.lib.vcs.exceptions import VCSError
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
46 from rhodecode.lib.vcs.utils.lazy import LazyProperty
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
47 from rhodecode.lib.vcs.backends.base import EmptyChangeset
48
48
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
49 from rhodecode.lib.utils2 import str2bool, safe_str, get_changeset_safe, \
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
50 safe_unicode, remove_suffix, remove_prefix, time_to_datetime, _set_extras
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 from rhodecode.model.meta import Base, Session
54 from rhodecode.model.meta import Base, Session
55
55
56 URL_SEP = '/'
56 URL_SEP = '/'
57 log = logging.getLogger(__name__)
57 log = logging.getLogger(__name__)
58
58
59 #==============================================================================
59 #==============================================================================
60 # BASE CLASSES
60 # BASE CLASSES
61 #==============================================================================
61 #==============================================================================
62
62
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
63 _hash_key = lambda k: hashlib.md5(safe_str(k)).hexdigest()
64
64
65
65
66 class BaseModel(object):
66 class BaseModel(object):
67 """
67 """
68 Base Model for all classess
68 Base Model for all classess
69 """
69 """
70
70
71 @classmethod
71 @classmethod
72 def _get_keys(cls):
72 def _get_keys(cls):
73 """return column names for this model """
73 """return column names for this model """
74 return class_mapper(cls).c.keys()
74 return class_mapper(cls).c.keys()
75
75
76 def get_dict(self):
76 def get_dict(self):
77 """
77 """
78 return dict with keys and values corresponding
78 return dict with keys and values corresponding
79 to this model data """
79 to this model data """
80
80
81 d = {}
81 d = {}
82 for k in self._get_keys():
82 for k in self._get_keys():
83 d[k] = getattr(self, k)
83 d[k] = getattr(self, k)
84
84
85 # also use __json__() if present to get additional fields
85 # also use __json__() if present to get additional fields
86 _json_attr = getattr(self, '__json__', None)
86 _json_attr = getattr(self, '__json__', None)
87 if _json_attr:
87 if _json_attr:
88 # update with attributes from __json__
88 # update with attributes from __json__
89 if callable(_json_attr):
89 if callable(_json_attr):
90 _json_attr = _json_attr()
90 _json_attr = _json_attr()
91 for k, val in _json_attr.iteritems():
91 for k, val in _json_attr.iteritems():
92 d[k] = val
92 d[k] = val
93 return d
93 return d
94
94
95 def get_appstruct(self):
95 def get_appstruct(self):
96 """return list with keys and values tupples corresponding
96 """return list with keys and values tupples corresponding
97 to this model data """
97 to this model data """
98
98
99 l = []
99 l = []
100 for k in self._get_keys():
100 for k in self._get_keys():
101 l.append((k, getattr(self, k),))
101 l.append((k, getattr(self, k),))
102 return l
102 return l
103
103
104 def populate_obj(self, populate_dict):
104 def populate_obj(self, populate_dict):
105 """populate model with data from given populate_dict"""
105 """populate model with data from given populate_dict"""
106
106
107 for k in self._get_keys():
107 for k in self._get_keys():
108 if k in populate_dict:
108 if k in populate_dict:
109 setattr(self, k, populate_dict[k])
109 setattr(self, k, populate_dict[k])
110
110
111 @classmethod
111 @classmethod
112 def query(cls):
112 def query(cls):
113 return Session().query(cls)
113 return Session().query(cls)
114
114
115 @classmethod
115 @classmethod
116 def get(cls, id_):
116 def get(cls, id_):
117 if id_:
117 if id_:
118 return cls.query().get(id_)
118 return cls.query().get(id_)
119
119
120 @classmethod
120 @classmethod
121 def get_or_404(cls, id_):
121 def get_or_404(cls, id_):
122 try:
122 try:
123 id_ = int(id_)
123 id_ = int(id_)
124 except (TypeError, ValueError):
124 except (TypeError, ValueError):
125 raise HTTPNotFound
125 raise HTTPNotFound
126
126
127 res = cls.query().get(id_)
127 res = cls.query().get(id_)
128 if not res:
128 if not res:
129 raise HTTPNotFound
129 raise HTTPNotFound
130 return res
130 return res
131
131
132 @classmethod
132 @classmethod
133 def getAll(cls):
133 def getAll(cls):
134 # deprecated and left for backward compatibility
134 # deprecated and left for backward compatibility
135 return cls.get_all()
135 return cls.get_all()
136
136
137 @classmethod
137 @classmethod
138 def get_all(cls):
138 def get_all(cls):
139 return cls.query().all()
139 return cls.query().all()
140
140
141 @classmethod
141 @classmethod
142 def delete(cls, id_):
142 def delete(cls, id_):
143 obj = cls.query().get(id_)
143 obj = cls.query().get(id_)
144 Session().delete(obj)
144 Session().delete(obj)
145
145
146 def __repr__(self):
146 def __repr__(self):
147 if hasattr(self, '__unicode__'):
147 if hasattr(self, '__unicode__'):
148 # python repr needs to return str
148 # python repr needs to return str
149 return safe_str(self.__unicode__())
149 return safe_str(self.__unicode__())
150 return '<DB:%s>' % (self.__class__.__name__)
150 return '<DB:%s>' % (self.__class__.__name__)
151
151
152
152
153 class RhodeCodeSetting(Base, BaseModel):
153 class RhodeCodeSetting(Base, BaseModel):
154 __tablename__ = 'rhodecode_settings'
154 __tablename__ = 'rhodecode_settings'
155 __table_args__ = (
155 __table_args__ = (
156 UniqueConstraint('app_settings_name'),
156 UniqueConstraint('app_settings_name'),
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
157 {'extend_existing': True, 'mysql_engine': 'InnoDB',
158 'mysql_charset': 'utf8'}
158 'mysql_charset': 'utf8'}
159 )
159 )
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
160 app_settings_id = Column("app_settings_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
161 app_settings_name = Column("app_settings_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
162 _app_settings_value = Column("app_settings_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
163
163
164 def __init__(self, k='', v=''):
164 def __init__(self, k='', v=''):
165 self.app_settings_name = k
165 self.app_settings_name = k
166 self.app_settings_value = v
166 self.app_settings_value = v
167
167
168 @validates('_app_settings_value')
168 @validates('_app_settings_value')
169 def validate_settings_value(self, key, val):
169 def validate_settings_value(self, key, val):
170 assert type(val) == unicode
170 assert type(val) == unicode
171 return val
171 return val
172
172
173 @hybrid_property
173 @hybrid_property
174 def app_settings_value(self):
174 def app_settings_value(self):
175 v = self._app_settings_value
175 v = self._app_settings_value
176 if self.app_settings_name in ["ldap_active",
176 if self.app_settings_name in ["ldap_active",
177 "default_repo_enable_statistics",
177 "default_repo_enable_statistics",
178 "default_repo_enable_locking",
178 "default_repo_enable_locking",
179 "default_repo_private",
179 "default_repo_private",
180 "default_repo_enable_downloads"]:
180 "default_repo_enable_downloads"]:
181 v = str2bool(v)
181 v = str2bool(v)
182 return v
182 return v
183
183
184 @app_settings_value.setter
184 @app_settings_value.setter
185 def app_settings_value(self, val):
185 def app_settings_value(self, val):
186 """
186 """
187 Setter that will always make sure we use unicode in app_settings_value
187 Setter that will always make sure we use unicode in app_settings_value
188
188
189 :param val:
189 :param val:
190 """
190 """
191 self._app_settings_value = safe_unicode(val)
191 self._app_settings_value = safe_unicode(val)
192
192
193 def __unicode__(self):
193 def __unicode__(self):
194 return u"<%s('%s:%s')>" % (
194 return u"<%s('%s:%s')>" % (
195 self.__class__.__name__,
195 self.__class__.__name__,
196 self.app_settings_name, self.app_settings_value
196 self.app_settings_name, self.app_settings_value
197 )
197 )
198
198
199 @classmethod
199 @classmethod
200 def get_by_name(cls, key):
200 def get_by_name(cls, key):
201 return cls.query()\
201 return cls.query()\
202 .filter(cls.app_settings_name == key).scalar()
202 .filter(cls.app_settings_name == key).scalar()
203
203
204 @classmethod
204 @classmethod
205 def get_by_name_or_create(cls, key):
205 def get_by_name_or_create(cls, key):
206 res = cls.get_by_name(key)
206 res = cls.get_by_name(key)
207 if not res:
207 if not res:
208 res = cls(key)
208 res = cls(key)
209 return res
209 return res
210
210
211 @classmethod
211 @classmethod
212 def get_app_settings(cls, cache=False):
212 def get_app_settings(cls, cache=False):
213
213
214 ret = cls.query()
214 ret = cls.query()
215
215
216 if cache:
216 if cache:
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
217 ret = ret.options(FromCache("sql_cache_short", "get_hg_settings"))
218
218
219 if not ret:
219 if not ret:
220 raise Exception('Could not get application settings !')
220 raise Exception('Could not get application settings !')
221 settings = {}
221 settings = {}
222 for each in ret:
222 for each in ret:
223 settings['rhodecode_' + each.app_settings_name] = \
223 settings['rhodecode_' + each.app_settings_name] = \
224 each.app_settings_value
224 each.app_settings_value
225
225
226 return settings
226 return settings
227
227
228 @classmethod
228 @classmethod
229 def get_ldap_settings(cls, cache=False):
229 def get_ldap_settings(cls, cache=False):
230 ret = cls.query()\
230 ret = cls.query()\
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
231 .filter(cls.app_settings_name.startswith('ldap_')).all()
232 fd = {}
232 fd = {}
233 for row in ret:
233 for row in ret:
234 fd.update({row.app_settings_name: row.app_settings_value})
234 fd.update({row.app_settings_name: row.app_settings_value})
235
235
236 return fd
236 return fd
237
237
238 @classmethod
238 @classmethod
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
239 def get_default_repo_settings(cls, cache=False, strip_prefix=False):
240 ret = cls.query()\
240 ret = cls.query()\
241 .filter(cls.app_settings_name.startswith('default_')).all()
241 .filter(cls.app_settings_name.startswith('default_')).all()
242 fd = {}
242 fd = {}
243 for row in ret:
243 for row in ret:
244 key = row.app_settings_name
244 key = row.app_settings_name
245 if strip_prefix:
245 if strip_prefix:
246 key = remove_prefix(key, prefix='default_')
246 key = remove_prefix(key, prefix='default_')
247 fd.update({key: row.app_settings_value})
247 fd.update({key: row.app_settings_value})
248
248
249 return fd
249 return fd
250
250
251
251
252 class RhodeCodeUi(Base, BaseModel):
252 class RhodeCodeUi(Base, BaseModel):
253 __tablename__ = 'rhodecode_ui'
253 __tablename__ = 'rhodecode_ui'
254 __table_args__ = (
254 __table_args__ = (
255 UniqueConstraint('ui_key'),
255 UniqueConstraint('ui_key'),
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
256 {'extend_existing': True, 'mysql_engine': 'InnoDB',
257 'mysql_charset': 'utf8'}
257 'mysql_charset': 'utf8'}
258 )
258 )
259
259
260 HOOK_UPDATE = 'changegroup.update'
260 HOOK_UPDATE = 'changegroup.update'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
261 HOOK_REPO_SIZE = 'changegroup.repo_size'
262 HOOK_PUSH = 'changegroup.push_logger'
262 HOOK_PUSH = 'changegroup.push_logger'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
263 HOOK_PRE_PUSH = 'prechangegroup.pre_push'
264 HOOK_PULL = 'outgoing.pull_logger'
264 HOOK_PULL = 'outgoing.pull_logger'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
265 HOOK_PRE_PULL = 'preoutgoing.pre_pull'
266
266
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
267 ui_id = Column("ui_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
268 ui_section = Column("ui_section", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
269 ui_key = Column("ui_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
270 ui_value = Column("ui_value", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
271 ui_active = Column("ui_active", Boolean(), nullable=True, unique=None, default=True)
272
272
273 @classmethod
273 @classmethod
274 def get_by_key(cls, key):
274 def get_by_key(cls, key):
275 return cls.query().filter(cls.ui_key == key).scalar()
275 return cls.query().filter(cls.ui_key == key).scalar()
276
276
277 @classmethod
277 @classmethod
278 def get_builtin_hooks(cls):
278 def get_builtin_hooks(cls):
279 q = cls.query()
279 q = cls.query()
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
280 q = q.filter(cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
281 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
282 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
283 return q.all()
283 return q.all()
284
284
285 @classmethod
285 @classmethod
286 def get_custom_hooks(cls):
286 def get_custom_hooks(cls):
287 q = cls.query()
287 q = cls.query()
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
288 q = q.filter(~cls.ui_key.in_([cls.HOOK_UPDATE, cls.HOOK_REPO_SIZE,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
289 cls.HOOK_PUSH, cls.HOOK_PRE_PUSH,
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
290 cls.HOOK_PULL, cls.HOOK_PRE_PULL]))
291 q = q.filter(cls.ui_section == 'hooks')
291 q = q.filter(cls.ui_section == 'hooks')
292 return q.all()
292 return q.all()
293
293
294 @classmethod
294 @classmethod
295 def get_repos_location(cls):
295 def get_repos_location(cls):
296 return cls.get_by_key('/').ui_value
296 return cls.get_by_key('/').ui_value
297
297
298 @classmethod
298 @classmethod
299 def create_or_update_hook(cls, key, val):
299 def create_or_update_hook(cls, key, val):
300 new_ui = cls.get_by_key(key) or cls()
300 new_ui = cls.get_by_key(key) or cls()
301 new_ui.ui_section = 'hooks'
301 new_ui.ui_section = 'hooks'
302 new_ui.ui_active = True
302 new_ui.ui_active = True
303 new_ui.ui_key = key
303 new_ui.ui_key = key
304 new_ui.ui_value = val
304 new_ui.ui_value = val
305
305
306 Session().add(new_ui)
306 Session().add(new_ui)
307
307
308 def __repr__(self):
308 def __repr__(self):
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
309 return '<DB:%s[%s:%s]>' % (self.__class__.__name__, self.ui_key,
310 self.ui_value)
310 self.ui_value)
311
311
312
312
313 class User(Base, BaseModel):
313 class User(Base, BaseModel):
314 __tablename__ = 'users'
314 __tablename__ = 'users'
315 __table_args__ = (
315 __table_args__ = (
316 UniqueConstraint('username'), UniqueConstraint('email'),
316 UniqueConstraint('username'), UniqueConstraint('email'),
317 Index('u_username_idx', 'username'),
317 Index('u_username_idx', 'username'),
318 Index('u_email_idx', 'email'),
318 Index('u_email_idx', 'email'),
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
319 {'extend_existing': True, 'mysql_engine': 'InnoDB',
320 'mysql_charset': 'utf8'}
320 'mysql_charset': 'utf8'}
321 )
321 )
322 DEFAULT_USER = 'default'
322 DEFAULT_USER = 'default'
323
323
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
324 user_id = Column("user_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
325 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
326 password = Column("password", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
327 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
328 admin = Column("admin", Boolean(), nullable=True, unique=None, default=False)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
329 name = Column("firstname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
330 lastname = Column("lastname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
331 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
332 last_login = Column("last_login", DateTime(timezone=False), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
333 ldap_dn = Column("ldap_dn", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
334 api_key = Column("api_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
335 inherit_default_permissions = Column("inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
336
336
337 user_log = relationship('UserLog')
337 user_log = relationship('UserLog')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
338 user_perms = relationship('UserToPerm', primaryjoin="User.user_id==UserToPerm.user_id", cascade='all')
339
339
340 repositories = relationship('Repository')
340 repositories = relationship('Repository')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
341 user_followers = relationship('UserFollowing', primaryjoin='UserFollowing.follows_user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
342 followings = relationship('UserFollowing', primaryjoin='UserFollowing.user_id==User.user_id', cascade='all')
343
343
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
344 repo_to_perm = relationship('UserRepoToPerm', primaryjoin='UserRepoToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
345 repo_group_to_perm = relationship('UserRepoGroupToPerm', primaryjoin='UserRepoGroupToPerm.user_id==User.user_id', cascade='all')
346
346
347 group_member = relationship('UserGroupMember', cascade='all')
347 group_member = relationship('UserGroupMember', cascade='all')
348
348
349 notifications = relationship('UserNotification', cascade='all')
349 notifications = relationship('UserNotification', cascade='all')
350 # notifications assigned to this user
350 # notifications assigned to this user
351 user_created_notifications = relationship('Notification', cascade='all')
351 user_created_notifications = relationship('Notification', cascade='all')
352 # comments created by this user
352 # comments created by this user
353 user_comments = relationship('ChangesetComment', cascade='all')
353 user_comments = relationship('ChangesetComment', cascade='all')
354 #extra emails for this user
354 #extra emails for this user
355 user_emails = relationship('UserEmailMap', cascade='all')
355 user_emails = relationship('UserEmailMap', cascade='all')
356
356
357 @hybrid_property
357 @hybrid_property
358 def email(self):
358 def email(self):
359 return self._email
359 return self._email
360
360
361 @email.setter
361 @email.setter
362 def email(self, val):
362 def email(self, val):
363 self._email = val.lower() if val else None
363 self._email = val.lower() if val else None
364
364
365 @property
365 @property
366 def firstname(self):
366 def firstname(self):
367 # alias for future
367 # alias for future
368 return self.name
368 return self.name
369
369
370 @property
370 @property
371 def emails(self):
371 def emails(self):
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
372 other = UserEmailMap.query().filter(UserEmailMap.user==self).all()
373 return [self.email] + [x.email for x in other]
373 return [self.email] + [x.email for x in other]
374
374
375 @property
375 @property
376 def ip_addresses(self):
376 def ip_addresses(self):
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
377 ret = UserIpMap.query().filter(UserIpMap.user == self).all()
378 return [x.ip_addr for x in ret]
378 return [x.ip_addr for x in ret]
379
379
380 @property
380 @property
381 def username_and_name(self):
381 def username_and_name(self):
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
382 return '%s (%s %s)' % (self.username, self.firstname, self.lastname)
383
383
384 @property
384 @property
385 def full_name(self):
385 def full_name(self):
386 return '%s %s' % (self.firstname, self.lastname)
386 return '%s %s' % (self.firstname, self.lastname)
387
387
388 @property
388 @property
389 def full_name_or_username(self):
389 def full_name_or_username(self):
390 return ('%s %s' % (self.firstname, self.lastname)
390 return ('%s %s' % (self.firstname, self.lastname)
391 if (self.firstname and self.lastname) else self.username)
391 if (self.firstname and self.lastname) else self.username)
392
392
393 @property
393 @property
394 def full_contact(self):
394 def full_contact(self):
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
395 return '%s %s <%s>' % (self.firstname, self.lastname, self.email)
396
396
397 @property
397 @property
398 def short_contact(self):
398 def short_contact(self):
399 return '%s %s' % (self.firstname, self.lastname)
399 return '%s %s' % (self.firstname, self.lastname)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def AuthUser(self):
406 def AuthUser(self):
407 """
407 """
408 Returns instance of AuthUser for this user
408 Returns instance of AuthUser for this user
409 """
409 """
410 from rhodecode.lib.auth import AuthUser
410 from rhodecode.lib.auth import AuthUser
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
411 return AuthUser(user_id=self.user_id, api_key=self.api_key,
412 username=self.username)
412 username=self.username)
413
413
414 def __unicode__(self):
414 def __unicode__(self):
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
415 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
416 self.user_id, self.username)
416 self.user_id, self.username)
417
417
418 @classmethod
418 @classmethod
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
419 def get_by_username(cls, username, case_insensitive=False, cache=False):
420 if case_insensitive:
420 if case_insensitive:
421 q = cls.query().filter(cls.username.ilike(username))
421 q = cls.query().filter(cls.username.ilike(username))
422 else:
422 else:
423 q = cls.query().filter(cls.username == username)
423 q = cls.query().filter(cls.username == username)
424
424
425 if cache:
425 if cache:
426 q = q.options(FromCache(
426 q = q.options(FromCache(
427 "sql_cache_short",
427 "sql_cache_short",
428 "get_user_%s" % _hash_key(username)
428 "get_user_%s" % _hash_key(username)
429 )
429 )
430 )
430 )
431 return q.scalar()
431 return q.scalar()
432
432
433 @classmethod
433 @classmethod
434 def get_by_api_key(cls, api_key, cache=False):
434 def get_by_api_key(cls, api_key, cache=False):
435 q = cls.query().filter(cls.api_key == api_key)
435 q = cls.query().filter(cls.api_key == api_key)
436
436
437 if cache:
437 if cache:
438 q = q.options(FromCache("sql_cache_short",
438 q = q.options(FromCache("sql_cache_short",
439 "get_api_key_%s" % api_key))
439 "get_api_key_%s" % api_key))
440 return q.scalar()
440 return q.scalar()
441
441
442 @classmethod
442 @classmethod
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
443 def get_by_email(cls, email, case_insensitive=False, cache=False):
444 if case_insensitive:
444 if case_insensitive:
445 q = cls.query().filter(cls.email.ilike(email))
445 q = cls.query().filter(cls.email.ilike(email))
446 else:
446 else:
447 q = cls.query().filter(cls.email == email)
447 q = cls.query().filter(cls.email == email)
448
448
449 if cache:
449 if cache:
450 q = q.options(FromCache("sql_cache_short",
450 q = q.options(FromCache("sql_cache_short",
451 "get_email_key_%s" % email))
451 "get_email_key_%s" % email))
452
452
453 ret = q.scalar()
453 ret = q.scalar()
454 if ret is None:
454 if ret is None:
455 q = UserEmailMap.query()
455 q = UserEmailMap.query()
456 # try fetching in alternate email map
456 # try fetching in alternate email map
457 if case_insensitive:
457 if case_insensitive:
458 q = q.filter(UserEmailMap.email.ilike(email))
458 q = q.filter(UserEmailMap.email.ilike(email))
459 else:
459 else:
460 q = q.filter(UserEmailMap.email == email)
460 q = q.filter(UserEmailMap.email == email)
461 q = q.options(joinedload(UserEmailMap.user))
461 q = q.options(joinedload(UserEmailMap.user))
462 if cache:
462 if cache:
463 q = q.options(FromCache("sql_cache_short",
463 q = q.options(FromCache("sql_cache_short",
464 "get_email_map_key_%s" % email))
464 "get_email_map_key_%s" % email))
465 ret = getattr(q.scalar(), 'user', None)
465 ret = getattr(q.scalar(), 'user', None)
466
466
467 return ret
467 return ret
468
468
469 @classmethod
469 @classmethod
470 def get_from_cs_author(cls, author):
470 def get_from_cs_author(cls, author):
471 """
471 """
472 Tries to get User objects out of commit author string
472 Tries to get User objects out of commit author string
473
473
474 :param author:
474 :param author:
475 """
475 """
476 from rhodecode.lib.helpers import email, author_name
476 from rhodecode.lib.helpers import email, author_name
477 # Valid email in the attribute passed, see if they're in the system
477 # Valid email in the attribute passed, see if they're in the system
478 _email = email(author)
478 _email = email(author)
479 if _email:
479 if _email:
480 user = cls.get_by_email(_email, case_insensitive=True)
480 user = cls.get_by_email(_email, case_insensitive=True)
481 if user:
481 if user:
482 return user
482 return user
483 # Maybe we can match by username?
483 # Maybe we can match by username?
484 _author = author_name(author)
484 _author = author_name(author)
485 user = cls.get_by_username(_author, case_insensitive=True)
485 user = cls.get_by_username(_author, case_insensitive=True)
486 if user:
486 if user:
487 return user
487 return user
488
488
489 def update_lastlogin(self):
489 def update_lastlogin(self):
490 """Update user lastlogin"""
490 """Update user lastlogin"""
491 self.last_login = datetime.datetime.now()
491 self.last_login = datetime.datetime.now()
492 Session().add(self)
492 Session().add(self)
493 log.debug('updated user %s lastlogin' % self.username)
493 log.debug('updated user %s lastlogin' % self.username)
494
494
495 @classmethod
495 @classmethod
496 def get_first_admin(cls):
496 def get_first_admin(cls):
497 user = User.query().filter(User.admin == True).first()
497 user = User.query().filter(User.admin == True).first()
498 if user is None:
498 if user is None:
499 raise Exception('Missing administrative account!')
499 raise Exception('Missing administrative account!')
500 return user
500 return user
501
501
502 @classmethod
502 @classmethod
503 def get_default_user(cls, cache=False):
503 def get_default_user(cls, cache=False):
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
504 user = User.get_by_username(User.DEFAULT_USER, cache=cache)
505 if user is None:
505 if user is None:
506 raise Exception('Missing default account!')
506 raise Exception('Missing default account!')
507 return user
507 return user
508
508
509 def get_api_data(self):
509 def get_api_data(self):
510 """
510 """
511 Common function for generating user related data for API
511 Common function for generating user related data for API
512 """
512 """
513 user = self
513 user = self
514 data = dict(
514 data = dict(
515 user_id=user.user_id,
515 user_id=user.user_id,
516 username=user.username,
516 username=user.username,
517 firstname=user.name,
517 firstname=user.name,
518 lastname=user.lastname,
518 lastname=user.lastname,
519 email=user.email,
519 email=user.email,
520 emails=user.emails,
520 emails=user.emails,
521 api_key=user.api_key,
521 api_key=user.api_key,
522 active=user.active,
522 active=user.active,
523 admin=user.admin,
523 admin=user.admin,
524 ldap_dn=user.ldap_dn,
524 ldap_dn=user.ldap_dn,
525 last_login=user.last_login,
525 last_login=user.last_login,
526 ip_addresses=user.ip_addresses
526 ip_addresses=user.ip_addresses
527 )
527 )
528 return data
528 return data
529
529
530 def __json__(self):
530 def __json__(self):
531 data = dict(
531 data = dict(
532 full_name=self.full_name,
532 full_name=self.full_name,
533 full_name_or_username=self.full_name_or_username,
533 full_name_or_username=self.full_name_or_username,
534 short_contact=self.short_contact,
534 short_contact=self.short_contact,
535 full_contact=self.full_contact
535 full_contact=self.full_contact
536 )
536 )
537 data.update(self.get_api_data())
537 data.update(self.get_api_data())
538 return data
538 return data
539
539
540
540
541 class UserEmailMap(Base, BaseModel):
541 class UserEmailMap(Base, BaseModel):
542 __tablename__ = 'user_email_map'
542 __tablename__ = 'user_email_map'
543 __table_args__ = (
543 __table_args__ = (
544 Index('uem_email_idx', 'email'),
544 Index('uem_email_idx', 'email'),
545 UniqueConstraint('email'),
545 UniqueConstraint('email'),
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
546 {'extend_existing': True, 'mysql_engine': 'InnoDB',
547 'mysql_charset': 'utf8'}
547 'mysql_charset': 'utf8'}
548 )
548 )
549 __mapper_args__ = {}
549 __mapper_args__ = {}
550
550
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
551 email_id = Column("email_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
552 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
553 _email = Column("email", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
554 user = relationship('User', lazy='joined')
554 user = relationship('User', lazy='joined')
555
555
556 @validates('_email')
556 @validates('_email')
557 def validate_email(self, key, email):
557 def validate_email(self, key, email):
558 # check if this email is not main one
558 # check if this email is not main one
559 main_email = Session().query(User).filter(User.email == email).scalar()
559 main_email = Session().query(User).filter(User.email == email).scalar()
560 if main_email is not None:
560 if main_email is not None:
561 raise AttributeError('email %s is present is user table' % email)
561 raise AttributeError('email %s is present is user table' % email)
562 return email
562 return email
563
563
564 @hybrid_property
564 @hybrid_property
565 def email(self):
565 def email(self):
566 return self._email
566 return self._email
567
567
568 @email.setter
568 @email.setter
569 def email(self, val):
569 def email(self, val):
570 self._email = val.lower() if val else None
570 self._email = val.lower() if val else None
571
571
572
572
573 class UserIpMap(Base, BaseModel):
573 class UserIpMap(Base, BaseModel):
574 __tablename__ = 'user_ip_map'
574 __tablename__ = 'user_ip_map'
575 __table_args__ = (
575 __table_args__ = (
576 UniqueConstraint('user_id', 'ip_addr'),
576 UniqueConstraint('user_id', 'ip_addr'),
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
577 {'extend_existing': True, 'mysql_engine': 'InnoDB',
578 'mysql_charset': 'utf8'}
578 'mysql_charset': 'utf8'}
579 )
579 )
580 __mapper_args__ = {}
580 __mapper_args__ = {}
581
581
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
582 ip_id = Column("ip_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
583 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
584 ip_addr = Column("ip_addr", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
585 active = Column("active", Boolean(), nullable=True, unique=None, default=True)
586 user = relationship('User', lazy='joined')
586 user = relationship('User', lazy='joined')
587
587
588 @classmethod
588 @classmethod
589 def _get_ip_range(cls, ip_addr):
589 def _get_ip_range(cls, ip_addr):
590 from rhodecode.lib import ipaddr
590 from rhodecode.lib import ipaddr
591 net = ipaddr.IPNetwork(address=ip_addr)
591 net = ipaddr.IPNetwork(address=ip_addr)
592 return [str(net.network), str(net.broadcast)]
592 return [str(net.network), str(net.broadcast)]
593
593
594 def __json__(self):
594 def __json__(self):
595 return dict(
595 return dict(
596 ip_addr=self.ip_addr,
596 ip_addr=self.ip_addr,
597 ip_range=self._get_ip_range(self.ip_addr)
597 ip_range=self._get_ip_range(self.ip_addr)
598 )
598 )
599
599
600
600
601 class UserLog(Base, BaseModel):
601 class UserLog(Base, BaseModel):
602 __tablename__ = 'user_logs'
602 __tablename__ = 'user_logs'
603 __table_args__ = (
603 __table_args__ = (
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
604 {'extend_existing': True, 'mysql_engine': 'InnoDB',
605 'mysql_charset': 'utf8'},
605 'mysql_charset': 'utf8'},
606 )
606 )
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
607 user_log_id = Column("user_log_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
608 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
609 username = Column("username", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
610 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
611 repository_name = Column("repository_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
612 user_ip = Column("user_ip", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
613 action = Column("action", UnicodeText(1200000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
614 action_date = Column("action_date", DateTime(timezone=False), nullable=True, unique=None, default=None)
615
615
616 @property
616 @property
617 def action_as_day(self):
617 def action_as_day(self):
618 return datetime.date(*self.action_date.timetuple()[:3])
618 return datetime.date(*self.action_date.timetuple()[:3])
619
619
620 user = relationship('User')
620 user = relationship('User')
621 repository = relationship('Repository', cascade='')
621 repository = relationship('Repository', cascade='')
622
622
623
623
624 class UserGroup(Base, BaseModel):
624 class UserGroup(Base, BaseModel):
625 __tablename__ = 'users_groups'
625 __tablename__ = 'users_groups'
626 __table_args__ = (
626 __table_args__ = (
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
627 {'extend_existing': True, 'mysql_engine': 'InnoDB',
628 'mysql_charset': 'utf8'},
628 'mysql_charset': 'utf8'},
629 )
629 )
630
630
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
631 users_group_id = Column("users_group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
632 users_group_name = Column("users_group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
633 users_group_active = Column("users_group_active", Boolean(), nullable=True, unique=None, default=None)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
634 inherit_default_permissions = Column("users_group_inherit_default_permissions", Boolean(), nullable=False, unique=None, default=True)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
635 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
636
636
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
637 members = relationship('UserGroupMember', cascade="all, delete, delete-orphan", lazy="joined")
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
638 users_group_to_perm = relationship('UserGroupToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
639 users_group_repo_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
640 users_group_repo_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
641 user_user_group_to_perm = relationship('UserUserGroupToPerm ', cascade='all')
642 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
642 user_group_user_group_to_perm = relationship('UserGroupUserGroupToPerm ', primaryjoin="UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id", cascade='all')
643
643
644 user = relationship('User')
644 user = relationship('User')
645
645
646 def __unicode__(self):
646 def __unicode__(self):
647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
647 return u"<%s('id:%s:%s')>" % (self.__class__.__name__,
648 self.users_group_id,
648 self.users_group_id,
649 self.users_group_name)
649 self.users_group_name)
650
650
651 @classmethod
651 @classmethod
652 def get_by_group_name(cls, group_name, cache=False,
652 def get_by_group_name(cls, group_name, cache=False,
653 case_insensitive=False):
653 case_insensitive=False):
654 if case_insensitive:
654 if case_insensitive:
655 q = cls.query().filter(cls.users_group_name.ilike(group_name))
655 q = cls.query().filter(cls.users_group_name.ilike(group_name))
656 else:
656 else:
657 q = cls.query().filter(cls.users_group_name == group_name)
657 q = cls.query().filter(cls.users_group_name == group_name)
658 if cache:
658 if cache:
659 q = q.options(FromCache(
659 q = q.options(FromCache(
660 "sql_cache_short",
660 "sql_cache_short",
661 "get_user_%s" % _hash_key(group_name)
661 "get_user_%s" % _hash_key(group_name)
662 )
662 )
663 )
663 )
664 return q.scalar()
664 return q.scalar()
665
665
666 @classmethod
666 @classmethod
667 def get(cls, users_group_id, cache=False):
667 def get(cls, users_group_id, cache=False):
668 users_group = cls.query()
668 users_group = cls.query()
669 if cache:
669 if cache:
670 users_group = users_group.options(FromCache("sql_cache_short",
670 users_group = users_group.options(FromCache("sql_cache_short",
671 "get_users_group_%s" % users_group_id))
671 "get_users_group_%s" % users_group_id))
672 return users_group.get(users_group_id)
672 return users_group.get(users_group_id)
673
673
674 def get_api_data(self):
674 def get_api_data(self):
675 users_group = self
675 users_group = self
676
676
677 data = dict(
677 data = dict(
678 users_group_id=users_group.users_group_id,
678 users_group_id=users_group.users_group_id,
679 group_name=users_group.users_group_name,
679 group_name=users_group.users_group_name,
680 active=users_group.users_group_active,
680 active=users_group.users_group_active,
681 )
681 )
682
682
683 return data
683 return data
684
684
685
685
686 class UserGroupMember(Base, BaseModel):
686 class UserGroupMember(Base, BaseModel):
687 __tablename__ = 'users_groups_members'
687 __tablename__ = 'users_groups_members'
688 __table_args__ = (
688 __table_args__ = (
689 {'extend_existing': True, 'mysql_engine': 'InnoDB',
689 {'extend_existing': True, 'mysql_engine': 'InnoDB',
690 'mysql_charset': 'utf8'},
690 'mysql_charset': 'utf8'},
691 )
691 )
692
692
693 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
693 users_group_member_id = Column("users_group_member_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
694 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
694 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
695 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
695 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
696
696
697 user = relationship('User', lazy='joined')
697 user = relationship('User', lazy='joined')
698 users_group = relationship('UserGroup')
698 users_group = relationship('UserGroup')
699
699
700 def __init__(self, gr_id='', u_id=''):
700 def __init__(self, gr_id='', u_id=''):
701 self.users_group_id = gr_id
701 self.users_group_id = gr_id
702 self.user_id = u_id
702 self.user_id = u_id
703
703
704
704
705 class RepositoryField(Base, BaseModel):
705 class RepositoryField(Base, BaseModel):
706 __tablename__ = 'repositories_fields'
706 __tablename__ = 'repositories_fields'
707 __table_args__ = (
707 __table_args__ = (
708 UniqueConstraint('repository_id', 'field_key'), # no-multi field
708 UniqueConstraint('repository_id', 'field_key'), # no-multi field
709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
709 {'extend_existing': True, 'mysql_engine': 'InnoDB',
710 'mysql_charset': 'utf8'},
710 'mysql_charset': 'utf8'},
711 )
711 )
712 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
712 PREFIX = 'ex_' # prefix used in form to not conflict with already existing fields
713
713
714 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
714 repo_field_id = Column("repo_field_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
715 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
716 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
716 field_key = Column("field_key", String(250, convert_unicode=False, assert_unicode=None))
717 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
717 field_label = Column("field_label", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
718 field_value = Column("field_value", String(10000, convert_unicode=False, assert_unicode=None), nullable=False)
719 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
719 field_desc = Column("field_desc", String(1024, convert_unicode=False, assert_unicode=None), nullable=False)
720 field_type = Column("field_type", String(256), nullable=False, unique=None)
720 field_type = Column("field_type", String(256), nullable=False, unique=None)
721 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
721 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
722
722
723 repository = relationship('Repository')
723 repository = relationship('Repository')
724
724
725 @property
725 @property
726 def field_key_prefixed(self):
726 def field_key_prefixed(self):
727 return 'ex_%s' % self.field_key
727 return 'ex_%s' % self.field_key
728
728
729 @classmethod
729 @classmethod
730 def un_prefix_key(cls, key):
730 def un_prefix_key(cls, key):
731 if key.startswith(cls.PREFIX):
731 if key.startswith(cls.PREFIX):
732 return key[len(cls.PREFIX):]
732 return key[len(cls.PREFIX):]
733 return key
733 return key
734
734
735 @classmethod
735 @classmethod
736 def get_by_key_name(cls, key, repo):
736 def get_by_key_name(cls, key, repo):
737 row = cls.query()\
737 row = cls.query()\
738 .filter(cls.repository == repo)\
738 .filter(cls.repository == repo)\
739 .filter(cls.field_key == key).scalar()
739 .filter(cls.field_key == key).scalar()
740 return row
740 return row
741
741
742
742
743 class Repository(Base, BaseModel):
743 class Repository(Base, BaseModel):
744 __tablename__ = 'repositories'
744 __tablename__ = 'repositories'
745 __table_args__ = (
745 __table_args__ = (
746 UniqueConstraint('repo_name'),
746 UniqueConstraint('repo_name'),
747 Index('r_repo_name_idx', 'repo_name'),
747 Index('r_repo_name_idx', 'repo_name'),
748 {'extend_existing': True, 'mysql_engine': 'InnoDB',
748 {'extend_existing': True, 'mysql_engine': 'InnoDB',
749 'mysql_charset': 'utf8'},
749 'mysql_charset': 'utf8'},
750 )
750 )
751
751
752 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
752 repo_id = Column("repo_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
753 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
753 repo_name = Column("repo_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
754 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
754 clone_uri = Column("clone_uri", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
755 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
755 repo_type = Column("repo_type", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
756 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
757 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
757 private = Column("private", Boolean(), nullable=True, unique=None, default=None)
758 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
758 enable_statistics = Column("statistics", Boolean(), nullable=True, unique=None, default=True)
759 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
759 enable_downloads = Column("downloads", Boolean(), nullable=True, unique=None, default=True)
760 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
760 description = Column("description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
761 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
761 created_on = Column('created_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
762 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
762 updated_on = Column('updated_on', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
763 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
763 landing_rev = Column("landing_revision", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=False, default=None)
764 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
764 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
765 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
765 _locked = Column("locked", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=False, default=None)
766 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
766 _changeset_cache = Column("changeset_cache", LargeBinary(), nullable=True) #JSON data
767
767
768 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
768 fork_id = Column("fork_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=False, default=None)
769 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
769 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=False, default=None)
770
770
771 user = relationship('User')
771 user = relationship('User')
772 fork = relationship('Repository', remote_side=repo_id)
772 fork = relationship('Repository', remote_side=repo_id)
773 group = relationship('RepoGroup')
773 group = relationship('RepoGroup')
774 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
774 repo_to_perm = relationship('UserRepoToPerm', cascade='all', order_by='UserRepoToPerm.repo_to_perm_id')
775 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
775 users_group_to_perm = relationship('UserGroupRepoToPerm', cascade='all')
776 stats = relationship('Statistics', cascade='all', uselist=False)
776 stats = relationship('Statistics', cascade='all', uselist=False)
777
777
778 followers = relationship('UserFollowing',
778 followers = relationship('UserFollowing',
779 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
779 primaryjoin='UserFollowing.follows_repo_id==Repository.repo_id',
780 cascade='all')
780 cascade='all')
781 extra_fields = relationship('RepositoryField',
781 extra_fields = relationship('RepositoryField',
782 cascade="all, delete, delete-orphan")
782 cascade="all, delete, delete-orphan")
783
783
784 logs = relationship('UserLog')
784 logs = relationship('UserLog')
785 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
785 comments = relationship('ChangesetComment', cascade="all, delete, delete-orphan")
786
786
787 pull_requests_org = relationship('PullRequest',
787 pull_requests_org = relationship('PullRequest',
788 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
788 primaryjoin='PullRequest.org_repo_id==Repository.repo_id',
789 cascade="all, delete, delete-orphan")
789 cascade="all, delete, delete-orphan")
790
790
791 pull_requests_other = relationship('PullRequest',
791 pull_requests_other = relationship('PullRequest',
792 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
792 primaryjoin='PullRequest.other_repo_id==Repository.repo_id',
793 cascade="all, delete, delete-orphan")
793 cascade="all, delete, delete-orphan")
794
794
795 def __unicode__(self):
795 def __unicode__(self):
796 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
796 return u"<%s('%s:%s')>" % (self.__class__.__name__, self.repo_id,
797 self.repo_name)
797 self.repo_name)
798
798
799 @hybrid_property
799 @hybrid_property
800 def locked(self):
800 def locked(self):
801 # always should return [user_id, timelocked]
801 # always should return [user_id, timelocked]
802 if self._locked:
802 if self._locked:
803 _lock_info = self._locked.split(':')
803 _lock_info = self._locked.split(':')
804 return int(_lock_info[0]), _lock_info[1]
804 return int(_lock_info[0]), _lock_info[1]
805 return [None, None]
805 return [None, None]
806
806
807 @locked.setter
807 @locked.setter
808 def locked(self, val):
808 def locked(self, val):
809 if val and isinstance(val, (list, tuple)):
809 if val and isinstance(val, (list, tuple)):
810 self._locked = ':'.join(map(str, val))
810 self._locked = ':'.join(map(str, val))
811 else:
811 else:
812 self._locked = None
812 self._locked = None
813
813
814 @hybrid_property
814 @hybrid_property
815 def changeset_cache(self):
815 def changeset_cache(self):
816 from rhodecode.lib.vcs.backends.base import EmptyChangeset
816 from rhodecode.lib.vcs.backends.base import EmptyChangeset
817 dummy = EmptyChangeset().__json__()
817 dummy = EmptyChangeset().__json__()
818 if not self._changeset_cache:
818 if not self._changeset_cache:
819 return dummy
819 return dummy
820 try:
820 try:
821 return json.loads(self._changeset_cache)
821 return json.loads(self._changeset_cache)
822 except TypeError:
822 except TypeError:
823 return dummy
823 return dummy
824
824
825 @changeset_cache.setter
825 @changeset_cache.setter
826 def changeset_cache(self, val):
826 def changeset_cache(self, val):
827 try:
827 try:
828 self._changeset_cache = json.dumps(val)
828 self._changeset_cache = json.dumps(val)
829 except Exception:
829 except Exception:
830 log.error(traceback.format_exc())
830 log.error(traceback.format_exc())
831
831
832 @classmethod
832 @classmethod
833 def url_sep(cls):
833 def url_sep(cls):
834 return URL_SEP
834 return URL_SEP
835
835
836 @classmethod
836 @classmethod
837 def normalize_repo_name(cls, repo_name):
837 def normalize_repo_name(cls, repo_name):
838 """
838 """
839 Normalizes os specific repo_name to the format internally stored inside
839 Normalizes os specific repo_name to the format internally stored inside
840 dabatabase using URL_SEP
840 dabatabase using URL_SEP
841
841
842 :param cls:
842 :param cls:
843 :param repo_name:
843 :param repo_name:
844 """
844 """
845 return cls.url_sep().join(repo_name.split(os.sep))
845 return cls.url_sep().join(repo_name.split(os.sep))
846
846
847 @classmethod
847 @classmethod
848 def get_by_repo_name(cls, repo_name):
848 def get_by_repo_name(cls, repo_name):
849 q = Session().query(cls).filter(cls.repo_name == repo_name)
849 q = Session().query(cls).filter(cls.repo_name == repo_name)
850 q = q.options(joinedload(Repository.fork))\
850 q = q.options(joinedload(Repository.fork))\
851 .options(joinedload(Repository.user))\
851 .options(joinedload(Repository.user))\
852 .options(joinedload(Repository.group))
852 .options(joinedload(Repository.group))
853 return q.scalar()
853 return q.scalar()
854
854
855 @classmethod
855 @classmethod
856 def get_by_full_path(cls, repo_full_path):
856 def get_by_full_path(cls, repo_full_path):
857 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
857 repo_name = repo_full_path.split(cls.base_path(), 1)[-1]
858 repo_name = cls.normalize_repo_name(repo_name)
858 repo_name = cls.normalize_repo_name(repo_name)
859 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
859 return cls.get_by_repo_name(repo_name.strip(URL_SEP))
860
860
861 @classmethod
861 @classmethod
862 def get_repo_forks(cls, repo_id):
862 def get_repo_forks(cls, repo_id):
863 return cls.query().filter(Repository.fork_id == repo_id)
863 return cls.query().filter(Repository.fork_id == repo_id)
864
864
865 @classmethod
865 @classmethod
866 def base_path(cls):
866 def base_path(cls):
867 """
867 """
868 Returns base path when all repos are stored
868 Returns base path when all repos are stored
869
869
870 :param cls:
870 :param cls:
871 """
871 """
872 q = Session().query(RhodeCodeUi)\
872 q = Session().query(RhodeCodeUi)\
873 .filter(RhodeCodeUi.ui_key == cls.url_sep())
873 .filter(RhodeCodeUi.ui_key == cls.url_sep())
874 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
874 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
875 return q.one().ui_value
875 return q.one().ui_value
876
876
877 @property
877 @property
878 def forks(self):
878 def forks(self):
879 """
879 """
880 Return forks of this repo
880 Return forks of this repo
881 """
881 """
882 return Repository.get_repo_forks(self.repo_id)
882 return Repository.get_repo_forks(self.repo_id)
883
883
884 @property
884 @property
885 def parent(self):
885 def parent(self):
886 """
886 """
887 Returns fork parent
887 Returns fork parent
888 """
888 """
889 return self.fork
889 return self.fork
890
890
891 @property
891 @property
892 def just_name(self):
892 def just_name(self):
893 return self.repo_name.split(Repository.url_sep())[-1]
893 return self.repo_name.split(Repository.url_sep())[-1]
894
894
895 @property
895 @property
896 def groups_with_parents(self):
896 def groups_with_parents(self):
897 groups = []
897 groups = []
898 if self.group is None:
898 if self.group is None:
899 return groups
899 return groups
900
900
901 cur_gr = self.group
901 cur_gr = self.group
902 groups.insert(0, cur_gr)
902 groups.insert(0, cur_gr)
903 while 1:
903 while 1:
904 gr = getattr(cur_gr, 'parent_group', None)
904 gr = getattr(cur_gr, 'parent_group', None)
905 cur_gr = cur_gr.parent_group
905 cur_gr = cur_gr.parent_group
906 if gr is None:
906 if gr is None:
907 break
907 break
908 groups.insert(0, gr)
908 groups.insert(0, gr)
909
909
910 return groups
910 return groups
911
911
912 @property
912 @property
913 def groups_and_repo(self):
913 def groups_and_repo(self):
914 return self.groups_with_parents, self.just_name, self.repo_name
914 return self.groups_with_parents, self.just_name, self.repo_name
915
915
916 @LazyProperty
916 @LazyProperty
917 def repo_path(self):
917 def repo_path(self):
918 """
918 """
919 Returns base full path for that repository means where it actually
919 Returns base full path for that repository means where it actually
920 exists on a filesystem
920 exists on a filesystem
921 """
921 """
922 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
922 q = Session().query(RhodeCodeUi).filter(RhodeCodeUi.ui_key ==
923 Repository.url_sep())
923 Repository.url_sep())
924 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
924 q = q.options(FromCache("sql_cache_short", "repository_repo_path"))
925 return q.one().ui_value
925 return q.one().ui_value
926
926
927 @property
927 @property
928 def repo_full_path(self):
928 def repo_full_path(self):
929 p = [self.repo_path]
929 p = [self.repo_path]
930 # we need to split the name by / since this is how we store the
930 # we need to split the name by / since this is how we store the
931 # names in the database, but that eventually needs to be converted
931 # names in the database, but that eventually needs to be converted
932 # into a valid system path
932 # into a valid system path
933 p += self.repo_name.split(Repository.url_sep())
933 p += self.repo_name.split(Repository.url_sep())
934 return os.path.join(*map(safe_unicode, p))
934 return os.path.join(*map(safe_unicode, p))
935
935
936 @property
936 @property
937 def cache_keys(self):
937 def cache_keys(self):
938 """
938 """
939 Returns associated cache keys for that repo
939 Returns associated cache keys for that repo
940 """
940 """
941 return CacheInvalidation.query()\
941 return CacheInvalidation.query()\
942 .filter(CacheInvalidation.cache_args == self.repo_name)\
942 .filter(CacheInvalidation.cache_args == self.repo_name)\
943 .order_by(CacheInvalidation.cache_key)\
943 .order_by(CacheInvalidation.cache_key)\
944 .all()
944 .all()
945
945
946 def get_new_name(self, repo_name):
946 def get_new_name(self, repo_name):
947 """
947 """
948 returns new full repository name based on assigned group and new new
948 returns new full repository name based on assigned group and new new
949
949
950 :param group_name:
950 :param group_name:
951 """
951 """
952 path_prefix = self.group.full_path_splitted if self.group else []
952 path_prefix = self.group.full_path_splitted if self.group else []
953 return Repository.url_sep().join(path_prefix + [repo_name])
953 return Repository.url_sep().join(path_prefix + [repo_name])
954
954
955 @property
955 @property
956 def _ui(self):
956 def _ui(self):
957 """
957 """
958 Creates an db based ui object for this repository
958 Creates an db based ui object for this repository
959 """
959 """
960 from rhodecode.lib.utils import make_ui
960 from rhodecode.lib.utils import make_ui
961 return make_ui('db', clear_session=False)
961 return make_ui('db', clear_session=False)
962
962
963 @classmethod
963 @classmethod
964 def is_valid(cls, repo_name):
964 def is_valid(cls, repo_name):
965 """
965 """
966 returns True if given repo name is a valid filesystem repository
966 returns True if given repo name is a valid filesystem repository
967
967
968 :param cls:
968 :param cls:
969 :param repo_name:
969 :param repo_name:
970 """
970 """
971 from rhodecode.lib.utils import is_valid_repo
971 from rhodecode.lib.utils import is_valid_repo
972
972
973 return is_valid_repo(repo_name, cls.base_path())
973 return is_valid_repo(repo_name, cls.base_path())
974
974
975 def get_api_data(self):
975 def get_api_data(self):
976 """
976 """
977 Common function for generating repo api data
977 Common function for generating repo api data
978
978
979 """
979 """
980 repo = self
980 repo = self
981 data = dict(
981 data = dict(
982 repo_id=repo.repo_id,
982 repo_id=repo.repo_id,
983 repo_name=repo.repo_name,
983 repo_name=repo.repo_name,
984 repo_type=repo.repo_type,
984 repo_type=repo.repo_type,
985 clone_uri=repo.clone_uri,
985 clone_uri=repo.clone_uri,
986 private=repo.private,
986 private=repo.private,
987 created_on=repo.created_on,
987 created_on=repo.created_on,
988 description=repo.description,
988 description=repo.description,
989 landing_rev=repo.landing_rev,
989 landing_rev=repo.landing_rev,
990 owner=repo.user.username,
990 owner=repo.user.username,
991 fork_of=repo.fork.repo_name if repo.fork else None,
991 fork_of=repo.fork.repo_name if repo.fork else None,
992 enable_statistics=repo.enable_statistics,
992 enable_statistics=repo.enable_statistics,
993 enable_locking=repo.enable_locking,
993 enable_locking=repo.enable_locking,
994 enable_downloads=repo.enable_downloads,
994 enable_downloads=repo.enable_downloads,
995 last_changeset=repo.changeset_cache,
995 last_changeset=repo.changeset_cache,
996 locked_by=User.get(self.locked[0]).get_api_data() \
996 locked_by=User.get(self.locked[0]).get_api_data() \
997 if self.locked[0] else None,
997 if self.locked[0] else None,
998 locked_date=time_to_datetime(self.locked[1]) \
998 locked_date=time_to_datetime(self.locked[1]) \
999 if self.locked[1] else None
999 if self.locked[1] else None
1000 )
1000 )
1001 rc_config = RhodeCodeSetting.get_app_settings()
1001 rc_config = RhodeCodeSetting.get_app_settings()
1002 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1002 repository_fields = str2bool(rc_config.get('rhodecode_repository_fields'))
1003 if repository_fields:
1003 if repository_fields:
1004 for f in self.extra_fields:
1004 for f in self.extra_fields:
1005 data[f.field_key_prefixed] = f.field_value
1005 data[f.field_key_prefixed] = f.field_value
1006
1006
1007 return data
1007 return data
1008
1008
1009 @classmethod
1009 @classmethod
1010 def lock(cls, repo, user_id):
1010 def lock(cls, repo, user_id, lock_time=None):
1011 repo.locked = [user_id, time.time()]
1011 if not lock_time:
1012 lock_time = time.time()
1013 repo.locked = [user_id, lock_time]
1012 Session().add(repo)
1014 Session().add(repo)
1013 Session().commit()
1015 Session().commit()
1014
1016
1015 @classmethod
1017 @classmethod
1016 def unlock(cls, repo):
1018 def unlock(cls, repo):
1017 repo.locked = None
1019 repo.locked = None
1018 Session().add(repo)
1020 Session().add(repo)
1019 Session().commit()
1021 Session().commit()
1020
1022
1021 @classmethod
1023 @classmethod
1022 def getlock(cls, repo):
1024 def getlock(cls, repo):
1023 return repo.locked
1025 return repo.locked
1024
1026
1025 @property
1027 @property
1026 def last_db_change(self):
1028 def last_db_change(self):
1027 return self.updated_on
1029 return self.updated_on
1028
1030
1029 def clone_url(self, **override):
1031 def clone_url(self, **override):
1030 from pylons import url
1032 from pylons import url
1031 from urlparse import urlparse
1033 from urlparse import urlparse
1032 import urllib
1034 import urllib
1033 parsed_url = urlparse(url('home', qualified=True))
1035 parsed_url = urlparse(url('home', qualified=True))
1034 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1036 default_clone_uri = '%(scheme)s://%(user)s%(pass)s%(netloc)s%(prefix)s%(path)s'
1035 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1037 decoded_path = safe_unicode(urllib.unquote(parsed_url.path))
1036 args = {
1038 args = {
1037 'user': '',
1039 'user': '',
1038 'pass': '',
1040 'pass': '',
1039 'scheme': parsed_url.scheme,
1041 'scheme': parsed_url.scheme,
1040 'netloc': parsed_url.netloc,
1042 'netloc': parsed_url.netloc,
1041 'prefix': decoded_path,
1043 'prefix': decoded_path,
1042 'path': self.repo_name
1044 'path': self.repo_name
1043 }
1045 }
1044
1046
1045 args.update(override)
1047 args.update(override)
1046 return default_clone_uri % args
1048 return default_clone_uri % args
1047
1049
1048 #==========================================================================
1050 #==========================================================================
1049 # SCM PROPERTIES
1051 # SCM PROPERTIES
1050 #==========================================================================
1052 #==========================================================================
1051
1053
1052 def get_changeset(self, rev=None):
1054 def get_changeset(self, rev=None):
1053 return get_changeset_safe(self.scm_instance, rev)
1055 return get_changeset_safe(self.scm_instance, rev)
1054
1056
1055 def get_landing_changeset(self):
1057 def get_landing_changeset(self):
1056 """
1058 """
1057 Returns landing changeset, or if that doesn't exist returns the tip
1059 Returns landing changeset, or if that doesn't exist returns the tip
1058 """
1060 """
1059 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1061 cs = self.get_changeset(self.landing_rev) or self.get_changeset()
1060 return cs
1062 return cs
1061
1063
1062 def update_changeset_cache(self, cs_cache=None):
1064 def update_changeset_cache(self, cs_cache=None):
1063 """
1065 """
1064 Update cache of last changeset for repository, keys should be::
1066 Update cache of last changeset for repository, keys should be::
1065
1067
1066 short_id
1068 short_id
1067 raw_id
1069 raw_id
1068 revision
1070 revision
1069 message
1071 message
1070 date
1072 date
1071 author
1073 author
1072
1074
1073 :param cs_cache:
1075 :param cs_cache:
1074 """
1076 """
1075 from rhodecode.lib.vcs.backends.base import BaseChangeset
1077 from rhodecode.lib.vcs.backends.base import BaseChangeset
1076 if cs_cache is None:
1078 if cs_cache is None:
1077 cs_cache = EmptyChangeset()
1079 cs_cache = EmptyChangeset()
1078 # use no-cache version here
1080 # use no-cache version here
1079 scm_repo = self.scm_instance_no_cache()
1081 scm_repo = self.scm_instance_no_cache()
1080 if scm_repo:
1082 if scm_repo:
1081 cs_cache = scm_repo.get_changeset()
1083 cs_cache = scm_repo.get_changeset()
1082
1084
1083 if isinstance(cs_cache, BaseChangeset):
1085 if isinstance(cs_cache, BaseChangeset):
1084 cs_cache = cs_cache.__json__()
1086 cs_cache = cs_cache.__json__()
1085
1087
1086 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1088 if (cs_cache != self.changeset_cache or not self.changeset_cache):
1087 _default = datetime.datetime.fromtimestamp(0)
1089 _default = datetime.datetime.fromtimestamp(0)
1088 last_change = cs_cache.get('date') or _default
1090 last_change = cs_cache.get('date') or _default
1089 log.debug('updated repo %s with new cs cache %s'
1091 log.debug('updated repo %s with new cs cache %s'
1090 % (self.repo_name, cs_cache))
1092 % (self.repo_name, cs_cache))
1091 self.updated_on = last_change
1093 self.updated_on = last_change
1092 self.changeset_cache = cs_cache
1094 self.changeset_cache = cs_cache
1093 Session().add(self)
1095 Session().add(self)
1094 Session().commit()
1096 Session().commit()
1095 else:
1097 else:
1096 log.debug('Skipping repo:%s already with latest changes'
1098 log.debug('Skipping repo:%s already with latest changes'
1097 % self.repo_name)
1099 % self.repo_name)
1098
1100
1099 @property
1101 @property
1100 def tip(self):
1102 def tip(self):
1101 return self.get_changeset('tip')
1103 return self.get_changeset('tip')
1102
1104
1103 @property
1105 @property
1104 def author(self):
1106 def author(self):
1105 return self.tip.author
1107 return self.tip.author
1106
1108
1107 @property
1109 @property
1108 def last_change(self):
1110 def last_change(self):
1109 return self.scm_instance.last_change
1111 return self.scm_instance.last_change
1110
1112
1111 def get_comments(self, revisions=None):
1113 def get_comments(self, revisions=None):
1112 """
1114 """
1113 Returns comments for this repository grouped by revisions
1115 Returns comments for this repository grouped by revisions
1114
1116
1115 :param revisions: filter query by revisions only
1117 :param revisions: filter query by revisions only
1116 """
1118 """
1117 cmts = ChangesetComment.query()\
1119 cmts = ChangesetComment.query()\
1118 .filter(ChangesetComment.repo == self)
1120 .filter(ChangesetComment.repo == self)
1119 if revisions:
1121 if revisions:
1120 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1122 cmts = cmts.filter(ChangesetComment.revision.in_(revisions))
1121 grouped = defaultdict(list)
1123 grouped = defaultdict(list)
1122 for cmt in cmts.all():
1124 for cmt in cmts.all():
1123 grouped[cmt.revision].append(cmt)
1125 grouped[cmt.revision].append(cmt)
1124 return grouped
1126 return grouped
1125
1127
1126 def statuses(self, revisions=None):
1128 def statuses(self, revisions=None):
1127 """
1129 """
1128 Returns statuses for this repository
1130 Returns statuses for this repository
1129
1131
1130 :param revisions: list of revisions to get statuses for
1132 :param revisions: list of revisions to get statuses for
1131 :type revisions: list
1133 :type revisions: list
1132 """
1134 """
1133
1135
1134 statuses = ChangesetStatus.query()\
1136 statuses = ChangesetStatus.query()\
1135 .filter(ChangesetStatus.repo == self)\
1137 .filter(ChangesetStatus.repo == self)\
1136 .filter(ChangesetStatus.version == 0)
1138 .filter(ChangesetStatus.version == 0)
1137 if revisions:
1139 if revisions:
1138 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1140 statuses = statuses.filter(ChangesetStatus.revision.in_(revisions))
1139 grouped = {}
1141 grouped = {}
1140
1142
1141 #maybe we have open new pullrequest without a status ?
1143 #maybe we have open new pullrequest without a status ?
1142 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1144 stat = ChangesetStatus.STATUS_UNDER_REVIEW
1143 status_lbl = ChangesetStatus.get_status_lbl(stat)
1145 status_lbl = ChangesetStatus.get_status_lbl(stat)
1144 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1146 for pr in PullRequest.query().filter(PullRequest.org_repo == self).all():
1145 for rev in pr.revisions:
1147 for rev in pr.revisions:
1146 pr_id = pr.pull_request_id
1148 pr_id = pr.pull_request_id
1147 pr_repo = pr.other_repo.repo_name
1149 pr_repo = pr.other_repo.repo_name
1148 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1150 grouped[rev] = [stat, status_lbl, pr_id, pr_repo]
1149
1151
1150 for stat in statuses.all():
1152 for stat in statuses.all():
1151 pr_id = pr_repo = None
1153 pr_id = pr_repo = None
1152 if stat.pull_request:
1154 if stat.pull_request:
1153 pr_id = stat.pull_request.pull_request_id
1155 pr_id = stat.pull_request.pull_request_id
1154 pr_repo = stat.pull_request.other_repo.repo_name
1156 pr_repo = stat.pull_request.other_repo.repo_name
1155 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1157 grouped[stat.revision] = [str(stat.status), stat.status_lbl,
1156 pr_id, pr_repo]
1158 pr_id, pr_repo]
1157 return grouped
1159 return grouped
1158
1160
1159 def _repo_size(self):
1161 def _repo_size(self):
1160 from rhodecode.lib import helpers as h
1162 from rhodecode.lib import helpers as h
1161 log.debug('calculating repository size...')
1163 log.debug('calculating repository size...')
1162 return h.format_byte_size(self.scm_instance.size)
1164 return h.format_byte_size(self.scm_instance.size)
1163
1165
1164 #==========================================================================
1166 #==========================================================================
1165 # SCM CACHE INSTANCE
1167 # SCM CACHE INSTANCE
1166 #==========================================================================
1168 #==========================================================================
1167
1169
1168 def set_invalidate(self):
1170 def set_invalidate(self):
1169 """
1171 """
1170 Mark caches of this repo as invalid.
1172 Mark caches of this repo as invalid.
1171 """
1173 """
1172 CacheInvalidation.set_invalidate(self.repo_name)
1174 CacheInvalidation.set_invalidate(self.repo_name)
1173
1175
1174 def scm_instance_no_cache(self):
1176 def scm_instance_no_cache(self):
1175 return self.__get_instance()
1177 return self.__get_instance()
1176
1178
1177 @property
1179 @property
1178 def scm_instance(self):
1180 def scm_instance(self):
1179 import rhodecode
1181 import rhodecode
1180 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1182 full_cache = str2bool(rhodecode.CONFIG.get('vcs_full_cache'))
1181 if full_cache:
1183 if full_cache:
1182 return self.scm_instance_cached()
1184 return self.scm_instance_cached()
1183 return self.__get_instance()
1185 return self.__get_instance()
1184
1186
1185 def scm_instance_cached(self, valid_cache_keys=None):
1187 def scm_instance_cached(self, valid_cache_keys=None):
1186 @cache_region('long_term')
1188 @cache_region('long_term')
1187 def _c(repo_name):
1189 def _c(repo_name):
1188 return self.__get_instance()
1190 return self.__get_instance()
1189 rn = self.repo_name
1191 rn = self.repo_name
1190
1192
1191 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1193 valid = CacheInvalidation.test_and_set_valid(rn, None, valid_cache_keys=valid_cache_keys)
1192 if not valid:
1194 if not valid:
1193 log.debug('Cache for %s invalidated, getting new object' % (rn))
1195 log.debug('Cache for %s invalidated, getting new object' % (rn))
1194 region_invalidate(_c, None, rn)
1196 region_invalidate(_c, None, rn)
1195 else:
1197 else:
1196 log.debug('Getting obj for %s from cache' % (rn))
1198 log.debug('Getting obj for %s from cache' % (rn))
1197 return _c(rn)
1199 return _c(rn)
1198
1200
1199 def __get_instance(self):
1201 def __get_instance(self):
1200 repo_full_path = self.repo_full_path
1202 repo_full_path = self.repo_full_path
1201 try:
1203 try:
1202 alias = get_scm(repo_full_path)[0]
1204 alias = get_scm(repo_full_path)[0]
1203 log.debug('Creating instance of %s repository from %s'
1205 log.debug('Creating instance of %s repository from %s'
1204 % (alias, repo_full_path))
1206 % (alias, repo_full_path))
1205 backend = get_backend(alias)
1207 backend = get_backend(alias)
1206 except VCSError:
1208 except VCSError:
1207 log.error(traceback.format_exc())
1209 log.error(traceback.format_exc())
1208 log.error('Perhaps this repository is in db and not in '
1210 log.error('Perhaps this repository is in db and not in '
1209 'filesystem run rescan repositories with '
1211 'filesystem run rescan repositories with '
1210 '"destroy old data " option from admin panel')
1212 '"destroy old data " option from admin panel')
1211 return
1213 return
1212
1214
1213 if alias == 'hg':
1215 if alias == 'hg':
1214
1216
1215 repo = backend(safe_str(repo_full_path), create=False,
1217 repo = backend(safe_str(repo_full_path), create=False,
1216 baseui=self._ui)
1218 baseui=self._ui)
1217 # skip hidden web repository
1219 # skip hidden web repository
1218 if repo._get_hidden():
1220 if repo._get_hidden():
1219 return
1221 return
1220 else:
1222 else:
1221 repo = backend(repo_full_path, create=False)
1223 repo = backend(repo_full_path, create=False)
1222
1224
1223 return repo
1225 return repo
1224
1226
1225
1227
1226 class RepoGroup(Base, BaseModel):
1228 class RepoGroup(Base, BaseModel):
1227 __tablename__ = 'groups'
1229 __tablename__ = 'groups'
1228 __table_args__ = (
1230 __table_args__ = (
1229 UniqueConstraint('group_name', 'group_parent_id'),
1231 UniqueConstraint('group_name', 'group_parent_id'),
1230 CheckConstraint('group_id != group_parent_id'),
1232 CheckConstraint('group_id != group_parent_id'),
1231 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1233 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1232 'mysql_charset': 'utf8'},
1234 'mysql_charset': 'utf8'},
1233 )
1235 )
1234 __mapper_args__ = {'order_by': 'group_name'}
1236 __mapper_args__ = {'order_by': 'group_name'}
1235
1237
1236 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1238 group_id = Column("group_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1237 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1239 group_name = Column("group_name", String(255, convert_unicode=False, assert_unicode=None), nullable=False, unique=True, default=None)
1238 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1240 group_parent_id = Column("group_parent_id", Integer(), ForeignKey('groups.group_id'), nullable=True, unique=None, default=None)
1239 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1241 group_description = Column("group_description", String(10000, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1240 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1242 enable_locking = Column("enable_locking", Boolean(), nullable=False, unique=None, default=False)
1241 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1243 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=False, default=None)
1242
1244
1243 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1245 repo_group_to_perm = relationship('UserRepoGroupToPerm', cascade='all', order_by='UserRepoGroupToPerm.group_to_perm_id')
1244 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1246 users_group_to_perm = relationship('UserGroupRepoGroupToPerm', cascade='all')
1245 parent_group = relationship('RepoGroup', remote_side=group_id)
1247 parent_group = relationship('RepoGroup', remote_side=group_id)
1246 user = relationship('User')
1248 user = relationship('User')
1247
1249
1248 def __init__(self, group_name='', parent_group=None):
1250 def __init__(self, group_name='', parent_group=None):
1249 self.group_name = group_name
1251 self.group_name = group_name
1250 self.parent_group = parent_group
1252 self.parent_group = parent_group
1251
1253
1252 def __unicode__(self):
1254 def __unicode__(self):
1253 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1255 return u"<%s('id:%s:%s')>" % (self.__class__.__name__, self.group_id,
1254 self.group_name)
1256 self.group_name)
1255
1257
1256 @classmethod
1258 @classmethod
1257 def groups_choices(cls, groups=None, show_empty_group=True):
1259 def groups_choices(cls, groups=None, show_empty_group=True):
1258 from webhelpers.html import literal as _literal
1260 from webhelpers.html import literal as _literal
1259 if not groups:
1261 if not groups:
1260 groups = cls.query().all()
1262 groups = cls.query().all()
1261
1263
1262 repo_groups = []
1264 repo_groups = []
1263 if show_empty_group:
1265 if show_empty_group:
1264 repo_groups = [('-1', '-- %s --' % _('top level'))]
1266 repo_groups = [('-1', '-- %s --' % _('top level'))]
1265 sep = ' &raquo; '
1267 sep = ' &raquo; '
1266 _name = lambda k: _literal(sep.join(k))
1268 _name = lambda k: _literal(sep.join(k))
1267
1269
1268 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1270 repo_groups.extend([(x.group_id, _name(x.full_path_splitted))
1269 for x in groups])
1271 for x in groups])
1270
1272
1271 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1273 repo_groups = sorted(repo_groups, key=lambda t: t[1].split(sep)[0])
1272 return repo_groups
1274 return repo_groups
1273
1275
1274 @classmethod
1276 @classmethod
1275 def url_sep(cls):
1277 def url_sep(cls):
1276 return URL_SEP
1278 return URL_SEP
1277
1279
1278 @classmethod
1280 @classmethod
1279 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1281 def get_by_group_name(cls, group_name, cache=False, case_insensitive=False):
1280 if case_insensitive:
1282 if case_insensitive:
1281 gr = cls.query()\
1283 gr = cls.query()\
1282 .filter(cls.group_name.ilike(group_name))
1284 .filter(cls.group_name.ilike(group_name))
1283 else:
1285 else:
1284 gr = cls.query()\
1286 gr = cls.query()\
1285 .filter(cls.group_name == group_name)
1287 .filter(cls.group_name == group_name)
1286 if cache:
1288 if cache:
1287 gr = gr.options(FromCache(
1289 gr = gr.options(FromCache(
1288 "sql_cache_short",
1290 "sql_cache_short",
1289 "get_group_%s" % _hash_key(group_name)
1291 "get_group_%s" % _hash_key(group_name)
1290 )
1292 )
1291 )
1293 )
1292 return gr.scalar()
1294 return gr.scalar()
1293
1295
1294 @property
1296 @property
1295 def parents(self):
1297 def parents(self):
1296 parents_recursion_limit = 5
1298 parents_recursion_limit = 5
1297 groups = []
1299 groups = []
1298 if self.parent_group is None:
1300 if self.parent_group is None:
1299 return groups
1301 return groups
1300 cur_gr = self.parent_group
1302 cur_gr = self.parent_group
1301 groups.insert(0, cur_gr)
1303 groups.insert(0, cur_gr)
1302 cnt = 0
1304 cnt = 0
1303 while 1:
1305 while 1:
1304 cnt += 1
1306 cnt += 1
1305 gr = getattr(cur_gr, 'parent_group', None)
1307 gr = getattr(cur_gr, 'parent_group', None)
1306 cur_gr = cur_gr.parent_group
1308 cur_gr = cur_gr.parent_group
1307 if gr is None:
1309 if gr is None:
1308 break
1310 break
1309 if cnt == parents_recursion_limit:
1311 if cnt == parents_recursion_limit:
1310 # this will prevent accidental infinit loops
1312 # this will prevent accidental infinit loops
1311 log.error('group nested more than %s' %
1313 log.error('group nested more than %s' %
1312 parents_recursion_limit)
1314 parents_recursion_limit)
1313 break
1315 break
1314
1316
1315 groups.insert(0, gr)
1317 groups.insert(0, gr)
1316 return groups
1318 return groups
1317
1319
1318 @property
1320 @property
1319 def children(self):
1321 def children(self):
1320 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1322 return RepoGroup.query().filter(RepoGroup.parent_group == self)
1321
1323
1322 @property
1324 @property
1323 def name(self):
1325 def name(self):
1324 return self.group_name.split(RepoGroup.url_sep())[-1]
1326 return self.group_name.split(RepoGroup.url_sep())[-1]
1325
1327
1326 @property
1328 @property
1327 def full_path(self):
1329 def full_path(self):
1328 return self.group_name
1330 return self.group_name
1329
1331
1330 @property
1332 @property
1331 def full_path_splitted(self):
1333 def full_path_splitted(self):
1332 return self.group_name.split(RepoGroup.url_sep())
1334 return self.group_name.split(RepoGroup.url_sep())
1333
1335
1334 @property
1336 @property
1335 def repositories(self):
1337 def repositories(self):
1336 return Repository.query()\
1338 return Repository.query()\
1337 .filter(Repository.group == self)\
1339 .filter(Repository.group == self)\
1338 .order_by(Repository.repo_name)
1340 .order_by(Repository.repo_name)
1339
1341
1340 @property
1342 @property
1341 def repositories_recursive_count(self):
1343 def repositories_recursive_count(self):
1342 cnt = self.repositories.count()
1344 cnt = self.repositories.count()
1343
1345
1344 def children_count(group):
1346 def children_count(group):
1345 cnt = 0
1347 cnt = 0
1346 for child in group.children:
1348 for child in group.children:
1347 cnt += child.repositories.count()
1349 cnt += child.repositories.count()
1348 cnt += children_count(child)
1350 cnt += children_count(child)
1349 return cnt
1351 return cnt
1350
1352
1351 return cnt + children_count(self)
1353 return cnt + children_count(self)
1352
1354
1353 def _recursive_objects(self, include_repos=True):
1355 def _recursive_objects(self, include_repos=True):
1354 all_ = []
1356 all_ = []
1355
1357
1356 def _get_members(root_gr):
1358 def _get_members(root_gr):
1357 if include_repos:
1359 if include_repos:
1358 for r in root_gr.repositories:
1360 for r in root_gr.repositories:
1359 all_.append(r)
1361 all_.append(r)
1360 childs = root_gr.children.all()
1362 childs = root_gr.children.all()
1361 if childs:
1363 if childs:
1362 for gr in childs:
1364 for gr in childs:
1363 all_.append(gr)
1365 all_.append(gr)
1364 _get_members(gr)
1366 _get_members(gr)
1365
1367
1366 _get_members(self)
1368 _get_members(self)
1367 return [self] + all_
1369 return [self] + all_
1368
1370
1369 def recursive_groups_and_repos(self):
1371 def recursive_groups_and_repos(self):
1370 """
1372 """
1371 Recursive return all groups, with repositories in those groups
1373 Recursive return all groups, with repositories in those groups
1372 """
1374 """
1373 return self._recursive_objects()
1375 return self._recursive_objects()
1374
1376
1375 def recursive_groups(self):
1377 def recursive_groups(self):
1376 """
1378 """
1377 Returns all children groups for this group including children of children
1379 Returns all children groups for this group including children of children
1378 """
1380 """
1379 return self._recursive_objects(include_repos=False)
1381 return self._recursive_objects(include_repos=False)
1380
1382
1381 def get_new_name(self, group_name):
1383 def get_new_name(self, group_name):
1382 """
1384 """
1383 returns new full group name based on parent and new name
1385 returns new full group name based on parent and new name
1384
1386
1385 :param group_name:
1387 :param group_name:
1386 """
1388 """
1387 path_prefix = (self.parent_group.full_path_splitted if
1389 path_prefix = (self.parent_group.full_path_splitted if
1388 self.parent_group else [])
1390 self.parent_group else [])
1389 return RepoGroup.url_sep().join(path_prefix + [group_name])
1391 return RepoGroup.url_sep().join(path_prefix + [group_name])
1390
1392
1391
1393
1392 class Permission(Base, BaseModel):
1394 class Permission(Base, BaseModel):
1393 __tablename__ = 'permissions'
1395 __tablename__ = 'permissions'
1394 __table_args__ = (
1396 __table_args__ = (
1395 Index('p_perm_name_idx', 'permission_name'),
1397 Index('p_perm_name_idx', 'permission_name'),
1396 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1398 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1397 'mysql_charset': 'utf8'},
1399 'mysql_charset': 'utf8'},
1398 )
1400 )
1399 PERMS = [
1401 PERMS = [
1400 ('hg.admin', _('RhodeCode Administrator')),
1402 ('hg.admin', _('RhodeCode Administrator')),
1401
1403
1402 ('repository.none', _('Repository no access')),
1404 ('repository.none', _('Repository no access')),
1403 ('repository.read', _('Repository read access')),
1405 ('repository.read', _('Repository read access')),
1404 ('repository.write', _('Repository write access')),
1406 ('repository.write', _('Repository write access')),
1405 ('repository.admin', _('Repository admin access')),
1407 ('repository.admin', _('Repository admin access')),
1406
1408
1407 ('group.none', _('Repository group no access')),
1409 ('group.none', _('Repository group no access')),
1408 ('group.read', _('Repository group read access')),
1410 ('group.read', _('Repository group read access')),
1409 ('group.write', _('Repository group write access')),
1411 ('group.write', _('Repository group write access')),
1410 ('group.admin', _('Repository group admin access')),
1412 ('group.admin', _('Repository group admin access')),
1411
1413
1412 ('usergroup.none', _('User group no access')),
1414 ('usergroup.none', _('User group no access')),
1413 ('usergroup.read', _('User group read access')),
1415 ('usergroup.read', _('User group read access')),
1414 ('usergroup.write', _('User group write access')),
1416 ('usergroup.write', _('User group write access')),
1415 ('usergroup.admin', _('User group admin access')),
1417 ('usergroup.admin', _('User group admin access')),
1416
1418
1417 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1419 ('hg.repogroup.create.false', _('Repository Group creation disabled')),
1418 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1420 ('hg.repogroup.create.true', _('Repository Group creation enabled')),
1419
1421
1420 ('hg.usergroup.create.false', _('User Group creation disabled')),
1422 ('hg.usergroup.create.false', _('User Group creation disabled')),
1421 ('hg.usergroup.create.true', _('User Group creation enabled')),
1423 ('hg.usergroup.create.true', _('User Group creation enabled')),
1422
1424
1423 ('hg.create.none', _('Repository creation disabled')),
1425 ('hg.create.none', _('Repository creation disabled')),
1424 ('hg.create.repository', _('Repository creation enabled')),
1426 ('hg.create.repository', _('Repository creation enabled')),
1425
1427
1426 ('hg.fork.none', _('Repository forking disabled')),
1428 ('hg.fork.none', _('Repository forking disabled')),
1427 ('hg.fork.repository', _('Repository forking enabled')),
1429 ('hg.fork.repository', _('Repository forking enabled')),
1428
1430
1429 ('hg.register.none', _('Registration disabled')),
1431 ('hg.register.none', _('Registration disabled')),
1430 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1432 ('hg.register.manual_activate', _('User Registration with manual account activation')),
1431 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1433 ('hg.register.auto_activate', _('User Registration with automatic account activation')),
1432
1434
1433 ('hg.extern_activate.manual', _('Manual activation of external account')),
1435 ('hg.extern_activate.manual', _('Manual activation of external account')),
1434 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1436 ('hg.extern_activate.auto', _('Automatic activation of external account')),
1435
1437
1436 ]
1438 ]
1437
1439
1438 #definition of system default permissions for DEFAULT user
1440 #definition of system default permissions for DEFAULT user
1439 DEFAULT_USER_PERMISSIONS = [
1441 DEFAULT_USER_PERMISSIONS = [
1440 'repository.read',
1442 'repository.read',
1441 'group.read',
1443 'group.read',
1442 'usergroup.read',
1444 'usergroup.read',
1443 'hg.create.repository',
1445 'hg.create.repository',
1444 'hg.fork.repository',
1446 'hg.fork.repository',
1445 'hg.register.manual_activate',
1447 'hg.register.manual_activate',
1446 'hg.extern_activate.auto',
1448 'hg.extern_activate.auto',
1447 ]
1449 ]
1448
1450
1449 # defines which permissions are more important higher the more important
1451 # defines which permissions are more important higher the more important
1450 # Weight defines which permissions are more important.
1452 # Weight defines which permissions are more important.
1451 # The higher number the more important.
1453 # The higher number the more important.
1452 PERM_WEIGHTS = {
1454 PERM_WEIGHTS = {
1453 'repository.none': 0,
1455 'repository.none': 0,
1454 'repository.read': 1,
1456 'repository.read': 1,
1455 'repository.write': 3,
1457 'repository.write': 3,
1456 'repository.admin': 4,
1458 'repository.admin': 4,
1457
1459
1458 'group.none': 0,
1460 'group.none': 0,
1459 'group.read': 1,
1461 'group.read': 1,
1460 'group.write': 3,
1462 'group.write': 3,
1461 'group.admin': 4,
1463 'group.admin': 4,
1462
1464
1463 'usergroup.none': 0,
1465 'usergroup.none': 0,
1464 'usergroup.read': 1,
1466 'usergroup.read': 1,
1465 'usergroup.write': 3,
1467 'usergroup.write': 3,
1466 'usergroup.admin': 4,
1468 'usergroup.admin': 4,
1467 'hg.repogroup.create.false': 0,
1469 'hg.repogroup.create.false': 0,
1468 'hg.repogroup.create.true': 1,
1470 'hg.repogroup.create.true': 1,
1469
1471
1470 'hg.usergroup.create.false': 0,
1472 'hg.usergroup.create.false': 0,
1471 'hg.usergroup.create.true': 1,
1473 'hg.usergroup.create.true': 1,
1472
1474
1473 'hg.fork.none': 0,
1475 'hg.fork.none': 0,
1474 'hg.fork.repository': 1,
1476 'hg.fork.repository': 1,
1475 'hg.create.none': 0,
1477 'hg.create.none': 0,
1476 'hg.create.repository': 1
1478 'hg.create.repository': 1
1477 }
1479 }
1478
1480
1479 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1481 permission_id = Column("permission_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1480 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1482 permission_name = Column("permission_name", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1481 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1483 permission_longname = Column("permission_longname", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1482
1484
1483 def __unicode__(self):
1485 def __unicode__(self):
1484 return u"<%s('%s:%s')>" % (
1486 return u"<%s('%s:%s')>" % (
1485 self.__class__.__name__, self.permission_id, self.permission_name
1487 self.__class__.__name__, self.permission_id, self.permission_name
1486 )
1488 )
1487
1489
1488 @classmethod
1490 @classmethod
1489 def get_by_key(cls, key):
1491 def get_by_key(cls, key):
1490 return cls.query().filter(cls.permission_name == key).scalar()
1492 return cls.query().filter(cls.permission_name == key).scalar()
1491
1493
1492 @classmethod
1494 @classmethod
1493 def get_default_perms(cls, default_user_id):
1495 def get_default_perms(cls, default_user_id):
1494 q = Session().query(UserRepoToPerm, Repository, cls)\
1496 q = Session().query(UserRepoToPerm, Repository, cls)\
1495 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1497 .join((Repository, UserRepoToPerm.repository_id == Repository.repo_id))\
1496 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1498 .join((cls, UserRepoToPerm.permission_id == cls.permission_id))\
1497 .filter(UserRepoToPerm.user_id == default_user_id)
1499 .filter(UserRepoToPerm.user_id == default_user_id)
1498
1500
1499 return q.all()
1501 return q.all()
1500
1502
1501 @classmethod
1503 @classmethod
1502 def get_default_group_perms(cls, default_user_id):
1504 def get_default_group_perms(cls, default_user_id):
1503 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1505 q = Session().query(UserRepoGroupToPerm, RepoGroup, cls)\
1504 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1506 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
1505 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1507 .join((cls, UserRepoGroupToPerm.permission_id == cls.permission_id))\
1506 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1508 .filter(UserRepoGroupToPerm.user_id == default_user_id)
1507
1509
1508 return q.all()
1510 return q.all()
1509
1511
1510 @classmethod
1512 @classmethod
1511 def get_default_user_group_perms(cls, default_user_id):
1513 def get_default_user_group_perms(cls, default_user_id):
1512 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1514 q = Session().query(UserUserGroupToPerm, UserGroup, cls)\
1513 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1515 .join((UserGroup, UserUserGroupToPerm.user_group_id == UserGroup.users_group_id))\
1514 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1516 .join((cls, UserUserGroupToPerm.permission_id == cls.permission_id))\
1515 .filter(UserUserGroupToPerm.user_id == default_user_id)
1517 .filter(UserUserGroupToPerm.user_id == default_user_id)
1516
1518
1517 return q.all()
1519 return q.all()
1518
1520
1519
1521
1520 class UserRepoToPerm(Base, BaseModel):
1522 class UserRepoToPerm(Base, BaseModel):
1521 __tablename__ = 'repo_to_perm'
1523 __tablename__ = 'repo_to_perm'
1522 __table_args__ = (
1524 __table_args__ = (
1523 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1525 UniqueConstraint('user_id', 'repository_id', 'permission_id'),
1524 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1526 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1525 'mysql_charset': 'utf8'}
1527 'mysql_charset': 'utf8'}
1526 )
1528 )
1527 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1529 repo_to_perm_id = Column("repo_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1528 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1530 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1529 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1531 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1530 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1532 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1531
1533
1532 user = relationship('User')
1534 user = relationship('User')
1533 repository = relationship('Repository')
1535 repository = relationship('Repository')
1534 permission = relationship('Permission')
1536 permission = relationship('Permission')
1535
1537
1536 @classmethod
1538 @classmethod
1537 def create(cls, user, repository, permission):
1539 def create(cls, user, repository, permission):
1538 n = cls()
1540 n = cls()
1539 n.user = user
1541 n.user = user
1540 n.repository = repository
1542 n.repository = repository
1541 n.permission = permission
1543 n.permission = permission
1542 Session().add(n)
1544 Session().add(n)
1543 return n
1545 return n
1544
1546
1545 def __unicode__(self):
1547 def __unicode__(self):
1546 return u'<%s => %s >' % (self.user, self.repository)
1548 return u'<%s => %s >' % (self.user, self.repository)
1547
1549
1548
1550
1549 class UserUserGroupToPerm(Base, BaseModel):
1551 class UserUserGroupToPerm(Base, BaseModel):
1550 __tablename__ = 'user_user_group_to_perm'
1552 __tablename__ = 'user_user_group_to_perm'
1551 __table_args__ = (
1553 __table_args__ = (
1552 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1554 UniqueConstraint('user_id', 'user_group_id', 'permission_id'),
1553 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1555 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1554 'mysql_charset': 'utf8'}
1556 'mysql_charset': 'utf8'}
1555 )
1557 )
1556 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1558 user_user_group_to_perm_id = Column("user_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1557 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1559 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1558 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1560 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1559 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1561 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1560
1562
1561 user = relationship('User')
1563 user = relationship('User')
1562 user_group = relationship('UserGroup')
1564 user_group = relationship('UserGroup')
1563 permission = relationship('Permission')
1565 permission = relationship('Permission')
1564
1566
1565 @classmethod
1567 @classmethod
1566 def create(cls, user, user_group, permission):
1568 def create(cls, user, user_group, permission):
1567 n = cls()
1569 n = cls()
1568 n.user = user
1570 n.user = user
1569 n.user_group = user_group
1571 n.user_group = user_group
1570 n.permission = permission
1572 n.permission = permission
1571 Session().add(n)
1573 Session().add(n)
1572 return n
1574 return n
1573
1575
1574 def __unicode__(self):
1576 def __unicode__(self):
1575 return u'<%s => %s >' % (self.user, self.user_group)
1577 return u'<%s => %s >' % (self.user, self.user_group)
1576
1578
1577
1579
1578 class UserToPerm(Base, BaseModel):
1580 class UserToPerm(Base, BaseModel):
1579 __tablename__ = 'user_to_perm'
1581 __tablename__ = 'user_to_perm'
1580 __table_args__ = (
1582 __table_args__ = (
1581 UniqueConstraint('user_id', 'permission_id'),
1583 UniqueConstraint('user_id', 'permission_id'),
1582 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1584 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1583 'mysql_charset': 'utf8'}
1585 'mysql_charset': 'utf8'}
1584 )
1586 )
1585 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1587 user_to_perm_id = Column("user_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1586 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1588 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1587 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1589 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1588
1590
1589 user = relationship('User')
1591 user = relationship('User')
1590 permission = relationship('Permission', lazy='joined')
1592 permission = relationship('Permission', lazy='joined')
1591
1593
1592 def __unicode__(self):
1594 def __unicode__(self):
1593 return u'<%s => %s >' % (self.user, self.permission)
1595 return u'<%s => %s >' % (self.user, self.permission)
1594
1596
1595
1597
1596 class UserGroupRepoToPerm(Base, BaseModel):
1598 class UserGroupRepoToPerm(Base, BaseModel):
1597 __tablename__ = 'users_group_repo_to_perm'
1599 __tablename__ = 'users_group_repo_to_perm'
1598 __table_args__ = (
1600 __table_args__ = (
1599 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1601 UniqueConstraint('repository_id', 'users_group_id', 'permission_id'),
1600 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1602 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1601 'mysql_charset': 'utf8'}
1603 'mysql_charset': 'utf8'}
1602 )
1604 )
1603 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1605 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1604 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1606 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1605 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1607 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1606 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1608 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=None, default=None)
1607
1609
1608 users_group = relationship('UserGroup')
1610 users_group = relationship('UserGroup')
1609 permission = relationship('Permission')
1611 permission = relationship('Permission')
1610 repository = relationship('Repository')
1612 repository = relationship('Repository')
1611
1613
1612 @classmethod
1614 @classmethod
1613 def create(cls, users_group, repository, permission):
1615 def create(cls, users_group, repository, permission):
1614 n = cls()
1616 n = cls()
1615 n.users_group = users_group
1617 n.users_group = users_group
1616 n.repository = repository
1618 n.repository = repository
1617 n.permission = permission
1619 n.permission = permission
1618 Session().add(n)
1620 Session().add(n)
1619 return n
1621 return n
1620
1622
1621 def __unicode__(self):
1623 def __unicode__(self):
1622 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1624 return u'<UserGroupRepoToPerm:%s => %s >' % (self.users_group, self.repository)
1623
1625
1624
1626
1625 class UserGroupUserGroupToPerm(Base, BaseModel):
1627 class UserGroupUserGroupToPerm(Base, BaseModel):
1626 __tablename__ = 'user_group_user_group_to_perm'
1628 __tablename__ = 'user_group_user_group_to_perm'
1627 __table_args__ = (
1629 __table_args__ = (
1628 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1630 UniqueConstraint('target_user_group_id', 'user_group_id', 'permission_id'),
1629 CheckConstraint('target_user_group_id != user_group_id'),
1631 CheckConstraint('target_user_group_id != user_group_id'),
1630 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1632 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1631 'mysql_charset': 'utf8'}
1633 'mysql_charset': 'utf8'}
1632 )
1634 )
1633 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1635 user_group_user_group_to_perm_id = Column("user_group_user_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1634 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1636 target_user_group_id = Column("target_user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1635 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1637 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1636 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1638 user_group_id = Column("user_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1637
1639
1638 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1640 target_user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.target_user_group_id==UserGroup.users_group_id')
1639 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1641 user_group = relationship('UserGroup', primaryjoin='UserGroupUserGroupToPerm.user_group_id==UserGroup.users_group_id')
1640 permission = relationship('Permission')
1642 permission = relationship('Permission')
1641
1643
1642 @classmethod
1644 @classmethod
1643 def create(cls, target_user_group, user_group, permission):
1645 def create(cls, target_user_group, user_group, permission):
1644 n = cls()
1646 n = cls()
1645 n.target_user_group = target_user_group
1647 n.target_user_group = target_user_group
1646 n.user_group = user_group
1648 n.user_group = user_group
1647 n.permission = permission
1649 n.permission = permission
1648 Session().add(n)
1650 Session().add(n)
1649 return n
1651 return n
1650
1652
1651 def __unicode__(self):
1653 def __unicode__(self):
1652 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1654 return u'<UserGroupUserGroup:%s => %s >' % (self.target_user_group, self.user_group)
1653
1655
1654
1656
1655 class UserGroupToPerm(Base, BaseModel):
1657 class UserGroupToPerm(Base, BaseModel):
1656 __tablename__ = 'users_group_to_perm'
1658 __tablename__ = 'users_group_to_perm'
1657 __table_args__ = (
1659 __table_args__ = (
1658 UniqueConstraint('users_group_id', 'permission_id',),
1660 UniqueConstraint('users_group_id', 'permission_id',),
1659 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1661 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1660 'mysql_charset': 'utf8'}
1662 'mysql_charset': 'utf8'}
1661 )
1663 )
1662 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1664 users_group_to_perm_id = Column("users_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1663 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1665 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1664 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1666 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1665
1667
1666 users_group = relationship('UserGroup')
1668 users_group = relationship('UserGroup')
1667 permission = relationship('Permission')
1669 permission = relationship('Permission')
1668
1670
1669
1671
1670 class UserRepoGroupToPerm(Base, BaseModel):
1672 class UserRepoGroupToPerm(Base, BaseModel):
1671 __tablename__ = 'user_repo_group_to_perm'
1673 __tablename__ = 'user_repo_group_to_perm'
1672 __table_args__ = (
1674 __table_args__ = (
1673 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1675 UniqueConstraint('user_id', 'group_id', 'permission_id'),
1674 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1676 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1675 'mysql_charset': 'utf8'}
1677 'mysql_charset': 'utf8'}
1676 )
1678 )
1677
1679
1678 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1680 group_to_perm_id = Column("group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1679 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1681 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1680 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1682 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1681 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1683 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1682
1684
1683 user = relationship('User')
1685 user = relationship('User')
1684 group = relationship('RepoGroup')
1686 group = relationship('RepoGroup')
1685 permission = relationship('Permission')
1687 permission = relationship('Permission')
1686
1688
1687
1689
1688 class UserGroupRepoGroupToPerm(Base, BaseModel):
1690 class UserGroupRepoGroupToPerm(Base, BaseModel):
1689 __tablename__ = 'users_group_repo_group_to_perm'
1691 __tablename__ = 'users_group_repo_group_to_perm'
1690 __table_args__ = (
1692 __table_args__ = (
1691 UniqueConstraint('users_group_id', 'group_id'),
1693 UniqueConstraint('users_group_id', 'group_id'),
1692 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1694 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1693 'mysql_charset': 'utf8'}
1695 'mysql_charset': 'utf8'}
1694 )
1696 )
1695
1697
1696 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1698 users_group_repo_group_to_perm_id = Column("users_group_repo_group_to_perm_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1697 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1699 users_group_id = Column("users_group_id", Integer(), ForeignKey('users_groups.users_group_id'), nullable=False, unique=None, default=None)
1698 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1700 group_id = Column("group_id", Integer(), ForeignKey('groups.group_id'), nullable=False, unique=None, default=None)
1699 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1701 permission_id = Column("permission_id", Integer(), ForeignKey('permissions.permission_id'), nullable=False, unique=None, default=None)
1700
1702
1701 users_group = relationship('UserGroup')
1703 users_group = relationship('UserGroup')
1702 permission = relationship('Permission')
1704 permission = relationship('Permission')
1703 group = relationship('RepoGroup')
1705 group = relationship('RepoGroup')
1704
1706
1705
1707
1706 class Statistics(Base, BaseModel):
1708 class Statistics(Base, BaseModel):
1707 __tablename__ = 'statistics'
1709 __tablename__ = 'statistics'
1708 __table_args__ = (
1710 __table_args__ = (
1709 UniqueConstraint('repository_id'),
1711 UniqueConstraint('repository_id'),
1710 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1712 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1711 'mysql_charset': 'utf8'}
1713 'mysql_charset': 'utf8'}
1712 )
1714 )
1713 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1715 stat_id = Column("stat_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1714 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1716 repository_id = Column("repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=False, unique=True, default=None)
1715 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1717 stat_on_revision = Column("stat_on_revision", Integer(), nullable=False)
1716 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1718 commit_activity = Column("commit_activity", LargeBinary(1000000), nullable=False)#JSON data
1717 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1719 commit_activity_combined = Column("commit_activity_combined", LargeBinary(), nullable=False)#JSON data
1718 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1720 languages = Column("languages", LargeBinary(1000000), nullable=False)#JSON data
1719
1721
1720 repository = relationship('Repository', single_parent=True)
1722 repository = relationship('Repository', single_parent=True)
1721
1723
1722
1724
1723 class UserFollowing(Base, BaseModel):
1725 class UserFollowing(Base, BaseModel):
1724 __tablename__ = 'user_followings'
1726 __tablename__ = 'user_followings'
1725 __table_args__ = (
1727 __table_args__ = (
1726 UniqueConstraint('user_id', 'follows_repository_id'),
1728 UniqueConstraint('user_id', 'follows_repository_id'),
1727 UniqueConstraint('user_id', 'follows_user_id'),
1729 UniqueConstraint('user_id', 'follows_user_id'),
1728 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1730 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1729 'mysql_charset': 'utf8'}
1731 'mysql_charset': 'utf8'}
1730 )
1732 )
1731
1733
1732 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1734 user_following_id = Column("user_following_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1733 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1735 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None, default=None)
1734 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1736 follows_repo_id = Column("follows_repository_id", Integer(), ForeignKey('repositories.repo_id'), nullable=True, unique=None, default=None)
1735 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1737 follows_user_id = Column("follows_user_id", Integer(), ForeignKey('users.user_id'), nullable=True, unique=None, default=None)
1736 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1738 follows_from = Column('follows_from', DateTime(timezone=False), nullable=True, unique=None, default=datetime.datetime.now)
1737
1739
1738 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1740 user = relationship('User', primaryjoin='User.user_id==UserFollowing.user_id')
1739
1741
1740 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1742 follows_user = relationship('User', primaryjoin='User.user_id==UserFollowing.follows_user_id')
1741 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1743 follows_repository = relationship('Repository', order_by='Repository.repo_name')
1742
1744
1743 @classmethod
1745 @classmethod
1744 def get_repo_followers(cls, repo_id):
1746 def get_repo_followers(cls, repo_id):
1745 return cls.query().filter(cls.follows_repo_id == repo_id)
1747 return cls.query().filter(cls.follows_repo_id == repo_id)
1746
1748
1747
1749
1748 class CacheInvalidation(Base, BaseModel):
1750 class CacheInvalidation(Base, BaseModel):
1749 __tablename__ = 'cache_invalidation'
1751 __tablename__ = 'cache_invalidation'
1750 __table_args__ = (
1752 __table_args__ = (
1751 UniqueConstraint('cache_key'),
1753 UniqueConstraint('cache_key'),
1752 Index('key_idx', 'cache_key'),
1754 Index('key_idx', 'cache_key'),
1753 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1755 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1754 'mysql_charset': 'utf8'},
1756 'mysql_charset': 'utf8'},
1755 )
1757 )
1756 # cache_id, not used
1758 # cache_id, not used
1757 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1759 cache_id = Column("cache_id", Integer(), nullable=False, unique=True, default=None, primary_key=True)
1758 # cache_key as created by _get_cache_key
1760 # cache_key as created by _get_cache_key
1759 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1761 cache_key = Column("cache_key", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1760 # cache_args is a repo_name
1762 # cache_args is a repo_name
1761 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1763 cache_args = Column("cache_args", String(255, convert_unicode=False, assert_unicode=None), nullable=True, unique=None, default=None)
1762 # instance sets cache_active True when it is caching,
1764 # instance sets cache_active True when it is caching,
1763 # other instances set cache_active to False to indicate that this cache is invalid
1765 # other instances set cache_active to False to indicate that this cache is invalid
1764 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1766 cache_active = Column("cache_active", Boolean(), nullable=True, unique=None, default=False)
1765
1767
1766 def __init__(self, cache_key, repo_name=''):
1768 def __init__(self, cache_key, repo_name=''):
1767 self.cache_key = cache_key
1769 self.cache_key = cache_key
1768 self.cache_args = repo_name
1770 self.cache_args = repo_name
1769 self.cache_active = False
1771 self.cache_active = False
1770
1772
1771 def __unicode__(self):
1773 def __unicode__(self):
1772 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1774 return u"<%s('%s:%s[%s]')>" % (self.__class__.__name__,
1773 self.cache_id, self.cache_key, self.cache_active)
1775 self.cache_id, self.cache_key, self.cache_active)
1774
1776
1775 def _cache_key_partition(self):
1777 def _cache_key_partition(self):
1776 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1778 prefix, repo_name, suffix = self.cache_key.partition(self.cache_args)
1777 return prefix, repo_name, suffix
1779 return prefix, repo_name, suffix
1778
1780
1779 def get_prefix(self):
1781 def get_prefix(self):
1780 """
1782 """
1781 get prefix that might have been used in _get_cache_key to
1783 get prefix that might have been used in _get_cache_key to
1782 generate self.cache_key. Only used for informational purposes
1784 generate self.cache_key. Only used for informational purposes
1783 in repo_edit.html.
1785 in repo_edit.html.
1784 """
1786 """
1785 # prefix, repo_name, suffix
1787 # prefix, repo_name, suffix
1786 return self._cache_key_partition()[0]
1788 return self._cache_key_partition()[0]
1787
1789
1788 def get_suffix(self):
1790 def get_suffix(self):
1789 """
1791 """
1790 get suffix that might have been used in _get_cache_key to
1792 get suffix that might have been used in _get_cache_key to
1791 generate self.cache_key. Only used for informational purposes
1793 generate self.cache_key. Only used for informational purposes
1792 in repo_edit.html.
1794 in repo_edit.html.
1793 """
1795 """
1794 # prefix, repo_name, suffix
1796 # prefix, repo_name, suffix
1795 return self._cache_key_partition()[2]
1797 return self._cache_key_partition()[2]
1796
1798
1797 @classmethod
1799 @classmethod
1798 def clear_cache(cls):
1800 def clear_cache(cls):
1799 """
1801 """
1800 Delete all cache keys from database.
1802 Delete all cache keys from database.
1801 Should only be run when all instances are down and all entries thus stale.
1803 Should only be run when all instances are down and all entries thus stale.
1802 """
1804 """
1803 cls.query().delete()
1805 cls.query().delete()
1804 Session().commit()
1806 Session().commit()
1805
1807
1806 @classmethod
1808 @classmethod
1807 def _get_cache_key(cls, key):
1809 def _get_cache_key(cls, key):
1808 """
1810 """
1809 Wrapper for generating a unique cache key for this instance and "key".
1811 Wrapper for generating a unique cache key for this instance and "key".
1810 key must / will start with a repo_name which will be stored in .cache_args .
1812 key must / will start with a repo_name which will be stored in .cache_args .
1811 """
1813 """
1812 import rhodecode
1814 import rhodecode
1813 prefix = rhodecode.CONFIG.get('instance_id', '')
1815 prefix = rhodecode.CONFIG.get('instance_id', '')
1814 return "%s%s" % (prefix, key)
1816 return "%s%s" % (prefix, key)
1815
1817
1816 @classmethod
1818 @classmethod
1817 def set_invalidate(cls, repo_name):
1819 def set_invalidate(cls, repo_name):
1818 """
1820 """
1819 Mark all caches of a repo as invalid in the database.
1821 Mark all caches of a repo as invalid in the database.
1820 """
1822 """
1821 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1823 inv_objs = Session().query(cls).filter(cls.cache_args == repo_name).all()
1822
1824
1823 try:
1825 try:
1824 for inv_obj in inv_objs:
1826 for inv_obj in inv_objs:
1825 log.debug('marking %s key for invalidation based on repo_name=%s'
1827 log.debug('marking %s key for invalidation based on repo_name=%s'
1826 % (inv_obj, safe_str(repo_name)))
1828 % (inv_obj, safe_str(repo_name)))
1827 inv_obj.cache_active = False
1829 inv_obj.cache_active = False
1828 Session().add(inv_obj)
1830 Session().add(inv_obj)
1829 Session().commit()
1831 Session().commit()
1830 except Exception:
1832 except Exception:
1831 log.error(traceback.format_exc())
1833 log.error(traceback.format_exc())
1832 Session().rollback()
1834 Session().rollback()
1833
1835
1834 @classmethod
1836 @classmethod
1835 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1837 def test_and_set_valid(cls, repo_name, kind, valid_cache_keys=None):
1836 """
1838 """
1837 Mark this cache key as active and currently cached.
1839 Mark this cache key as active and currently cached.
1838 Return True if the existing cache registration still was valid.
1840 Return True if the existing cache registration still was valid.
1839 Return False to indicate that it had been invalidated and caches should be refreshed.
1841 Return False to indicate that it had been invalidated and caches should be refreshed.
1840 """
1842 """
1841
1843
1842 key = (repo_name + '_' + kind) if kind else repo_name
1844 key = (repo_name + '_' + kind) if kind else repo_name
1843 cache_key = cls._get_cache_key(key)
1845 cache_key = cls._get_cache_key(key)
1844
1846
1845 if valid_cache_keys and cache_key in valid_cache_keys:
1847 if valid_cache_keys and cache_key in valid_cache_keys:
1846 return True
1848 return True
1847
1849
1848 try:
1850 try:
1849 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1851 inv_obj = cls.query().filter(cls.cache_key == cache_key).scalar()
1850 if not inv_obj:
1852 if not inv_obj:
1851 inv_obj = CacheInvalidation(cache_key, repo_name)
1853 inv_obj = CacheInvalidation(cache_key, repo_name)
1852 was_valid = inv_obj.cache_active
1854 was_valid = inv_obj.cache_active
1853 inv_obj.cache_active = True
1855 inv_obj.cache_active = True
1854 Session().add(inv_obj)
1856 Session().add(inv_obj)
1855 Session().commit()
1857 Session().commit()
1856 return was_valid
1858 return was_valid
1857 except Exception:
1859 except Exception:
1858 log.error(traceback.format_exc())
1860 log.error(traceback.format_exc())
1859 Session().rollback()
1861 Session().rollback()
1860 return False
1862 return False
1861
1863
1862 @classmethod
1864 @classmethod
1863 def get_valid_cache_keys(cls):
1865 def get_valid_cache_keys(cls):
1864 """
1866 """
1865 Return opaque object with information of which caches still are valid
1867 Return opaque object with information of which caches still are valid
1866 and can be used without checking for invalidation.
1868 and can be used without checking for invalidation.
1867 """
1869 """
1868 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1870 return set(inv_obj.cache_key for inv_obj in cls.query().filter(cls.cache_active).all())
1869
1871
1870
1872
1871 class ChangesetComment(Base, BaseModel):
1873 class ChangesetComment(Base, BaseModel):
1872 __tablename__ = 'changeset_comments'
1874 __tablename__ = 'changeset_comments'
1873 __table_args__ = (
1875 __table_args__ = (
1874 Index('cc_revision_idx', 'revision'),
1876 Index('cc_revision_idx', 'revision'),
1875 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1877 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1876 'mysql_charset': 'utf8'},
1878 'mysql_charset': 'utf8'},
1877 )
1879 )
1878 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1880 comment_id = Column('comment_id', Integer(), nullable=False, primary_key=True)
1879 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1881 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1880 revision = Column('revision', String(40), nullable=True)
1882 revision = Column('revision', String(40), nullable=True)
1881 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1883 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1882 line_no = Column('line_no', Unicode(10), nullable=True)
1884 line_no = Column('line_no', Unicode(10), nullable=True)
1883 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1885 hl_lines = Column('hl_lines', Unicode(512), nullable=True)
1884 f_path = Column('f_path', Unicode(1000), nullable=True)
1886 f_path = Column('f_path', Unicode(1000), nullable=True)
1885 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1887 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), nullable=False)
1886 text = Column('text', UnicodeText(25000), nullable=False)
1888 text = Column('text', UnicodeText(25000), nullable=False)
1887 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1889 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1888 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1890 modified_at = Column('modified_at', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1889
1891
1890 author = relationship('User', lazy='joined')
1892 author = relationship('User', lazy='joined')
1891 repo = relationship('Repository')
1893 repo = relationship('Repository')
1892 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1894 status_change = relationship('ChangesetStatus', cascade="all, delete, delete-orphan")
1893 pull_request = relationship('PullRequest', lazy='joined')
1895 pull_request = relationship('PullRequest', lazy='joined')
1894
1896
1895 @classmethod
1897 @classmethod
1896 def get_users(cls, revision=None, pull_request_id=None):
1898 def get_users(cls, revision=None, pull_request_id=None):
1897 """
1899 """
1898 Returns user associated with this ChangesetComment. ie those
1900 Returns user associated with this ChangesetComment. ie those
1899 who actually commented
1901 who actually commented
1900
1902
1901 :param cls:
1903 :param cls:
1902 :param revision:
1904 :param revision:
1903 """
1905 """
1904 q = Session().query(User)\
1906 q = Session().query(User)\
1905 .join(ChangesetComment.author)
1907 .join(ChangesetComment.author)
1906 if revision:
1908 if revision:
1907 q = q.filter(cls.revision == revision)
1909 q = q.filter(cls.revision == revision)
1908 elif pull_request_id:
1910 elif pull_request_id:
1909 q = q.filter(cls.pull_request_id == pull_request_id)
1911 q = q.filter(cls.pull_request_id == pull_request_id)
1910 return q.all()
1912 return q.all()
1911
1913
1912
1914
1913 class ChangesetStatus(Base, BaseModel):
1915 class ChangesetStatus(Base, BaseModel):
1914 __tablename__ = 'changeset_statuses'
1916 __tablename__ = 'changeset_statuses'
1915 __table_args__ = (
1917 __table_args__ = (
1916 Index('cs_revision_idx', 'revision'),
1918 Index('cs_revision_idx', 'revision'),
1917 Index('cs_version_idx', 'version'),
1919 Index('cs_version_idx', 'version'),
1918 UniqueConstraint('repo_id', 'revision', 'version'),
1920 UniqueConstraint('repo_id', 'revision', 'version'),
1919 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1921 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1920 'mysql_charset': 'utf8'}
1922 'mysql_charset': 'utf8'}
1921 )
1923 )
1922 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1924 STATUS_NOT_REVIEWED = DEFAULT = 'not_reviewed'
1923 STATUS_APPROVED = 'approved'
1925 STATUS_APPROVED = 'approved'
1924 STATUS_REJECTED = 'rejected'
1926 STATUS_REJECTED = 'rejected'
1925 STATUS_UNDER_REVIEW = 'under_review'
1927 STATUS_UNDER_REVIEW = 'under_review'
1926
1928
1927 STATUSES = [
1929 STATUSES = [
1928 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1930 (STATUS_NOT_REVIEWED, _("Not Reviewed")), # (no icon) and default
1929 (STATUS_APPROVED, _("Approved")),
1931 (STATUS_APPROVED, _("Approved")),
1930 (STATUS_REJECTED, _("Rejected")),
1932 (STATUS_REJECTED, _("Rejected")),
1931 (STATUS_UNDER_REVIEW, _("Under Review")),
1933 (STATUS_UNDER_REVIEW, _("Under Review")),
1932 ]
1934 ]
1933
1935
1934 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1936 changeset_status_id = Column('changeset_status_id', Integer(), nullable=False, primary_key=True)
1935 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1937 repo_id = Column('repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1936 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1938 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1937 revision = Column('revision', String(40), nullable=False)
1939 revision = Column('revision', String(40), nullable=False)
1938 status = Column('status', String(128), nullable=False, default=DEFAULT)
1940 status = Column('status', String(128), nullable=False, default=DEFAULT)
1939 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1941 changeset_comment_id = Column('changeset_comment_id', Integer(), ForeignKey('changeset_comments.comment_id'))
1940 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1942 modified_at = Column('modified_at', DateTime(), nullable=False, default=datetime.datetime.now)
1941 version = Column('version', Integer(), nullable=False, default=0)
1943 version = Column('version', Integer(), nullable=False, default=0)
1942 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1944 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=True)
1943
1945
1944 author = relationship('User', lazy='joined')
1946 author = relationship('User', lazy='joined')
1945 repo = relationship('Repository')
1947 repo = relationship('Repository')
1946 comment = relationship('ChangesetComment', lazy='joined')
1948 comment = relationship('ChangesetComment', lazy='joined')
1947 pull_request = relationship('PullRequest', lazy='joined')
1949 pull_request = relationship('PullRequest', lazy='joined')
1948
1950
1949 def __unicode__(self):
1951 def __unicode__(self):
1950 return u"<%s('%s:%s')>" % (
1952 return u"<%s('%s:%s')>" % (
1951 self.__class__.__name__,
1953 self.__class__.__name__,
1952 self.status, self.author
1954 self.status, self.author
1953 )
1955 )
1954
1956
1955 @classmethod
1957 @classmethod
1956 def get_status_lbl(cls, value):
1958 def get_status_lbl(cls, value):
1957 return dict(cls.STATUSES).get(value)
1959 return dict(cls.STATUSES).get(value)
1958
1960
1959 @property
1961 @property
1960 def status_lbl(self):
1962 def status_lbl(self):
1961 return ChangesetStatus.get_status_lbl(self.status)
1963 return ChangesetStatus.get_status_lbl(self.status)
1962
1964
1963
1965
1964 class PullRequest(Base, BaseModel):
1966 class PullRequest(Base, BaseModel):
1965 __tablename__ = 'pull_requests'
1967 __tablename__ = 'pull_requests'
1966 __table_args__ = (
1968 __table_args__ = (
1967 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1969 {'extend_existing': True, 'mysql_engine': 'InnoDB',
1968 'mysql_charset': 'utf8'},
1970 'mysql_charset': 'utf8'},
1969 )
1971 )
1970
1972
1971 STATUS_NEW = u'new'
1973 STATUS_NEW = u'new'
1972 STATUS_OPEN = u'open'
1974 STATUS_OPEN = u'open'
1973 STATUS_CLOSED = u'closed'
1975 STATUS_CLOSED = u'closed'
1974
1976
1975 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1977 pull_request_id = Column('pull_request_id', Integer(), nullable=False, primary_key=True)
1976 title = Column('title', Unicode(256), nullable=True)
1978 title = Column('title', Unicode(256), nullable=True)
1977 description = Column('description', UnicodeText(10240), nullable=True)
1979 description = Column('description', UnicodeText(10240), nullable=True)
1978 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1980 status = Column('status', Unicode(256), nullable=False, default=STATUS_NEW)
1979 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1981 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1980 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1982 updated_on = Column('updated_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
1981 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1983 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=False, unique=None)
1982 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1984 _revisions = Column('revisions', UnicodeText(20500)) # 500 revisions max
1983 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1985 org_repo_id = Column('org_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1984 org_ref = Column('org_ref', Unicode(256), nullable=False)
1986 org_ref = Column('org_ref', Unicode(256), nullable=False)
1985 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1987 other_repo_id = Column('other_repo_id', Integer(), ForeignKey('repositories.repo_id'), nullable=False)
1986 other_ref = Column('other_ref', Unicode(256), nullable=False)
1988 other_ref = Column('other_ref', Unicode(256), nullable=False)
1987
1989
1988 @hybrid_property
1990 @hybrid_property
1989 def revisions(self):
1991 def revisions(self):
1990 return self._revisions.split(':')
1992 return self._revisions.split(':')
1991
1993
1992 @revisions.setter
1994 @revisions.setter
1993 def revisions(self, val):
1995 def revisions(self, val):
1994 self._revisions = ':'.join(val)
1996 self._revisions = ':'.join(val)
1995
1997
1996 @property
1998 @property
1997 def org_ref_parts(self):
1999 def org_ref_parts(self):
1998 return self.org_ref.split(':')
2000 return self.org_ref.split(':')
1999
2001
2000 @property
2002 @property
2001 def other_ref_parts(self):
2003 def other_ref_parts(self):
2002 return self.other_ref.split(':')
2004 return self.other_ref.split(':')
2003
2005
2004 author = relationship('User', lazy='joined')
2006 author = relationship('User', lazy='joined')
2005 reviewers = relationship('PullRequestReviewers',
2007 reviewers = relationship('PullRequestReviewers',
2006 cascade="all, delete, delete-orphan")
2008 cascade="all, delete, delete-orphan")
2007 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2009 org_repo = relationship('Repository', primaryjoin='PullRequest.org_repo_id==Repository.repo_id')
2008 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2010 other_repo = relationship('Repository', primaryjoin='PullRequest.other_repo_id==Repository.repo_id')
2009 statuses = relationship('ChangesetStatus')
2011 statuses = relationship('ChangesetStatus')
2010 comments = relationship('ChangesetComment',
2012 comments = relationship('ChangesetComment',
2011 cascade="all, delete, delete-orphan")
2013 cascade="all, delete, delete-orphan")
2012
2014
2013 def is_closed(self):
2015 def is_closed(self):
2014 return self.status == self.STATUS_CLOSED
2016 return self.status == self.STATUS_CLOSED
2015
2017
2016 @property
2018 @property
2017 def last_review_status(self):
2019 def last_review_status(self):
2018 return self.statuses[-1].status if self.statuses else ''
2020 return self.statuses[-1].status if self.statuses else ''
2019
2021
2020 def __json__(self):
2022 def __json__(self):
2021 return dict(
2023 return dict(
2022 revisions=self.revisions
2024 revisions=self.revisions
2023 )
2025 )
2024
2026
2025
2027
2026 class PullRequestReviewers(Base, BaseModel):
2028 class PullRequestReviewers(Base, BaseModel):
2027 __tablename__ = 'pull_request_reviewers'
2029 __tablename__ = 'pull_request_reviewers'
2028 __table_args__ = (
2030 __table_args__ = (
2029 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2031 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2030 'mysql_charset': 'utf8'},
2032 'mysql_charset': 'utf8'},
2031 )
2033 )
2032
2034
2033 def __init__(self, user=None, pull_request=None):
2035 def __init__(self, user=None, pull_request=None):
2034 self.user = user
2036 self.user = user
2035 self.pull_request = pull_request
2037 self.pull_request = pull_request
2036
2038
2037 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2039 pull_requests_reviewers_id = Column('pull_requests_reviewers_id', Integer(), nullable=False, primary_key=True)
2038 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2040 pull_request_id = Column("pull_request_id", Integer(), ForeignKey('pull_requests.pull_request_id'), nullable=False)
2039 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2041 user_id = Column("user_id", Integer(), ForeignKey('users.user_id'), nullable=True)
2040
2042
2041 user = relationship('User')
2043 user = relationship('User')
2042 pull_request = relationship('PullRequest')
2044 pull_request = relationship('PullRequest')
2043
2045
2044
2046
2045 class Notification(Base, BaseModel):
2047 class Notification(Base, BaseModel):
2046 __tablename__ = 'notifications'
2048 __tablename__ = 'notifications'
2047 __table_args__ = (
2049 __table_args__ = (
2048 Index('notification_type_idx', 'type'),
2050 Index('notification_type_idx', 'type'),
2049 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2051 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2050 'mysql_charset': 'utf8'},
2052 'mysql_charset': 'utf8'},
2051 )
2053 )
2052
2054
2053 TYPE_CHANGESET_COMMENT = u'cs_comment'
2055 TYPE_CHANGESET_COMMENT = u'cs_comment'
2054 TYPE_MESSAGE = u'message'
2056 TYPE_MESSAGE = u'message'
2055 TYPE_MENTION = u'mention'
2057 TYPE_MENTION = u'mention'
2056 TYPE_REGISTRATION = u'registration'
2058 TYPE_REGISTRATION = u'registration'
2057 TYPE_PULL_REQUEST = u'pull_request'
2059 TYPE_PULL_REQUEST = u'pull_request'
2058 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2060 TYPE_PULL_REQUEST_COMMENT = u'pull_request_comment'
2059
2061
2060 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2062 notification_id = Column('notification_id', Integer(), nullable=False, primary_key=True)
2061 subject = Column('subject', Unicode(512), nullable=True)
2063 subject = Column('subject', Unicode(512), nullable=True)
2062 body = Column('body', UnicodeText(50000), nullable=True)
2064 body = Column('body', UnicodeText(50000), nullable=True)
2063 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2065 created_by = Column("created_by", Integer(), ForeignKey('users.user_id'), nullable=True)
2064 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2066 created_on = Column('created_on', DateTime(timezone=False), nullable=False, default=datetime.datetime.now)
2065 type_ = Column('type', Unicode(256))
2067 type_ = Column('type', Unicode(256))
2066
2068
2067 created_by_user = relationship('User')
2069 created_by_user = relationship('User')
2068 notifications_to_users = relationship('UserNotification', lazy='joined',
2070 notifications_to_users = relationship('UserNotification', lazy='joined',
2069 cascade="all, delete, delete-orphan")
2071 cascade="all, delete, delete-orphan")
2070
2072
2071 @property
2073 @property
2072 def recipients(self):
2074 def recipients(self):
2073 return [x.user for x in UserNotification.query()\
2075 return [x.user for x in UserNotification.query()\
2074 .filter(UserNotification.notification == self)\
2076 .filter(UserNotification.notification == self)\
2075 .order_by(UserNotification.user_id.asc()).all()]
2077 .order_by(UserNotification.user_id.asc()).all()]
2076
2078
2077 @classmethod
2079 @classmethod
2078 def create(cls, created_by, subject, body, recipients, type_=None):
2080 def create(cls, created_by, subject, body, recipients, type_=None):
2079 if type_ is None:
2081 if type_ is None:
2080 type_ = Notification.TYPE_MESSAGE
2082 type_ = Notification.TYPE_MESSAGE
2081
2083
2082 notification = cls()
2084 notification = cls()
2083 notification.created_by_user = created_by
2085 notification.created_by_user = created_by
2084 notification.subject = subject
2086 notification.subject = subject
2085 notification.body = body
2087 notification.body = body
2086 notification.type_ = type_
2088 notification.type_ = type_
2087 notification.created_on = datetime.datetime.now()
2089 notification.created_on = datetime.datetime.now()
2088
2090
2089 for u in recipients:
2091 for u in recipients:
2090 assoc = UserNotification()
2092 assoc = UserNotification()
2091 assoc.notification = notification
2093 assoc.notification = notification
2092 u.notifications.append(assoc)
2094 u.notifications.append(assoc)
2093 Session().add(notification)
2095 Session().add(notification)
2094 return notification
2096 return notification
2095
2097
2096 @property
2098 @property
2097 def description(self):
2099 def description(self):
2098 from rhodecode.model.notification import NotificationModel
2100 from rhodecode.model.notification import NotificationModel
2099 return NotificationModel().make_description(self)
2101 return NotificationModel().make_description(self)
2100
2102
2101
2103
2102 class UserNotification(Base, BaseModel):
2104 class UserNotification(Base, BaseModel):
2103 __tablename__ = 'user_to_notification'
2105 __tablename__ = 'user_to_notification'
2104 __table_args__ = (
2106 __table_args__ = (
2105 UniqueConstraint('user_id', 'notification_id'),
2107 UniqueConstraint('user_id', 'notification_id'),
2106 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2108 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2107 'mysql_charset': 'utf8'}
2109 'mysql_charset': 'utf8'}
2108 )
2110 )
2109 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2111 user_id = Column('user_id', Integer(), ForeignKey('users.user_id'), primary_key=True)
2110 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2112 notification_id = Column("notification_id", Integer(), ForeignKey('notifications.notification_id'), primary_key=True)
2111 read = Column('read', Boolean, default=False)
2113 read = Column('read', Boolean, default=False)
2112 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2114 sent_on = Column('sent_on', DateTime(timezone=False), nullable=True, unique=None)
2113
2115
2114 user = relationship('User', lazy="joined")
2116 user = relationship('User', lazy="joined")
2115 notification = relationship('Notification', lazy="joined",
2117 notification = relationship('Notification', lazy="joined",
2116 order_by=lambda: Notification.created_on.desc(),)
2118 order_by=lambda: Notification.created_on.desc(),)
2117
2119
2118 def mark_as_read(self):
2120 def mark_as_read(self):
2119 self.read = True
2121 self.read = True
2120 Session().add(self)
2122 Session().add(self)
2121
2123
2122
2124
2123 class DbMigrateVersion(Base, BaseModel):
2125 class DbMigrateVersion(Base, BaseModel):
2124 __tablename__ = 'db_migrate_version'
2126 __tablename__ = 'db_migrate_version'
2125 __table_args__ = (
2127 __table_args__ = (
2126 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2128 {'extend_existing': True, 'mysql_engine': 'InnoDB',
2127 'mysql_charset': 'utf8'},
2129 'mysql_charset': 'utf8'},
2128 )
2130 )
2129 repository_id = Column('repository_id', String(250), primary_key=True)
2131 repository_id = Column('repository_id', String(250), primary_key=True)
2130 repository_path = Column('repository_path', Text)
2132 repository_path = Column('repository_path', Text)
2131 version = Column('version', Integer)
2133 version = Column('version', Integer)
@@ -1,1312 +1,1347 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2 import random
2 import random
3 import mock
3 import mock
4
4
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.tests.fixture import Fixture
6 from rhodecode.tests.fixture import Fixture
7 from rhodecode.lib.compat import json
7 from rhodecode.lib.compat import json
8 from rhodecode.lib.auth import AuthUser
8 from rhodecode.lib.auth import AuthUser
9 from rhodecode.model.user import UserModel
9 from rhodecode.model.user import UserModel
10 from rhodecode.model.users_group import UserGroupModel
10 from rhodecode.model.users_group import UserGroupModel
11 from rhodecode.model.repo import RepoModel
11 from rhodecode.model.repo import RepoModel
12 from rhodecode.model.meta import Session
12 from rhodecode.model.meta import Session
13 from rhodecode.model.scm import ScmModel
13 from rhodecode.model.scm import ScmModel
14 from rhodecode.model.db import Repository, User
14 from rhodecode.model.db import Repository, User
15 from rhodecode.lib.utils2 import time_to_datetime
15
16
16
17
17 API_URL = '/_admin/api'
18 API_URL = '/_admin/api'
18 TEST_USER_GROUP = 'test_users_group'
19 TEST_USER_GROUP = 'test_users_group'
19
20
20 fixture = Fixture()
21 fixture = Fixture()
21
22
22
23
23 def _build_data(apikey, method, **kw):
24 def _build_data(apikey, method, **kw):
24 """
25 """
25 Builds API data with given random ID
26 Builds API data with given random ID
26
27
27 :param random_id:
28 :param random_id:
28 :type random_id:
29 :type random_id:
29 """
30 """
30 random_id = random.randrange(1, 9999)
31 random_id = random.randrange(1, 9999)
31 return random_id, json.dumps({
32 return random_id, json.dumps({
32 "id": random_id,
33 "id": random_id,
33 "api_key": apikey,
34 "api_key": apikey,
34 "method": method,
35 "method": method,
35 "args": kw
36 "args": kw
36 })
37 })
37
38
38 jsonify = lambda obj: json.loads(json.dumps(obj))
39 jsonify = lambda obj: json.loads(json.dumps(obj))
39
40
40
41
41 def crash(*args, **kwargs):
42 def crash(*args, **kwargs):
42 raise Exception('Total Crash !')
43 raise Exception('Total Crash !')
43
44
44
45
45 def api_call(test_obj, params):
46 def api_call(test_obj, params):
46 response = test_obj.app.post(API_URL, content_type='application/json',
47 response = test_obj.app.post(API_URL, content_type='application/json',
47 params=params)
48 params=params)
48 return response
49 return response
49
50
50
51
51 ## helpers
52 ## helpers
52 def make_users_group(name=TEST_USER_GROUP):
53 def make_users_group(name=TEST_USER_GROUP):
53 gr = fixture.create_user_group(name, cur_user=TEST_USER_ADMIN_LOGIN)
54 gr = fixture.create_user_group(name, cur_user=TEST_USER_ADMIN_LOGIN)
54 UserGroupModel().add_user_to_group(users_group=gr,
55 UserGroupModel().add_user_to_group(users_group=gr,
55 user=TEST_USER_ADMIN_LOGIN)
56 user=TEST_USER_ADMIN_LOGIN)
56 Session().commit()
57 Session().commit()
57 return gr
58 return gr
58
59
59
60
60 def destroy_users_group(name=TEST_USER_GROUP):
61 def destroy_users_group(name=TEST_USER_GROUP):
61 UserGroupModel().delete(users_group=name, force=True)
62 UserGroupModel().delete(users_group=name, force=True)
62 Session().commit()
63 Session().commit()
63
64
64
65
65 class BaseTestApi(object):
66 class BaseTestApi(object):
66 REPO = None
67 REPO = None
67 REPO_TYPE = None
68 REPO_TYPE = None
68
69
69 @classmethod
70 @classmethod
70 def setUpClass(self):
71 def setUpClass(self):
71 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
72 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
72 self.apikey = self.usr.api_key
73 self.apikey = self.usr.api_key
73 self.test_user = UserModel().create_or_update(
74 self.test_user = UserModel().create_or_update(
74 username='test-api',
75 username='test-api',
75 password='test',
76 password='test',
76 email='test@api.rhodecode.org',
77 email='test@api.rhodecode.org',
77 firstname='first',
78 firstname='first',
78 lastname='last'
79 lastname='last'
79 )
80 )
80 Session().commit()
81 Session().commit()
81 self.TEST_USER_LOGIN = self.test_user.username
82 self.TEST_USER_LOGIN = self.test_user.username
82 self.apikey_regular = self.test_user.api_key
83 self.apikey_regular = self.test_user.api_key
83
84
84 @classmethod
85 @classmethod
85 def teardownClass(self):
86 def teardownClass(self):
86 pass
87 pass
87
88
88 def setUp(self):
89 def setUp(self):
89 self.maxDiff = None
90 self.maxDiff = None
90 make_users_group()
91 make_users_group()
91
92
92 def tearDown(self):
93 def tearDown(self):
93 destroy_users_group()
94 destroy_users_group()
94
95
95 def _compare_ok(self, id_, expected, given):
96 def _compare_ok(self, id_, expected, given):
96 expected = jsonify({
97 expected = jsonify({
97 'id': id_,
98 'id': id_,
98 'error': None,
99 'error': None,
99 'result': expected
100 'result': expected
100 })
101 })
101 given = json.loads(given)
102 given = json.loads(given)
102 self.assertEqual(expected, given)
103 self.assertEqual(expected, given)
103
104
104 def _compare_error(self, id_, expected, given):
105 def _compare_error(self, id_, expected, given):
105 expected = jsonify({
106 expected = jsonify({
106 'id': id_,
107 'id': id_,
107 'error': expected,
108 'error': expected,
108 'result': None
109 'result': None
109 })
110 })
110 given = json.loads(given)
111 given = json.loads(given)
111 self.assertEqual(expected, given)
112 self.assertEqual(expected, given)
112
113
113 # def test_Optional(self):
114 # def test_Optional(self):
114 # from rhodecode.controllers.api.api import Optional
115 # from rhodecode.controllers.api.api import Optional
115 # option1 = Optional(None)
116 # option1 = Optional(None)
116 # self.assertEqual('<Optional:%s>' % None, repr(option1))
117 # self.assertEqual('<Optional:%s>' % None, repr(option1))
117 #
118 #
118 # self.assertEqual(1, Optional.extract(Optional(1)))
119 # self.assertEqual(1, Optional.extract(Optional(1)))
119 # self.assertEqual('trololo', Optional.extract('trololo'))
120 # self.assertEqual('trololo', Optional.extract('trololo'))
120
121
121 def test_api_wrong_key(self):
122 def test_api_wrong_key(self):
122 id_, params = _build_data('trololo', 'get_user')
123 id_, params = _build_data('trololo', 'get_user')
123 response = api_call(self, params)
124 response = api_call(self, params)
124
125
125 expected = 'Invalid API KEY'
126 expected = 'Invalid API KEY'
126 self._compare_error(id_, expected, given=response.body)
127 self._compare_error(id_, expected, given=response.body)
127
128
128 def test_api_missing_non_optional_param(self):
129 def test_api_missing_non_optional_param(self):
129 id_, params = _build_data(self.apikey, 'get_repo')
130 id_, params = _build_data(self.apikey, 'get_repo')
130 response = api_call(self, params)
131 response = api_call(self, params)
131
132
132 expected = 'Missing non optional `repoid` arg in JSON DATA'
133 expected = 'Missing non optional `repoid` arg in JSON DATA'
133 self._compare_error(id_, expected, given=response.body)
134 self._compare_error(id_, expected, given=response.body)
134
135
135 def test_api_missing_non_optional_param_args_null(self):
136 def test_api_missing_non_optional_param_args_null(self):
136 id_, params = _build_data(self.apikey, 'get_repo')
137 id_, params = _build_data(self.apikey, 'get_repo')
137 params = params.replace('"args": {}', '"args": null')
138 params = params.replace('"args": {}', '"args": null')
138 response = api_call(self, params)
139 response = api_call(self, params)
139
140
140 expected = 'Missing non optional `repoid` arg in JSON DATA'
141 expected = 'Missing non optional `repoid` arg in JSON DATA'
141 self._compare_error(id_, expected, given=response.body)
142 self._compare_error(id_, expected, given=response.body)
142
143
143 def test_api_missing_non_optional_param_args_bad(self):
144 def test_api_missing_non_optional_param_args_bad(self):
144 id_, params = _build_data(self.apikey, 'get_repo')
145 id_, params = _build_data(self.apikey, 'get_repo')
145 params = params.replace('"args": {}', '"args": 1')
146 params = params.replace('"args": {}', '"args": 1')
146 response = api_call(self, params)
147 response = api_call(self, params)
147
148
148 expected = 'Missing non optional `repoid` arg in JSON DATA'
149 expected = 'Missing non optional `repoid` arg in JSON DATA'
149 self._compare_error(id_, expected, given=response.body)
150 self._compare_error(id_, expected, given=response.body)
150
151
151 def test_api_args_is_null(self):
152 def test_api_args_is_null(self):
152 id_, params = _build_data(self.apikey, 'get_users',)
153 id_, params = _build_data(self.apikey, 'get_users',)
153 params = params.replace('"args": {}', '"args": null')
154 params = params.replace('"args": {}', '"args": null')
154 response = api_call(self, params)
155 response = api_call(self, params)
155 self.assertEqual(response.status, '200 OK')
156 self.assertEqual(response.status, '200 OK')
156
157
157 def test_api_args_is_bad(self):
158 def test_api_args_is_bad(self):
158 id_, params = _build_data(self.apikey, 'get_users',)
159 id_, params = _build_data(self.apikey, 'get_users',)
159 params = params.replace('"args": {}', '"args": 1')
160 params = params.replace('"args": {}', '"args": 1')
160 response = api_call(self, params)
161 response = api_call(self, params)
161 self.assertEqual(response.status, '200 OK')
162 self.assertEqual(response.status, '200 OK')
162
163
163 def test_api_get_users(self):
164 def test_api_get_users(self):
164 id_, params = _build_data(self.apikey, 'get_users',)
165 id_, params = _build_data(self.apikey, 'get_users',)
165 response = api_call(self, params)
166 response = api_call(self, params)
166 ret_all = []
167 ret_all = []
167 _users = User.query().filter(User.username != User.DEFAULT_USER)\
168 _users = User.query().filter(User.username != User.DEFAULT_USER)\
168 .order_by(User.username).all()
169 .order_by(User.username).all()
169 for usr in _users:
170 for usr in _users:
170 ret = usr.get_api_data()
171 ret = usr.get_api_data()
171 ret_all.append(jsonify(ret))
172 ret_all.append(jsonify(ret))
172 expected = ret_all
173 expected = ret_all
173 self._compare_ok(id_, expected, given=response.body)
174 self._compare_ok(id_, expected, given=response.body)
174
175
175 def test_api_get_user(self):
176 def test_api_get_user(self):
176 id_, params = _build_data(self.apikey, 'get_user',
177 id_, params = _build_data(self.apikey, 'get_user',
177 userid=TEST_USER_ADMIN_LOGIN)
178 userid=TEST_USER_ADMIN_LOGIN)
178 response = api_call(self, params)
179 response = api_call(self, params)
179
180
180 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
181 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
181 ret = usr.get_api_data()
182 ret = usr.get_api_data()
182 ret['permissions'] = AuthUser(usr.user_id).permissions
183 ret['permissions'] = AuthUser(usr.user_id).permissions
183
184
184 expected = ret
185 expected = ret
185 self._compare_ok(id_, expected, given=response.body)
186 self._compare_ok(id_, expected, given=response.body)
186
187
187 def test_api_get_user_that_does_not_exist(self):
188 def test_api_get_user_that_does_not_exist(self):
188 id_, params = _build_data(self.apikey, 'get_user',
189 id_, params = _build_data(self.apikey, 'get_user',
189 userid='trololo')
190 userid='trololo')
190 response = api_call(self, params)
191 response = api_call(self, params)
191
192
192 expected = "user `%s` does not exist" % 'trololo'
193 expected = "user `%s` does not exist" % 'trololo'
193 self._compare_error(id_, expected, given=response.body)
194 self._compare_error(id_, expected, given=response.body)
194
195
195 def test_api_get_user_without_giving_userid(self):
196 def test_api_get_user_without_giving_userid(self):
196 id_, params = _build_data(self.apikey, 'get_user')
197 id_, params = _build_data(self.apikey, 'get_user')
197 response = api_call(self, params)
198 response = api_call(self, params)
198
199
199 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
200 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
200 ret = usr.get_api_data()
201 ret = usr.get_api_data()
201 ret['permissions'] = AuthUser(usr.user_id).permissions
202 ret['permissions'] = AuthUser(usr.user_id).permissions
202
203
203 expected = ret
204 expected = ret
204 self._compare_ok(id_, expected, given=response.body)
205 self._compare_ok(id_, expected, given=response.body)
205
206
206 def test_api_get_user_without_giving_userid_non_admin(self):
207 def test_api_get_user_without_giving_userid_non_admin(self):
207 id_, params = _build_data(self.apikey_regular, 'get_user')
208 id_, params = _build_data(self.apikey_regular, 'get_user')
208 response = api_call(self, params)
209 response = api_call(self, params)
209
210
210 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
211 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
211 ret = usr.get_api_data()
212 ret = usr.get_api_data()
212 ret['permissions'] = AuthUser(usr.user_id).permissions
213 ret['permissions'] = AuthUser(usr.user_id).permissions
213
214
214 expected = ret
215 expected = ret
215 self._compare_ok(id_, expected, given=response.body)
216 self._compare_ok(id_, expected, given=response.body)
216
217
217 def test_api_get_user_with_giving_userid_non_admin(self):
218 def test_api_get_user_with_giving_userid_non_admin(self):
218 id_, params = _build_data(self.apikey_regular, 'get_user',
219 id_, params = _build_data(self.apikey_regular, 'get_user',
219 userid=self.TEST_USER_LOGIN)
220 userid=self.TEST_USER_LOGIN)
220 response = api_call(self, params)
221 response = api_call(self, params)
221
222
222 expected = 'userid is not the same as your user'
223 expected = 'userid is not the same as your user'
223 self._compare_error(id_, expected, given=response.body)
224 self._compare_error(id_, expected, given=response.body)
224
225
225 def test_api_pull(self):
226 def test_api_pull(self):
226 #TODO: issues with rhodecode_extras here.. not sure why !
227 #TODO: issues with rhodecode_extras here.. not sure why !
227 pass
228 pass
228
229
229 # repo_name = 'test_pull'
230 # repo_name = 'test_pull'
230 # r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
231 # r = fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
231 # r.clone_uri = TEST_self.REPO
232 # r.clone_uri = TEST_self.REPO
232 # Session.add(r)
233 # Session.add(r)
233 # Session.commit()
234 # Session.commit()
234 #
235 #
235 # id_, params = _build_data(self.apikey, 'pull',
236 # id_, params = _build_data(self.apikey, 'pull',
236 # repoid=repo_name,)
237 # repoid=repo_name,)
237 # response = self.app.post(API_URL, content_type='application/json',
238 # response = self.app.post(API_URL, content_type='application/json',
238 # params=params)
239 # params=params)
239 #
240 #
240 # expected = 'Pulled from `%s`' % repo_name
241 # expected = 'Pulled from `%s`' % repo_name
241 # self._compare_ok(id_, expected, given=response.body)
242 # self._compare_ok(id_, expected, given=response.body)
242 #
243 #
243 # fixture.destroy_repo(repo_name)
244 # fixture.destroy_repo(repo_name)
244
245
245 def test_api_pull_error(self):
246 def test_api_pull_error(self):
246 id_, params = _build_data(self.apikey, 'pull',
247 id_, params = _build_data(self.apikey, 'pull',
247 repoid=self.REPO,)
248 repoid=self.REPO,)
248 response = api_call(self, params)
249 response = api_call(self, params)
249
250
250 expected = 'Unable to pull changes from `%s`' % self.REPO
251 expected = 'Unable to pull changes from `%s`' % self.REPO
251 self._compare_error(id_, expected, given=response.body)
252 self._compare_error(id_, expected, given=response.body)
252
253
253 def test_api_rescan_repos(self):
254 def test_api_rescan_repos(self):
254 id_, params = _build_data(self.apikey, 'rescan_repos')
255 id_, params = _build_data(self.apikey, 'rescan_repos')
255 response = api_call(self, params)
256 response = api_call(self, params)
256
257
257 expected = {'added': [], 'removed': []}
258 expected = {'added': [], 'removed': []}
258 self._compare_ok(id_, expected, given=response.body)
259 self._compare_ok(id_, expected, given=response.body)
259
260
260 @mock.patch.object(ScmModel, 'repo_scan', crash)
261 @mock.patch.object(ScmModel, 'repo_scan', crash)
261 def test_api_rescann_error(self):
262 def test_api_rescann_error(self):
262 id_, params = _build_data(self.apikey, 'rescan_repos',)
263 id_, params = _build_data(self.apikey, 'rescan_repos',)
263 response = api_call(self, params)
264 response = api_call(self, params)
264
265
265 expected = 'Error occurred during rescan repositories action'
266 expected = 'Error occurred during rescan repositories action'
266 self._compare_error(id_, expected, given=response.body)
267 self._compare_error(id_, expected, given=response.body)
267
268
268 def test_api_invalidate_cache(self):
269 def test_api_invalidate_cache(self):
269 repo = RepoModel().get_by_repo_name(self.REPO)
270 repo = RepoModel().get_by_repo_name(self.REPO)
270 repo.scm_instance_cached() # seed cache
271 repo.scm_instance_cached() # seed cache
271
272
272 id_, params = _build_data(self.apikey, 'invalidate_cache',
273 id_, params = _build_data(self.apikey, 'invalidate_cache',
273 repoid=self.REPO)
274 repoid=self.REPO)
274 response = api_call(self, params)
275 response = api_call(self, params)
275
276
276 expected = ("Caches of repository `%s` was invalidated" % (self.REPO))
277 expected = ("Caches of repository `%s` was invalidated" % (self.REPO))
277 self._compare_ok(id_, expected, given=response.body)
278 self._compare_ok(id_, expected, given=response.body)
278
279
279 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
280 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
280 def test_api_invalidate_cache_error(self):
281 def test_api_invalidate_cache_error(self):
281 id_, params = _build_data(self.apikey, 'invalidate_cache',
282 id_, params = _build_data(self.apikey, 'invalidate_cache',
282 repoid=self.REPO)
283 repoid=self.REPO)
283 response = api_call(self, params)
284 response = api_call(self, params)
284
285
285 expected = 'Error occurred during cache invalidation action'
286 expected = 'Error occurred during cache invalidation action'
286 self._compare_error(id_, expected, given=response.body)
287 self._compare_error(id_, expected, given=response.body)
287
288
288 def test_api_lock_repo_lock_aquire(self):
289 def test_api_lock_repo_lock_aquire(self):
289 id_, params = _build_data(self.apikey, 'lock',
290 id_, params = _build_data(self.apikey, 'lock',
290 userid=TEST_USER_ADMIN_LOGIN,
291 userid=TEST_USER_ADMIN_LOGIN,
291 repoid=self.REPO,
292 repoid=self.REPO,
292 locked=True)
293 locked=True)
293 response = api_call(self, params)
294 response = api_call(self, params)
294 expected = ('User `%s` set lock state for repo `%s` to `%s`'
295 expected = {
295 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
296 'repo': self.REPO,
297 'locked': True,
298 'locked_since': None,
299 'locked_by': TEST_USER_ADMIN_LOGIN,
300 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
301 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
302 }
303 expected['locked_since'] = json.loads(response.body)['result']['locked_since']
296 self._compare_ok(id_, expected, given=response.body)
304 self._compare_ok(id_, expected, given=response.body)
297
305
298 def test_api_lock_repo_lock_aquire_by_non_admin(self):
306 def test_api_lock_repo_lock_aquire_by_non_admin(self):
299 repo_name = 'api_delete_me'
307 repo_name = 'api_delete_me'
300 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
308 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
301 cur_user=self.TEST_USER_LOGIN)
309 cur_user=self.TEST_USER_LOGIN)
302 try:
310 try:
303 id_, params = _build_data(self.apikey_regular, 'lock',
311 id_, params = _build_data(self.apikey_regular, 'lock',
304 repoid=repo_name,
312 repoid=repo_name,
305 locked=True)
313 locked=True)
306 response = api_call(self, params)
314 response = api_call(self, params)
307 expected = ('User `%s` set lock state for repo `%s` to `%s`'
315 expected = {
308 % (self.TEST_USER_LOGIN, repo_name, True))
316 'repo': repo_name,
317 'locked': True,
318 'locked_since': None,
319 'locked_by': self.TEST_USER_LOGIN,
320 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
321 % (self.TEST_USER_LOGIN, repo_name, True))
322 }
323 expected['locked_since'] = json.loads(response.body)['result']['locked_since']
309 self._compare_ok(id_, expected, given=response.body)
324 self._compare_ok(id_, expected, given=response.body)
310 finally:
325 finally:
311 fixture.destroy_repo(repo_name)
326 fixture.destroy_repo(repo_name)
312
327
313 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
328 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
314 repo_name = 'api_delete_me'
329 repo_name = 'api_delete_me'
315 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
330 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
316 cur_user=self.TEST_USER_LOGIN)
331 cur_user=self.TEST_USER_LOGIN)
317 try:
332 try:
318 id_, params = _build_data(self.apikey_regular, 'lock',
333 id_, params = _build_data(self.apikey_regular, 'lock',
319 userid=TEST_USER_ADMIN_LOGIN,
334 userid=TEST_USER_ADMIN_LOGIN,
320 repoid=repo_name,
335 repoid=repo_name,
321 locked=True)
336 locked=True)
322 response = api_call(self, params)
337 response = api_call(self, params)
323 expected = 'userid is not the same as your user'
338 expected = 'userid is not the same as your user'
324 self._compare_error(id_, expected, given=response.body)
339 self._compare_error(id_, expected, given=response.body)
325 finally:
340 finally:
326 fixture.destroy_repo(repo_name)
341 fixture.destroy_repo(repo_name)
327
342
328 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
343 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
329 id_, params = _build_data(self.apikey_regular, 'lock',
344 id_, params = _build_data(self.apikey_regular, 'lock',
330 repoid=self.REPO,
345 repoid=self.REPO,
331 locked=True)
346 locked=True)
332 response = api_call(self, params)
347 response = api_call(self, params)
333 expected = 'repository `%s` does not exist' % (self.REPO)
348 expected = 'repository `%s` does not exist' % (self.REPO)
334 self._compare_error(id_, expected, given=response.body)
349 self._compare_error(id_, expected, given=response.body)
335
350
336 def test_api_lock_repo_lock_release(self):
351 def test_api_lock_repo_lock_release(self):
337 id_, params = _build_data(self.apikey, 'lock',
352 id_, params = _build_data(self.apikey, 'lock',
338 userid=TEST_USER_ADMIN_LOGIN,
353 userid=TEST_USER_ADMIN_LOGIN,
339 repoid=self.REPO,
354 repoid=self.REPO,
340 locked=False)
355 locked=False)
341 response = api_call(self, params)
356 response = api_call(self, params)
342 expected = ('User `%s` set lock state for repo `%s` to `%s`'
357 expected = {
343 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
358 'repo': self.REPO,
359 'locked': False,
360 'locked_since': None,
361 'locked_by': TEST_USER_ADMIN_LOGIN,
362 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
363 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
364 }
344 self._compare_ok(id_, expected, given=response.body)
365 self._compare_ok(id_, expected, given=response.body)
345
366
346 def test_api_lock_repo_lock_aquire_optional_userid(self):
367 def test_api_lock_repo_lock_aquire_optional_userid(self):
347 id_, params = _build_data(self.apikey, 'lock',
368 id_, params = _build_data(self.apikey, 'lock',
348 repoid=self.REPO,
369 repoid=self.REPO,
349 locked=True)
370 locked=True)
350 response = api_call(self, params)
371 response = api_call(self, params)
351 expected = ('User `%s` set lock state for repo `%s` to `%s`'
372 expected = {
352 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
373 'repo': self.REPO,
374 'locked': True,
375 'locked_since': None,
376 'locked_by': TEST_USER_ADMIN_LOGIN,
377 'msg': ('User `%s` set lock state for repo `%s` to `%s`'
378 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
379 }
380 expected['locked_since'] = json.loads(response.body)['result']['locked_since']
353 self._compare_ok(id_, expected, given=response.body)
381 self._compare_ok(id_, expected, given=response.body)
354
382
355 def test_api_lock_repo_lock_optional_locked(self):
383 def test_api_lock_repo_lock_optional_locked(self):
356 from rhodecode.lib.utils2 import time_to_datetime
357 _locked_since = json.dumps(time_to_datetime(Repository\
358 .get_by_repo_name(self.REPO).locked[1]))
359 id_, params = _build_data(self.apikey, 'lock',
384 id_, params = _build_data(self.apikey, 'lock',
360 repoid=self.REPO)
385 repoid=self.REPO)
361 response = api_call(self, params)
386 response = api_call(self, params)
362 expected = ('Repo `%s` locked by `%s`. Locked=`True`. Locked since: `%s`'
387 time_ = json.loads(response.body)['result']['locked_since']
363 % (self.REPO, TEST_USER_ADMIN_LOGIN, _locked_since))
388 expected = {
389 'repo': self.REPO,
390 'locked': True,
391 'locked_since': None,
392 'locked_by': TEST_USER_ADMIN_LOGIN,
393 'msg': ('Repo `%s` locked by `%s`. '
394 % (self.REPO,
395 json.dumps(time_to_datetime(time_))))
396
397 }
398 expected['locked_since'] = time_
364 self._compare_ok(id_, expected, given=response.body)
399 self._compare_ok(id_, expected, given=response.body)
365
400
366 @mock.patch.object(Repository, 'lock', crash)
401 @mock.patch.object(Repository, 'lock', crash)
367 def test_api_lock_error(self):
402 def test_api_lock_error(self):
368 id_, params = _build_data(self.apikey, 'lock',
403 id_, params = _build_data(self.apikey, 'lock',
369 userid=TEST_USER_ADMIN_LOGIN,
404 userid=TEST_USER_ADMIN_LOGIN,
370 repoid=self.REPO,
405 repoid=self.REPO,
371 locked=True)
406 locked=True)
372 response = api_call(self, params)
407 response = api_call(self, params)
373
408
374 expected = 'Error occurred locking repository `%s`' % self.REPO
409 expected = 'Error occurred locking repository `%s`' % self.REPO
375 self._compare_error(id_, expected, given=response.body)
410 self._compare_error(id_, expected, given=response.body)
376
411
377 def test_api_get_locks_regular_user(self):
412 def test_api_get_locks_regular_user(self):
378 id_, params = _build_data(self.apikey_regular, 'get_locks')
413 id_, params = _build_data(self.apikey_regular, 'get_locks')
379 response = api_call(self, params)
414 response = api_call(self, params)
380 expected = []
415 expected = []
381 self._compare_ok(id_, expected, given=response.body)
416 self._compare_ok(id_, expected, given=response.body)
382
417
383 def test_api_get_locks_with_userid_regular_user(self):
418 def test_api_get_locks_with_userid_regular_user(self):
384 id_, params = _build_data(self.apikey_regular, 'get_locks',
419 id_, params = _build_data(self.apikey_regular, 'get_locks',
385 userid=TEST_USER_ADMIN_LOGIN)
420 userid=TEST_USER_ADMIN_LOGIN)
386 response = api_call(self, params)
421 response = api_call(self, params)
387 expected = 'userid is not the same as your user'
422 expected = 'userid is not the same as your user'
388 self._compare_error(id_, expected, given=response.body)
423 self._compare_error(id_, expected, given=response.body)
389
424
390 def test_api_get_locks(self):
425 def test_api_get_locks(self):
391 id_, params = _build_data(self.apikey, 'get_locks')
426 id_, params = _build_data(self.apikey, 'get_locks')
392 response = api_call(self, params)
427 response = api_call(self, params)
393 expected = []
428 expected = []
394 self._compare_ok(id_, expected, given=response.body)
429 self._compare_ok(id_, expected, given=response.body)
395
430
396 def test_api_get_locks_with_userid(self):
431 def test_api_get_locks_with_userid(self):
397 id_, params = _build_data(self.apikey, 'get_locks',
432 id_, params = _build_data(self.apikey, 'get_locks',
398 userid=TEST_USER_REGULAR_LOGIN)
433 userid=TEST_USER_REGULAR_LOGIN)
399 response = api_call(self, params)
434 response = api_call(self, params)
400 expected = []
435 expected = []
401 self._compare_ok(id_, expected, given=response.body)
436 self._compare_ok(id_, expected, given=response.body)
402
437
403 def test_api_create_existing_user(self):
438 def test_api_create_existing_user(self):
404 id_, params = _build_data(self.apikey, 'create_user',
439 id_, params = _build_data(self.apikey, 'create_user',
405 username=TEST_USER_ADMIN_LOGIN,
440 username=TEST_USER_ADMIN_LOGIN,
406 email='test@foo.com',
441 email='test@foo.com',
407 password='trololo')
442 password='trololo')
408 response = api_call(self, params)
443 response = api_call(self, params)
409
444
410 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
445 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
411 self._compare_error(id_, expected, given=response.body)
446 self._compare_error(id_, expected, given=response.body)
412
447
413 def test_api_create_user_with_existing_email(self):
448 def test_api_create_user_with_existing_email(self):
414 id_, params = _build_data(self.apikey, 'create_user',
449 id_, params = _build_data(self.apikey, 'create_user',
415 username=TEST_USER_ADMIN_LOGIN + 'new',
450 username=TEST_USER_ADMIN_LOGIN + 'new',
416 email=TEST_USER_REGULAR_EMAIL,
451 email=TEST_USER_REGULAR_EMAIL,
417 password='trololo')
452 password='trololo')
418 response = api_call(self, params)
453 response = api_call(self, params)
419
454
420 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
455 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
421 self._compare_error(id_, expected, given=response.body)
456 self._compare_error(id_, expected, given=response.body)
422
457
423 def test_api_create_user(self):
458 def test_api_create_user(self):
424 username = 'test_new_api_user'
459 username = 'test_new_api_user'
425 email = username + "@foo.com"
460 email = username + "@foo.com"
426
461
427 id_, params = _build_data(self.apikey, 'create_user',
462 id_, params = _build_data(self.apikey, 'create_user',
428 username=username,
463 username=username,
429 email=email,
464 email=email,
430 password='trololo')
465 password='trololo')
431 response = api_call(self, params)
466 response = api_call(self, params)
432
467
433 usr = UserModel().get_by_username(username)
468 usr = UserModel().get_by_username(username)
434 ret = dict(
469 ret = dict(
435 msg='created new user `%s`' % username,
470 msg='created new user `%s`' % username,
436 user=jsonify(usr.get_api_data())
471 user=jsonify(usr.get_api_data())
437 )
472 )
438
473
439 expected = ret
474 expected = ret
440 self._compare_ok(id_, expected, given=response.body)
475 self._compare_ok(id_, expected, given=response.body)
441
476
442 UserModel().delete(usr.user_id)
477 UserModel().delete(usr.user_id)
443 Session().commit()
478 Session().commit()
444
479
445 @mock.patch.object(UserModel, 'create_or_update', crash)
480 @mock.patch.object(UserModel, 'create_or_update', crash)
446 def test_api_create_user_when_exception_happened(self):
481 def test_api_create_user_when_exception_happened(self):
447
482
448 username = 'test_new_api_user'
483 username = 'test_new_api_user'
449 email = username + "@foo.com"
484 email = username + "@foo.com"
450
485
451 id_, params = _build_data(self.apikey, 'create_user',
486 id_, params = _build_data(self.apikey, 'create_user',
452 username=username,
487 username=username,
453 email=email,
488 email=email,
454 password='trololo')
489 password='trololo')
455 response = api_call(self, params)
490 response = api_call(self, params)
456 expected = 'failed to create user `%s`' % username
491 expected = 'failed to create user `%s`' % username
457 self._compare_error(id_, expected, given=response.body)
492 self._compare_error(id_, expected, given=response.body)
458
493
459 def test_api_delete_user(self):
494 def test_api_delete_user(self):
460 usr = UserModel().create_or_update(username=u'test_user',
495 usr = UserModel().create_or_update(username=u'test_user',
461 password=u'qweqwe',
496 password=u'qweqwe',
462 email=u'u232@rhodecode.org',
497 email=u'u232@rhodecode.org',
463 firstname=u'u1', lastname=u'u1')
498 firstname=u'u1', lastname=u'u1')
464 Session().commit()
499 Session().commit()
465 username = usr.username
500 username = usr.username
466 email = usr.email
501 email = usr.email
467 usr_id = usr.user_id
502 usr_id = usr.user_id
468 ## DELETE THIS USER NOW
503 ## DELETE THIS USER NOW
469
504
470 id_, params = _build_data(self.apikey, 'delete_user',
505 id_, params = _build_data(self.apikey, 'delete_user',
471 userid=username,)
506 userid=username,)
472 response = api_call(self, params)
507 response = api_call(self, params)
473
508
474 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
509 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
475 'user': None}
510 'user': None}
476 expected = ret
511 expected = ret
477 self._compare_ok(id_, expected, given=response.body)
512 self._compare_ok(id_, expected, given=response.body)
478
513
479 @mock.patch.object(UserModel, 'delete', crash)
514 @mock.patch.object(UserModel, 'delete', crash)
480 def test_api_delete_user_when_exception_happened(self):
515 def test_api_delete_user_when_exception_happened(self):
481 usr = UserModel().create_or_update(username=u'test_user',
516 usr = UserModel().create_or_update(username=u'test_user',
482 password=u'qweqwe',
517 password=u'qweqwe',
483 email=u'u232@rhodecode.org',
518 email=u'u232@rhodecode.org',
484 firstname=u'u1', lastname=u'u1')
519 firstname=u'u1', lastname=u'u1')
485 Session().commit()
520 Session().commit()
486 username = usr.username
521 username = usr.username
487
522
488 id_, params = _build_data(self.apikey, 'delete_user',
523 id_, params = _build_data(self.apikey, 'delete_user',
489 userid=username,)
524 userid=username,)
490 response = api_call(self, params)
525 response = api_call(self, params)
491 ret = 'failed to delete ID:%s %s' % (usr.user_id,
526 ret = 'failed to delete ID:%s %s' % (usr.user_id,
492 usr.username)
527 usr.username)
493 expected = ret
528 expected = ret
494 self._compare_error(id_, expected, given=response.body)
529 self._compare_error(id_, expected, given=response.body)
495
530
496 @parameterized.expand([('firstname', 'new_username'),
531 @parameterized.expand([('firstname', 'new_username'),
497 ('lastname', 'new_username'),
532 ('lastname', 'new_username'),
498 ('email', 'new_username'),
533 ('email', 'new_username'),
499 ('admin', True),
534 ('admin', True),
500 ('admin', False),
535 ('admin', False),
501 ('ldap_dn', 'test'),
536 ('ldap_dn', 'test'),
502 ('ldap_dn', None),
537 ('ldap_dn', None),
503 ('active', False),
538 ('active', False),
504 ('active', True),
539 ('active', True),
505 ('password', 'newpass')
540 ('password', 'newpass')
506 ])
541 ])
507 def test_api_update_user(self, name, expected):
542 def test_api_update_user(self, name, expected):
508 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
543 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
509 kw = {name: expected,
544 kw = {name: expected,
510 'userid': usr.user_id}
545 'userid': usr.user_id}
511 id_, params = _build_data(self.apikey, 'update_user', **kw)
546 id_, params = _build_data(self.apikey, 'update_user', **kw)
512 response = api_call(self, params)
547 response = api_call(self, params)
513
548
514 ret = {
549 ret = {
515 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
550 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
516 'user': jsonify(UserModel()\
551 'user': jsonify(UserModel()\
517 .get_by_username(self.TEST_USER_LOGIN)\
552 .get_by_username(self.TEST_USER_LOGIN)\
518 .get_api_data())
553 .get_api_data())
519 }
554 }
520
555
521 expected = ret
556 expected = ret
522 self._compare_ok(id_, expected, given=response.body)
557 self._compare_ok(id_, expected, given=response.body)
523
558
524 def test_api_update_user_no_changed_params(self):
559 def test_api_update_user_no_changed_params(self):
525 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
560 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
526 ret = jsonify(usr.get_api_data())
561 ret = jsonify(usr.get_api_data())
527 id_, params = _build_data(self.apikey, 'update_user',
562 id_, params = _build_data(self.apikey, 'update_user',
528 userid=TEST_USER_ADMIN_LOGIN)
563 userid=TEST_USER_ADMIN_LOGIN)
529
564
530 response = api_call(self, params)
565 response = api_call(self, params)
531 ret = {
566 ret = {
532 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
567 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
533 'user': ret
568 'user': ret
534 }
569 }
535 expected = ret
570 expected = ret
536 self._compare_ok(id_, expected, given=response.body)
571 self._compare_ok(id_, expected, given=response.body)
537
572
538 def test_api_update_user_by_user_id(self):
573 def test_api_update_user_by_user_id(self):
539 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
574 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
540 ret = jsonify(usr.get_api_data())
575 ret = jsonify(usr.get_api_data())
541 id_, params = _build_data(self.apikey, 'update_user',
576 id_, params = _build_data(self.apikey, 'update_user',
542 userid=usr.user_id)
577 userid=usr.user_id)
543
578
544 response = api_call(self, params)
579 response = api_call(self, params)
545 ret = {
580 ret = {
546 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
581 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
547 'user': ret
582 'user': ret
548 }
583 }
549 expected = ret
584 expected = ret
550 self._compare_ok(id_, expected, given=response.body)
585 self._compare_ok(id_, expected, given=response.body)
551
586
552 @mock.patch.object(UserModel, 'update_user', crash)
587 @mock.patch.object(UserModel, 'update_user', crash)
553 def test_api_update_user_when_exception_happens(self):
588 def test_api_update_user_when_exception_happens(self):
554 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
589 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
555 ret = jsonify(usr.get_api_data())
590 ret = jsonify(usr.get_api_data())
556 id_, params = _build_data(self.apikey, 'update_user',
591 id_, params = _build_data(self.apikey, 'update_user',
557 userid=usr.user_id)
592 userid=usr.user_id)
558
593
559 response = api_call(self, params)
594 response = api_call(self, params)
560 ret = 'failed to update user `%s`' % usr.user_id
595 ret = 'failed to update user `%s`' % usr.user_id
561
596
562 expected = ret
597 expected = ret
563 self._compare_error(id_, expected, given=response.body)
598 self._compare_error(id_, expected, given=response.body)
564
599
565 def test_api_get_repo(self):
600 def test_api_get_repo(self):
566 new_group = 'some_new_group'
601 new_group = 'some_new_group'
567 make_users_group(new_group)
602 make_users_group(new_group)
568 RepoModel().grant_users_group_permission(repo=self.REPO,
603 RepoModel().grant_users_group_permission(repo=self.REPO,
569 group_name=new_group,
604 group_name=new_group,
570 perm='repository.read')
605 perm='repository.read')
571 Session().commit()
606 Session().commit()
572 id_, params = _build_data(self.apikey, 'get_repo',
607 id_, params = _build_data(self.apikey, 'get_repo',
573 repoid=self.REPO)
608 repoid=self.REPO)
574 response = api_call(self, params)
609 response = api_call(self, params)
575
610
576 repo = RepoModel().get_by_repo_name(self.REPO)
611 repo = RepoModel().get_by_repo_name(self.REPO)
577 ret = repo.get_api_data()
612 ret = repo.get_api_data()
578
613
579 members = []
614 members = []
580 followers = []
615 followers = []
581 for user in repo.repo_to_perm:
616 for user in repo.repo_to_perm:
582 perm = user.permission.permission_name
617 perm = user.permission.permission_name
583 user = user.user
618 user = user.user
584 user_data = user.get_api_data()
619 user_data = user.get_api_data()
585 user_data['type'] = "user"
620 user_data['type'] = "user"
586 user_data['permission'] = perm
621 user_data['permission'] = perm
587 members.append(user_data)
622 members.append(user_data)
588
623
589 for users_group in repo.users_group_to_perm:
624 for users_group in repo.users_group_to_perm:
590 perm = users_group.permission.permission_name
625 perm = users_group.permission.permission_name
591 users_group = users_group.users_group
626 users_group = users_group.users_group
592 users_group_data = users_group.get_api_data()
627 users_group_data = users_group.get_api_data()
593 users_group_data['type'] = "users_group"
628 users_group_data['type'] = "users_group"
594 users_group_data['permission'] = perm
629 users_group_data['permission'] = perm
595 members.append(users_group_data)
630 members.append(users_group_data)
596
631
597 for user in repo.followers:
632 for user in repo.followers:
598 followers.append(user.user.get_api_data())
633 followers.append(user.user.get_api_data())
599
634
600 ret['members'] = members
635 ret['members'] = members
601 ret['followers'] = followers
636 ret['followers'] = followers
602
637
603 expected = ret
638 expected = ret
604 self._compare_ok(id_, expected, given=response.body)
639 self._compare_ok(id_, expected, given=response.body)
605 destroy_users_group(new_group)
640 destroy_users_group(new_group)
606
641
607 def test_api_get_repo_by_non_admin(self):
642 def test_api_get_repo_by_non_admin(self):
608 id_, params = _build_data(self.apikey, 'get_repo',
643 id_, params = _build_data(self.apikey, 'get_repo',
609 repoid=self.REPO)
644 repoid=self.REPO)
610 response = api_call(self, params)
645 response = api_call(self, params)
611
646
612 repo = RepoModel().get_by_repo_name(self.REPO)
647 repo = RepoModel().get_by_repo_name(self.REPO)
613 ret = repo.get_api_data()
648 ret = repo.get_api_data()
614
649
615 members = []
650 members = []
616 followers = []
651 followers = []
617 for user in repo.repo_to_perm:
652 for user in repo.repo_to_perm:
618 perm = user.permission.permission_name
653 perm = user.permission.permission_name
619 user = user.user
654 user = user.user
620 user_data = user.get_api_data()
655 user_data = user.get_api_data()
621 user_data['type'] = "user"
656 user_data['type'] = "user"
622 user_data['permission'] = perm
657 user_data['permission'] = perm
623 members.append(user_data)
658 members.append(user_data)
624
659
625 for users_group in repo.users_group_to_perm:
660 for users_group in repo.users_group_to_perm:
626 perm = users_group.permission.permission_name
661 perm = users_group.permission.permission_name
627 users_group = users_group.users_group
662 users_group = users_group.users_group
628 users_group_data = users_group.get_api_data()
663 users_group_data = users_group.get_api_data()
629 users_group_data['type'] = "users_group"
664 users_group_data['type'] = "users_group"
630 users_group_data['permission'] = perm
665 users_group_data['permission'] = perm
631 members.append(users_group_data)
666 members.append(users_group_data)
632
667
633 for user in repo.followers:
668 for user in repo.followers:
634 followers.append(user.user.get_api_data())
669 followers.append(user.user.get_api_data())
635
670
636 ret['members'] = members
671 ret['members'] = members
637 ret['followers'] = followers
672 ret['followers'] = followers
638
673
639 expected = ret
674 expected = ret
640 self._compare_ok(id_, expected, given=response.body)
675 self._compare_ok(id_, expected, given=response.body)
641
676
642 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
677 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
643 RepoModel().grant_user_permission(repo=self.REPO,
678 RepoModel().grant_user_permission(repo=self.REPO,
644 user=self.TEST_USER_LOGIN,
679 user=self.TEST_USER_LOGIN,
645 perm='repository.none')
680 perm='repository.none')
646
681
647 id_, params = _build_data(self.apikey_regular, 'get_repo',
682 id_, params = _build_data(self.apikey_regular, 'get_repo',
648 repoid=self.REPO)
683 repoid=self.REPO)
649 response = api_call(self, params)
684 response = api_call(self, params)
650
685
651 expected = 'repository `%s` does not exist' % (self.REPO)
686 expected = 'repository `%s` does not exist' % (self.REPO)
652 self._compare_error(id_, expected, given=response.body)
687 self._compare_error(id_, expected, given=response.body)
653
688
654 def test_api_get_repo_that_doesn_not_exist(self):
689 def test_api_get_repo_that_doesn_not_exist(self):
655 id_, params = _build_data(self.apikey, 'get_repo',
690 id_, params = _build_data(self.apikey, 'get_repo',
656 repoid='no-such-repo')
691 repoid='no-such-repo')
657 response = api_call(self, params)
692 response = api_call(self, params)
658
693
659 ret = 'repository `%s` does not exist' % 'no-such-repo'
694 ret = 'repository `%s` does not exist' % 'no-such-repo'
660 expected = ret
695 expected = ret
661 self._compare_error(id_, expected, given=response.body)
696 self._compare_error(id_, expected, given=response.body)
662
697
663 def test_api_get_repos(self):
698 def test_api_get_repos(self):
664 id_, params = _build_data(self.apikey, 'get_repos')
699 id_, params = _build_data(self.apikey, 'get_repos')
665 response = api_call(self, params)
700 response = api_call(self, params)
666
701
667 result = []
702 result = []
668 for repo in RepoModel().get_all():
703 for repo in RepoModel().get_all():
669 result.append(repo.get_api_data())
704 result.append(repo.get_api_data())
670 ret = jsonify(result)
705 ret = jsonify(result)
671
706
672 expected = ret
707 expected = ret
673 self._compare_ok(id_, expected, given=response.body)
708 self._compare_ok(id_, expected, given=response.body)
674
709
675 def test_api_get_repos_non_admin(self):
710 def test_api_get_repos_non_admin(self):
676 id_, params = _build_data(self.apikey_regular, 'get_repos')
711 id_, params = _build_data(self.apikey_regular, 'get_repos')
677 response = api_call(self, params)
712 response = api_call(self, params)
678
713
679 result = []
714 result = []
680 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
715 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
681 result.append(repo.get_api_data())
716 result.append(repo.get_api_data())
682 ret = jsonify(result)
717 ret = jsonify(result)
683
718
684 expected = ret
719 expected = ret
685 self._compare_ok(id_, expected, given=response.body)
720 self._compare_ok(id_, expected, given=response.body)
686
721
687 @parameterized.expand([('all', 'all'),
722 @parameterized.expand([('all', 'all'),
688 ('dirs', 'dirs'),
723 ('dirs', 'dirs'),
689 ('files', 'files'), ])
724 ('files', 'files'), ])
690 def test_api_get_repo_nodes(self, name, ret_type):
725 def test_api_get_repo_nodes(self, name, ret_type):
691 rev = 'tip'
726 rev = 'tip'
692 path = '/'
727 path = '/'
693 id_, params = _build_data(self.apikey, 'get_repo_nodes',
728 id_, params = _build_data(self.apikey, 'get_repo_nodes',
694 repoid=self.REPO, revision=rev,
729 repoid=self.REPO, revision=rev,
695 root_path=path,
730 root_path=path,
696 ret_type=ret_type)
731 ret_type=ret_type)
697 response = api_call(self, params)
732 response = api_call(self, params)
698
733
699 # we don't the actual return types here since it's tested somewhere
734 # we don't the actual return types here since it's tested somewhere
700 # else
735 # else
701 expected = json.loads(response.body)['result']
736 expected = json.loads(response.body)['result']
702 self._compare_ok(id_, expected, given=response.body)
737 self._compare_ok(id_, expected, given=response.body)
703
738
704 def test_api_get_repo_nodes_bad_revisions(self):
739 def test_api_get_repo_nodes_bad_revisions(self):
705 rev = 'i-dont-exist'
740 rev = 'i-dont-exist'
706 path = '/'
741 path = '/'
707 id_, params = _build_data(self.apikey, 'get_repo_nodes',
742 id_, params = _build_data(self.apikey, 'get_repo_nodes',
708 repoid=self.REPO, revision=rev,
743 repoid=self.REPO, revision=rev,
709 root_path=path,)
744 root_path=path,)
710 response = api_call(self, params)
745 response = api_call(self, params)
711
746
712 expected = 'failed to get repo: `%s` nodes' % self.REPO
747 expected = 'failed to get repo: `%s` nodes' % self.REPO
713 self._compare_error(id_, expected, given=response.body)
748 self._compare_error(id_, expected, given=response.body)
714
749
715 def test_api_get_repo_nodes_bad_path(self):
750 def test_api_get_repo_nodes_bad_path(self):
716 rev = 'tip'
751 rev = 'tip'
717 path = '/idontexits'
752 path = '/idontexits'
718 id_, params = _build_data(self.apikey, 'get_repo_nodes',
753 id_, params = _build_data(self.apikey, 'get_repo_nodes',
719 repoid=self.REPO, revision=rev,
754 repoid=self.REPO, revision=rev,
720 root_path=path,)
755 root_path=path,)
721 response = api_call(self, params)
756 response = api_call(self, params)
722
757
723 expected = 'failed to get repo: `%s` nodes' % self.REPO
758 expected = 'failed to get repo: `%s` nodes' % self.REPO
724 self._compare_error(id_, expected, given=response.body)
759 self._compare_error(id_, expected, given=response.body)
725
760
726 def test_api_get_repo_nodes_bad_ret_type(self):
761 def test_api_get_repo_nodes_bad_ret_type(self):
727 rev = 'tip'
762 rev = 'tip'
728 path = '/'
763 path = '/'
729 ret_type = 'error'
764 ret_type = 'error'
730 id_, params = _build_data(self.apikey, 'get_repo_nodes',
765 id_, params = _build_data(self.apikey, 'get_repo_nodes',
731 repoid=self.REPO, revision=rev,
766 repoid=self.REPO, revision=rev,
732 root_path=path,
767 root_path=path,
733 ret_type=ret_type)
768 ret_type=ret_type)
734 response = api_call(self, params)
769 response = api_call(self, params)
735
770
736 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
771 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
737 self._compare_error(id_, expected, given=response.body)
772 self._compare_error(id_, expected, given=response.body)
738
773
739 def test_api_create_repo(self):
774 def test_api_create_repo(self):
740 repo_name = 'api-repo'
775 repo_name = 'api-repo'
741 id_, params = _build_data(self.apikey, 'create_repo',
776 id_, params = _build_data(self.apikey, 'create_repo',
742 repo_name=repo_name,
777 repo_name=repo_name,
743 owner=TEST_USER_ADMIN_LOGIN,
778 owner=TEST_USER_ADMIN_LOGIN,
744 repo_type='hg',
779 repo_type='hg',
745 )
780 )
746 response = api_call(self, params)
781 response = api_call(self, params)
747
782
748 repo = RepoModel().get_by_repo_name(repo_name)
783 repo = RepoModel().get_by_repo_name(repo_name)
749 ret = {
784 ret = {
750 'msg': 'Created new repository `%s`' % repo_name,
785 'msg': 'Created new repository `%s`' % repo_name,
751 'repo': jsonify(repo.get_api_data())
786 'repo': jsonify(repo.get_api_data())
752 }
787 }
753 expected = ret
788 expected = ret
754 self._compare_ok(id_, expected, given=response.body)
789 self._compare_ok(id_, expected, given=response.body)
755 fixture.destroy_repo(repo_name)
790 fixture.destroy_repo(repo_name)
756
791
757 def test_api_create_repo_unknown_owner(self):
792 def test_api_create_repo_unknown_owner(self):
758 repo_name = 'api-repo'
793 repo_name = 'api-repo'
759 owner = 'i-dont-exist'
794 owner = 'i-dont-exist'
760 id_, params = _build_data(self.apikey, 'create_repo',
795 id_, params = _build_data(self.apikey, 'create_repo',
761 repo_name=repo_name,
796 repo_name=repo_name,
762 owner=owner,
797 owner=owner,
763 repo_type='hg',
798 repo_type='hg',
764 )
799 )
765 response = api_call(self, params)
800 response = api_call(self, params)
766 expected = 'user `%s` does not exist' % owner
801 expected = 'user `%s` does not exist' % owner
767 self._compare_error(id_, expected, given=response.body)
802 self._compare_error(id_, expected, given=response.body)
768
803
769 def test_api_create_repo_dont_specify_owner(self):
804 def test_api_create_repo_dont_specify_owner(self):
770 repo_name = 'api-repo'
805 repo_name = 'api-repo'
771 owner = 'i-dont-exist'
806 owner = 'i-dont-exist'
772 id_, params = _build_data(self.apikey, 'create_repo',
807 id_, params = _build_data(self.apikey, 'create_repo',
773 repo_name=repo_name,
808 repo_name=repo_name,
774 repo_type='hg',
809 repo_type='hg',
775 )
810 )
776 response = api_call(self, params)
811 response = api_call(self, params)
777
812
778 repo = RepoModel().get_by_repo_name(repo_name)
813 repo = RepoModel().get_by_repo_name(repo_name)
779 ret = {
814 ret = {
780 'msg': 'Created new repository `%s`' % repo_name,
815 'msg': 'Created new repository `%s`' % repo_name,
781 'repo': jsonify(repo.get_api_data())
816 'repo': jsonify(repo.get_api_data())
782 }
817 }
783 expected = ret
818 expected = ret
784 self._compare_ok(id_, expected, given=response.body)
819 self._compare_ok(id_, expected, given=response.body)
785 fixture.destroy_repo(repo_name)
820 fixture.destroy_repo(repo_name)
786
821
787 def test_api_create_repo_by_non_admin(self):
822 def test_api_create_repo_by_non_admin(self):
788 repo_name = 'api-repo'
823 repo_name = 'api-repo'
789 owner = 'i-dont-exist'
824 owner = 'i-dont-exist'
790 id_, params = _build_data(self.apikey_regular, 'create_repo',
825 id_, params = _build_data(self.apikey_regular, 'create_repo',
791 repo_name=repo_name,
826 repo_name=repo_name,
792 repo_type='hg',
827 repo_type='hg',
793 )
828 )
794 response = api_call(self, params)
829 response = api_call(self, params)
795
830
796 repo = RepoModel().get_by_repo_name(repo_name)
831 repo = RepoModel().get_by_repo_name(repo_name)
797 ret = {
832 ret = {
798 'msg': 'Created new repository `%s`' % repo_name,
833 'msg': 'Created new repository `%s`' % repo_name,
799 'repo': jsonify(repo.get_api_data())
834 'repo': jsonify(repo.get_api_data())
800 }
835 }
801 expected = ret
836 expected = ret
802 self._compare_ok(id_, expected, given=response.body)
837 self._compare_ok(id_, expected, given=response.body)
803 fixture.destroy_repo(repo_name)
838 fixture.destroy_repo(repo_name)
804
839
805 def test_api_create_repo_by_non_admin_specify_owner(self):
840 def test_api_create_repo_by_non_admin_specify_owner(self):
806 repo_name = 'api-repo'
841 repo_name = 'api-repo'
807 owner = 'i-dont-exist'
842 owner = 'i-dont-exist'
808 id_, params = _build_data(self.apikey_regular, 'create_repo',
843 id_, params = _build_data(self.apikey_regular, 'create_repo',
809 repo_name=repo_name,
844 repo_name=repo_name,
810 repo_type='hg',
845 repo_type='hg',
811 owner=owner
846 owner=owner
812 )
847 )
813 response = api_call(self, params)
848 response = api_call(self, params)
814
849
815 expected = 'Only RhodeCode admin can specify `owner` param'
850 expected = 'Only RhodeCode admin can specify `owner` param'
816 self._compare_error(id_, expected, given=response.body)
851 self._compare_error(id_, expected, given=response.body)
817 fixture.destroy_repo(repo_name)
852 fixture.destroy_repo(repo_name)
818
853
819 def test_api_create_repo_exists(self):
854 def test_api_create_repo_exists(self):
820 repo_name = self.REPO
855 repo_name = self.REPO
821 id_, params = _build_data(self.apikey, 'create_repo',
856 id_, params = _build_data(self.apikey, 'create_repo',
822 repo_name=repo_name,
857 repo_name=repo_name,
823 owner=TEST_USER_ADMIN_LOGIN,
858 owner=TEST_USER_ADMIN_LOGIN,
824 repo_type='hg',
859 repo_type='hg',
825 )
860 )
826 response = api_call(self, params)
861 response = api_call(self, params)
827 expected = "repo `%s` already exist" % repo_name
862 expected = "repo `%s` already exist" % repo_name
828 self._compare_error(id_, expected, given=response.body)
863 self._compare_error(id_, expected, given=response.body)
829
864
830 @mock.patch.object(RepoModel, 'create_repo', crash)
865 @mock.patch.object(RepoModel, 'create_repo', crash)
831 def test_api_create_repo_exception_occurred(self):
866 def test_api_create_repo_exception_occurred(self):
832 repo_name = 'api-repo'
867 repo_name = 'api-repo'
833 id_, params = _build_data(self.apikey, 'create_repo',
868 id_, params = _build_data(self.apikey, 'create_repo',
834 repo_name=repo_name,
869 repo_name=repo_name,
835 owner=TEST_USER_ADMIN_LOGIN,
870 owner=TEST_USER_ADMIN_LOGIN,
836 repo_type='hg',
871 repo_type='hg',
837 )
872 )
838 response = api_call(self, params)
873 response = api_call(self, params)
839 expected = 'failed to create repository `%s`' % repo_name
874 expected = 'failed to create repository `%s`' % repo_name
840 self._compare_error(id_, expected, given=response.body)
875 self._compare_error(id_, expected, given=response.body)
841
876
842 def test_api_delete_repo(self):
877 def test_api_delete_repo(self):
843 repo_name = 'api_delete_me'
878 repo_name = 'api_delete_me'
844 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
879 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
845
880
846 id_, params = _build_data(self.apikey, 'delete_repo',
881 id_, params = _build_data(self.apikey, 'delete_repo',
847 repoid=repo_name,)
882 repoid=repo_name,)
848 response = api_call(self, params)
883 response = api_call(self, params)
849
884
850 ret = {
885 ret = {
851 'msg': 'Deleted repository `%s`' % repo_name,
886 'msg': 'Deleted repository `%s`' % repo_name,
852 'success': True
887 'success': True
853 }
888 }
854 expected = ret
889 expected = ret
855 self._compare_ok(id_, expected, given=response.body)
890 self._compare_ok(id_, expected, given=response.body)
856
891
857 def test_api_delete_repo_by_non_admin(self):
892 def test_api_delete_repo_by_non_admin(self):
858 repo_name = 'api_delete_me'
893 repo_name = 'api_delete_me'
859 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
894 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE,
860 cur_user=self.TEST_USER_LOGIN)
895 cur_user=self.TEST_USER_LOGIN)
861 try:
896 try:
862 id_, params = _build_data(self.apikey_regular, 'delete_repo',
897 id_, params = _build_data(self.apikey_regular, 'delete_repo',
863 repoid=repo_name,)
898 repoid=repo_name,)
864 response = api_call(self, params)
899 response = api_call(self, params)
865
900
866 ret = {
901 ret = {
867 'msg': 'Deleted repository `%s`' % repo_name,
902 'msg': 'Deleted repository `%s`' % repo_name,
868 'success': True
903 'success': True
869 }
904 }
870 expected = ret
905 expected = ret
871 self._compare_ok(id_, expected, given=response.body)
906 self._compare_ok(id_, expected, given=response.body)
872 finally:
907 finally:
873 fixture.destroy_repo(repo_name)
908 fixture.destroy_repo(repo_name)
874
909
875 def test_api_delete_repo_by_non_admin_no_permission(self):
910 def test_api_delete_repo_by_non_admin_no_permission(self):
876 repo_name = 'api_delete_me'
911 repo_name = 'api_delete_me'
877 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
912 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
878 try:
913 try:
879 id_, params = _build_data(self.apikey_regular, 'delete_repo',
914 id_, params = _build_data(self.apikey_regular, 'delete_repo',
880 repoid=repo_name,)
915 repoid=repo_name,)
881 response = api_call(self, params)
916 response = api_call(self, params)
882 expected = 'repository `%s` does not exist' % (repo_name)
917 expected = 'repository `%s` does not exist' % (repo_name)
883 self._compare_error(id_, expected, given=response.body)
918 self._compare_error(id_, expected, given=response.body)
884 finally:
919 finally:
885 fixture.destroy_repo(repo_name)
920 fixture.destroy_repo(repo_name)
886
921
887 def test_api_delete_repo_exception_occurred(self):
922 def test_api_delete_repo_exception_occurred(self):
888 repo_name = 'api_delete_me'
923 repo_name = 'api_delete_me'
889 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
924 fixture.create_repo(repo_name, repo_type=self.REPO_TYPE)
890 try:
925 try:
891 with mock.patch.object(RepoModel, 'delete', crash):
926 with mock.patch.object(RepoModel, 'delete', crash):
892 id_, params = _build_data(self.apikey, 'delete_repo',
927 id_, params = _build_data(self.apikey, 'delete_repo',
893 repoid=repo_name,)
928 repoid=repo_name,)
894 response = api_call(self, params)
929 response = api_call(self, params)
895
930
896 expected = 'failed to delete repository `%s`' % repo_name
931 expected = 'failed to delete repository `%s`' % repo_name
897 self._compare_error(id_, expected, given=response.body)
932 self._compare_error(id_, expected, given=response.body)
898 finally:
933 finally:
899 fixture.destroy_repo(repo_name)
934 fixture.destroy_repo(repo_name)
900
935
901 def test_api_fork_repo(self):
936 def test_api_fork_repo(self):
902 fork_name = 'api-repo-fork'
937 fork_name = 'api-repo-fork'
903 id_, params = _build_data(self.apikey, 'fork_repo',
938 id_, params = _build_data(self.apikey, 'fork_repo',
904 repoid=self.REPO,
939 repoid=self.REPO,
905 fork_name=fork_name,
940 fork_name=fork_name,
906 owner=TEST_USER_ADMIN_LOGIN,
941 owner=TEST_USER_ADMIN_LOGIN,
907 )
942 )
908 response = api_call(self, params)
943 response = api_call(self, params)
909
944
910 ret = {
945 ret = {
911 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
946 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
912 fork_name),
947 fork_name),
913 'success': True
948 'success': True
914 }
949 }
915 expected = ret
950 expected = ret
916 self._compare_ok(id_, expected, given=response.body)
951 self._compare_ok(id_, expected, given=response.body)
917 fixture.destroy_repo(fork_name)
952 fixture.destroy_repo(fork_name)
918
953
919 def test_api_fork_repo_non_admin(self):
954 def test_api_fork_repo_non_admin(self):
920 fork_name = 'api-repo-fork'
955 fork_name = 'api-repo-fork'
921 id_, params = _build_data(self.apikey_regular, 'fork_repo',
956 id_, params = _build_data(self.apikey_regular, 'fork_repo',
922 repoid=self.REPO,
957 repoid=self.REPO,
923 fork_name=fork_name,
958 fork_name=fork_name,
924 )
959 )
925 response = api_call(self, params)
960 response = api_call(self, params)
926
961
927 ret = {
962 ret = {
928 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
963 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
929 fork_name),
964 fork_name),
930 'success': True
965 'success': True
931 }
966 }
932 expected = ret
967 expected = ret
933 self._compare_ok(id_, expected, given=response.body)
968 self._compare_ok(id_, expected, given=response.body)
934 fixture.destroy_repo(fork_name)
969 fixture.destroy_repo(fork_name)
935
970
936 def test_api_fork_repo_non_admin_specify_owner(self):
971 def test_api_fork_repo_non_admin_specify_owner(self):
937 fork_name = 'api-repo-fork'
972 fork_name = 'api-repo-fork'
938 id_, params = _build_data(self.apikey_regular, 'fork_repo',
973 id_, params = _build_data(self.apikey_regular, 'fork_repo',
939 repoid=self.REPO,
974 repoid=self.REPO,
940 fork_name=fork_name,
975 fork_name=fork_name,
941 owner=TEST_USER_ADMIN_LOGIN,
976 owner=TEST_USER_ADMIN_LOGIN,
942 )
977 )
943 response = api_call(self, params)
978 response = api_call(self, params)
944 expected = 'Only RhodeCode admin can specify `owner` param'
979 expected = 'Only RhodeCode admin can specify `owner` param'
945 self._compare_error(id_, expected, given=response.body)
980 self._compare_error(id_, expected, given=response.body)
946 fixture.destroy_repo(fork_name)
981 fixture.destroy_repo(fork_name)
947
982
948 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
983 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
949 RepoModel().grant_user_permission(repo=self.REPO,
984 RepoModel().grant_user_permission(repo=self.REPO,
950 user=self.TEST_USER_LOGIN,
985 user=self.TEST_USER_LOGIN,
951 perm='repository.none')
986 perm='repository.none')
952 fork_name = 'api-repo-fork'
987 fork_name = 'api-repo-fork'
953 id_, params = _build_data(self.apikey_regular, 'fork_repo',
988 id_, params = _build_data(self.apikey_regular, 'fork_repo',
954 repoid=self.REPO,
989 repoid=self.REPO,
955 fork_name=fork_name,
990 fork_name=fork_name,
956 )
991 )
957 response = api_call(self, params)
992 response = api_call(self, params)
958 expected = 'repository `%s` does not exist' % (self.REPO)
993 expected = 'repository `%s` does not exist' % (self.REPO)
959 self._compare_error(id_, expected, given=response.body)
994 self._compare_error(id_, expected, given=response.body)
960 fixture.destroy_repo(fork_name)
995 fixture.destroy_repo(fork_name)
961
996
962 def test_api_fork_repo_unknown_owner(self):
997 def test_api_fork_repo_unknown_owner(self):
963 fork_name = 'api-repo-fork'
998 fork_name = 'api-repo-fork'
964 owner = 'i-dont-exist'
999 owner = 'i-dont-exist'
965 id_, params = _build_data(self.apikey, 'fork_repo',
1000 id_, params = _build_data(self.apikey, 'fork_repo',
966 repoid=self.REPO,
1001 repoid=self.REPO,
967 fork_name=fork_name,
1002 fork_name=fork_name,
968 owner=owner,
1003 owner=owner,
969 )
1004 )
970 response = api_call(self, params)
1005 response = api_call(self, params)
971 expected = 'user `%s` does not exist' % owner
1006 expected = 'user `%s` does not exist' % owner
972 self._compare_error(id_, expected, given=response.body)
1007 self._compare_error(id_, expected, given=response.body)
973
1008
974 def test_api_fork_repo_fork_exists(self):
1009 def test_api_fork_repo_fork_exists(self):
975 fork_name = 'api-repo-fork'
1010 fork_name = 'api-repo-fork'
976 fixture.create_fork(self.REPO, fork_name)
1011 fixture.create_fork(self.REPO, fork_name)
977
1012
978 try:
1013 try:
979 fork_name = 'api-repo-fork'
1014 fork_name = 'api-repo-fork'
980
1015
981 id_, params = _build_data(self.apikey, 'fork_repo',
1016 id_, params = _build_data(self.apikey, 'fork_repo',
982 repoid=self.REPO,
1017 repoid=self.REPO,
983 fork_name=fork_name,
1018 fork_name=fork_name,
984 owner=TEST_USER_ADMIN_LOGIN,
1019 owner=TEST_USER_ADMIN_LOGIN,
985 )
1020 )
986 response = api_call(self, params)
1021 response = api_call(self, params)
987
1022
988 expected = "fork `%s` already exist" % fork_name
1023 expected = "fork `%s` already exist" % fork_name
989 self._compare_error(id_, expected, given=response.body)
1024 self._compare_error(id_, expected, given=response.body)
990 finally:
1025 finally:
991 fixture.destroy_repo(fork_name)
1026 fixture.destroy_repo(fork_name)
992
1027
993 def test_api_fork_repo_repo_exists(self):
1028 def test_api_fork_repo_repo_exists(self):
994 fork_name = self.REPO
1029 fork_name = self.REPO
995
1030
996 id_, params = _build_data(self.apikey, 'fork_repo',
1031 id_, params = _build_data(self.apikey, 'fork_repo',
997 repoid=self.REPO,
1032 repoid=self.REPO,
998 fork_name=fork_name,
1033 fork_name=fork_name,
999 owner=TEST_USER_ADMIN_LOGIN,
1034 owner=TEST_USER_ADMIN_LOGIN,
1000 )
1035 )
1001 response = api_call(self, params)
1036 response = api_call(self, params)
1002
1037
1003 expected = "repo `%s` already exist" % fork_name
1038 expected = "repo `%s` already exist" % fork_name
1004 self._compare_error(id_, expected, given=response.body)
1039 self._compare_error(id_, expected, given=response.body)
1005
1040
1006 @mock.patch.object(RepoModel, 'create_fork', crash)
1041 @mock.patch.object(RepoModel, 'create_fork', crash)
1007 def test_api_fork_repo_exception_occurred(self):
1042 def test_api_fork_repo_exception_occurred(self):
1008 fork_name = 'api-repo-fork'
1043 fork_name = 'api-repo-fork'
1009 id_, params = _build_data(self.apikey, 'fork_repo',
1044 id_, params = _build_data(self.apikey, 'fork_repo',
1010 repoid=self.REPO,
1045 repoid=self.REPO,
1011 fork_name=fork_name,
1046 fork_name=fork_name,
1012 owner=TEST_USER_ADMIN_LOGIN,
1047 owner=TEST_USER_ADMIN_LOGIN,
1013 )
1048 )
1014 response = api_call(self, params)
1049 response = api_call(self, params)
1015
1050
1016 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1051 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
1017 fork_name)
1052 fork_name)
1018 self._compare_error(id_, expected, given=response.body)
1053 self._compare_error(id_, expected, given=response.body)
1019
1054
1020 def test_api_get_users_group(self):
1055 def test_api_get_users_group(self):
1021 id_, params = _build_data(self.apikey, 'get_users_group',
1056 id_, params = _build_data(self.apikey, 'get_users_group',
1022 usersgroupid=TEST_USER_GROUP)
1057 usersgroupid=TEST_USER_GROUP)
1023 response = api_call(self, params)
1058 response = api_call(self, params)
1024
1059
1025 users_group = UserGroupModel().get_group(TEST_USER_GROUP)
1060 users_group = UserGroupModel().get_group(TEST_USER_GROUP)
1026 members = []
1061 members = []
1027 for user in users_group.members:
1062 for user in users_group.members:
1028 user = user.user
1063 user = user.user
1029 members.append(user.get_api_data())
1064 members.append(user.get_api_data())
1030
1065
1031 ret = users_group.get_api_data()
1066 ret = users_group.get_api_data()
1032 ret['members'] = members
1067 ret['members'] = members
1033 expected = ret
1068 expected = ret
1034 self._compare_ok(id_, expected, given=response.body)
1069 self._compare_ok(id_, expected, given=response.body)
1035
1070
1036 def test_api_get_users_groups(self):
1071 def test_api_get_users_groups(self):
1037
1072
1038 make_users_group('test_users_group2')
1073 make_users_group('test_users_group2')
1039
1074
1040 id_, params = _build_data(self.apikey, 'get_users_groups',)
1075 id_, params = _build_data(self.apikey, 'get_users_groups',)
1041 response = api_call(self, params)
1076 response = api_call(self, params)
1042
1077
1043 expected = []
1078 expected = []
1044 for gr_name in [TEST_USER_GROUP, 'test_users_group2']:
1079 for gr_name in [TEST_USER_GROUP, 'test_users_group2']:
1045 users_group = UserGroupModel().get_group(gr_name)
1080 users_group = UserGroupModel().get_group(gr_name)
1046 ret = users_group.get_api_data()
1081 ret = users_group.get_api_data()
1047 expected.append(ret)
1082 expected.append(ret)
1048 self._compare_ok(id_, expected, given=response.body)
1083 self._compare_ok(id_, expected, given=response.body)
1049
1084
1050 UserGroupModel().delete(users_group='test_users_group2')
1085 UserGroupModel().delete(users_group='test_users_group2')
1051 Session().commit()
1086 Session().commit()
1052
1087
1053 def test_api_create_users_group(self):
1088 def test_api_create_users_group(self):
1054 group_name = 'some_new_group'
1089 group_name = 'some_new_group'
1055 id_, params = _build_data(self.apikey, 'create_users_group',
1090 id_, params = _build_data(self.apikey, 'create_users_group',
1056 group_name=group_name)
1091 group_name=group_name)
1057 response = api_call(self, params)
1092 response = api_call(self, params)
1058
1093
1059 ret = {
1094 ret = {
1060 'msg': 'created new user group `%s`' % group_name,
1095 'msg': 'created new user group `%s`' % group_name,
1061 'users_group': jsonify(UserGroupModel()\
1096 'users_group': jsonify(UserGroupModel()\
1062 .get_by_name(group_name)\
1097 .get_by_name(group_name)\
1063 .get_api_data())
1098 .get_api_data())
1064 }
1099 }
1065 expected = ret
1100 expected = ret
1066 self._compare_ok(id_, expected, given=response.body)
1101 self._compare_ok(id_, expected, given=response.body)
1067
1102
1068 destroy_users_group(group_name)
1103 destroy_users_group(group_name)
1069
1104
1070 def test_api_get_users_group_that_exist(self):
1105 def test_api_get_users_group_that_exist(self):
1071 id_, params = _build_data(self.apikey, 'create_users_group',
1106 id_, params = _build_data(self.apikey, 'create_users_group',
1072 group_name=TEST_USER_GROUP)
1107 group_name=TEST_USER_GROUP)
1073 response = api_call(self, params)
1108 response = api_call(self, params)
1074
1109
1075 expected = "user group `%s` already exist" % TEST_USER_GROUP
1110 expected = "user group `%s` already exist" % TEST_USER_GROUP
1076 self._compare_error(id_, expected, given=response.body)
1111 self._compare_error(id_, expected, given=response.body)
1077
1112
1078 @mock.patch.object(UserGroupModel, 'create', crash)
1113 @mock.patch.object(UserGroupModel, 'create', crash)
1079 def test_api_get_users_group_exception_occurred(self):
1114 def test_api_get_users_group_exception_occurred(self):
1080 group_name = 'exception_happens'
1115 group_name = 'exception_happens'
1081 id_, params = _build_data(self.apikey, 'create_users_group',
1116 id_, params = _build_data(self.apikey, 'create_users_group',
1082 group_name=group_name)
1117 group_name=group_name)
1083 response = api_call(self, params)
1118 response = api_call(self, params)
1084
1119
1085 expected = 'failed to create group `%s`' % group_name
1120 expected = 'failed to create group `%s`' % group_name
1086 self._compare_error(id_, expected, given=response.body)
1121 self._compare_error(id_, expected, given=response.body)
1087
1122
1088 def test_api_add_user_to_users_group(self):
1123 def test_api_add_user_to_users_group(self):
1089 gr_name = 'test_group'
1124 gr_name = 'test_group'
1090 fixture.create_user_group(gr_name)
1125 fixture.create_user_group(gr_name)
1091 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1126 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1092 usersgroupid=gr_name,
1127 usersgroupid=gr_name,
1093 userid=TEST_USER_ADMIN_LOGIN)
1128 userid=TEST_USER_ADMIN_LOGIN)
1094 response = api_call(self, params)
1129 response = api_call(self, params)
1095
1130
1096 expected = {
1131 expected = {
1097 'msg': 'added member `%s` to user group `%s`' % (
1132 'msg': 'added member `%s` to user group `%s`' % (
1098 TEST_USER_ADMIN_LOGIN, gr_name
1133 TEST_USER_ADMIN_LOGIN, gr_name
1099 ),
1134 ),
1100 'success': True}
1135 'success': True}
1101 self._compare_ok(id_, expected, given=response.body)
1136 self._compare_ok(id_, expected, given=response.body)
1102
1137
1103 UserGroupModel().delete(users_group=gr_name)
1138 UserGroupModel().delete(users_group=gr_name)
1104 Session().commit()
1139 Session().commit()
1105
1140
1106 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1141 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1107 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1142 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1108 usersgroupid='false-group',
1143 usersgroupid='false-group',
1109 userid=TEST_USER_ADMIN_LOGIN)
1144 userid=TEST_USER_ADMIN_LOGIN)
1110 response = api_call(self, params)
1145 response = api_call(self, params)
1111
1146
1112 expected = 'user group `%s` does not exist' % 'false-group'
1147 expected = 'user group `%s` does not exist' % 'false-group'
1113 self._compare_error(id_, expected, given=response.body)
1148 self._compare_error(id_, expected, given=response.body)
1114
1149
1115 @mock.patch.object(UserGroupModel, 'add_user_to_group', crash)
1150 @mock.patch.object(UserGroupModel, 'add_user_to_group', crash)
1116 def test_api_add_user_to_users_group_exception_occurred(self):
1151 def test_api_add_user_to_users_group_exception_occurred(self):
1117 gr_name = 'test_group'
1152 gr_name = 'test_group'
1118 fixture.create_user_group(gr_name)
1153 fixture.create_user_group(gr_name)
1119 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1154 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1120 usersgroupid=gr_name,
1155 usersgroupid=gr_name,
1121 userid=TEST_USER_ADMIN_LOGIN)
1156 userid=TEST_USER_ADMIN_LOGIN)
1122 response = api_call(self, params)
1157 response = api_call(self, params)
1123
1158
1124 expected = 'failed to add member to user group `%s`' % gr_name
1159 expected = 'failed to add member to user group `%s`' % gr_name
1125 self._compare_error(id_, expected, given=response.body)
1160 self._compare_error(id_, expected, given=response.body)
1126
1161
1127 UserGroupModel().delete(users_group=gr_name)
1162 UserGroupModel().delete(users_group=gr_name)
1128 Session().commit()
1163 Session().commit()
1129
1164
1130 def test_api_remove_user_from_users_group(self):
1165 def test_api_remove_user_from_users_group(self):
1131 gr_name = 'test_group_3'
1166 gr_name = 'test_group_3'
1132 gr = fixture.create_user_group(gr_name)
1167 gr = fixture.create_user_group(gr_name)
1133 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1168 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1134 Session().commit()
1169 Session().commit()
1135 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1170 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1136 usersgroupid=gr_name,
1171 usersgroupid=gr_name,
1137 userid=TEST_USER_ADMIN_LOGIN)
1172 userid=TEST_USER_ADMIN_LOGIN)
1138 response = api_call(self, params)
1173 response = api_call(self, params)
1139
1174
1140 expected = {
1175 expected = {
1141 'msg': 'removed member `%s` from user group `%s`' % (
1176 'msg': 'removed member `%s` from user group `%s`' % (
1142 TEST_USER_ADMIN_LOGIN, gr_name
1177 TEST_USER_ADMIN_LOGIN, gr_name
1143 ),
1178 ),
1144 'success': True}
1179 'success': True}
1145 self._compare_ok(id_, expected, given=response.body)
1180 self._compare_ok(id_, expected, given=response.body)
1146
1181
1147 UserGroupModel().delete(users_group=gr_name)
1182 UserGroupModel().delete(users_group=gr_name)
1148 Session().commit()
1183 Session().commit()
1149
1184
1150 @mock.patch.object(UserGroupModel, 'remove_user_from_group', crash)
1185 @mock.patch.object(UserGroupModel, 'remove_user_from_group', crash)
1151 def test_api_remove_user_from_users_group_exception_occurred(self):
1186 def test_api_remove_user_from_users_group_exception_occurred(self):
1152 gr_name = 'test_group_3'
1187 gr_name = 'test_group_3'
1153 gr = fixture.create_user_group(gr_name)
1188 gr = fixture.create_user_group(gr_name)
1154 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1189 UserGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1155 Session().commit()
1190 Session().commit()
1156 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1191 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1157 usersgroupid=gr_name,
1192 usersgroupid=gr_name,
1158 userid=TEST_USER_ADMIN_LOGIN)
1193 userid=TEST_USER_ADMIN_LOGIN)
1159 response = api_call(self, params)
1194 response = api_call(self, params)
1160
1195
1161 expected = 'failed to remove member from user group `%s`' % gr_name
1196 expected = 'failed to remove member from user group `%s`' % gr_name
1162 self._compare_error(id_, expected, given=response.body)
1197 self._compare_error(id_, expected, given=response.body)
1163
1198
1164 UserGroupModel().delete(users_group=gr_name)
1199 UserGroupModel().delete(users_group=gr_name)
1165 Session().commit()
1200 Session().commit()
1166
1201
1167 @parameterized.expand([('none', 'repository.none'),
1202 @parameterized.expand([('none', 'repository.none'),
1168 ('read', 'repository.read'),
1203 ('read', 'repository.read'),
1169 ('write', 'repository.write'),
1204 ('write', 'repository.write'),
1170 ('admin', 'repository.admin')])
1205 ('admin', 'repository.admin')])
1171 def test_api_grant_user_permission(self, name, perm):
1206 def test_api_grant_user_permission(self, name, perm):
1172 id_, params = _build_data(self.apikey, 'grant_user_permission',
1207 id_, params = _build_data(self.apikey, 'grant_user_permission',
1173 repoid=self.REPO,
1208 repoid=self.REPO,
1174 userid=TEST_USER_ADMIN_LOGIN,
1209 userid=TEST_USER_ADMIN_LOGIN,
1175 perm=perm)
1210 perm=perm)
1176 response = api_call(self, params)
1211 response = api_call(self, params)
1177
1212
1178 ret = {
1213 ret = {
1179 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1214 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1180 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1215 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1181 ),
1216 ),
1182 'success': True
1217 'success': True
1183 }
1218 }
1184 expected = ret
1219 expected = ret
1185 self._compare_ok(id_, expected, given=response.body)
1220 self._compare_ok(id_, expected, given=response.body)
1186
1221
1187 def test_api_grant_user_permission_wrong_permission(self):
1222 def test_api_grant_user_permission_wrong_permission(self):
1188 perm = 'haha.no.permission'
1223 perm = 'haha.no.permission'
1189 id_, params = _build_data(self.apikey, 'grant_user_permission',
1224 id_, params = _build_data(self.apikey, 'grant_user_permission',
1190 repoid=self.REPO,
1225 repoid=self.REPO,
1191 userid=TEST_USER_ADMIN_LOGIN,
1226 userid=TEST_USER_ADMIN_LOGIN,
1192 perm=perm)
1227 perm=perm)
1193 response = api_call(self, params)
1228 response = api_call(self, params)
1194
1229
1195 expected = 'permission `%s` does not exist' % perm
1230 expected = 'permission `%s` does not exist' % perm
1196 self._compare_error(id_, expected, given=response.body)
1231 self._compare_error(id_, expected, given=response.body)
1197
1232
1198 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1233 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1199 def test_api_grant_user_permission_exception_when_adding(self):
1234 def test_api_grant_user_permission_exception_when_adding(self):
1200 perm = 'repository.read'
1235 perm = 'repository.read'
1201 id_, params = _build_data(self.apikey, 'grant_user_permission',
1236 id_, params = _build_data(self.apikey, 'grant_user_permission',
1202 repoid=self.REPO,
1237 repoid=self.REPO,
1203 userid=TEST_USER_ADMIN_LOGIN,
1238 userid=TEST_USER_ADMIN_LOGIN,
1204 perm=perm)
1239 perm=perm)
1205 response = api_call(self, params)
1240 response = api_call(self, params)
1206
1241
1207 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1242 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1208 TEST_USER_ADMIN_LOGIN, self.REPO
1243 TEST_USER_ADMIN_LOGIN, self.REPO
1209 )
1244 )
1210 self._compare_error(id_, expected, given=response.body)
1245 self._compare_error(id_, expected, given=response.body)
1211
1246
1212 def test_api_revoke_user_permission(self):
1247 def test_api_revoke_user_permission(self):
1213 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1248 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1214 repoid=self.REPO,
1249 repoid=self.REPO,
1215 userid=TEST_USER_ADMIN_LOGIN,)
1250 userid=TEST_USER_ADMIN_LOGIN,)
1216 response = api_call(self, params)
1251 response = api_call(self, params)
1217
1252
1218 expected = {
1253 expected = {
1219 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1254 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1220 TEST_USER_ADMIN_LOGIN, self.REPO
1255 TEST_USER_ADMIN_LOGIN, self.REPO
1221 ),
1256 ),
1222 'success': True
1257 'success': True
1223 }
1258 }
1224 self._compare_ok(id_, expected, given=response.body)
1259 self._compare_ok(id_, expected, given=response.body)
1225
1260
1226 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1261 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1227 def test_api_revoke_user_permission_exception_when_adding(self):
1262 def test_api_revoke_user_permission_exception_when_adding(self):
1228 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1263 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1229 repoid=self.REPO,
1264 repoid=self.REPO,
1230 userid=TEST_USER_ADMIN_LOGIN,)
1265 userid=TEST_USER_ADMIN_LOGIN,)
1231 response = api_call(self, params)
1266 response = api_call(self, params)
1232
1267
1233 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1268 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1234 TEST_USER_ADMIN_LOGIN, self.REPO
1269 TEST_USER_ADMIN_LOGIN, self.REPO
1235 )
1270 )
1236 self._compare_error(id_, expected, given=response.body)
1271 self._compare_error(id_, expected, given=response.body)
1237
1272
1238 @parameterized.expand([('none', 'repository.none'),
1273 @parameterized.expand([('none', 'repository.none'),
1239 ('read', 'repository.read'),
1274 ('read', 'repository.read'),
1240 ('write', 'repository.write'),
1275 ('write', 'repository.write'),
1241 ('admin', 'repository.admin')])
1276 ('admin', 'repository.admin')])
1242 def test_api_grant_users_group_permission(self, name, perm):
1277 def test_api_grant_users_group_permission(self, name, perm):
1243 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1278 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1244 repoid=self.REPO,
1279 repoid=self.REPO,
1245 usersgroupid=TEST_USER_GROUP,
1280 usersgroupid=TEST_USER_GROUP,
1246 perm=perm)
1281 perm=perm)
1247 response = api_call(self, params)
1282 response = api_call(self, params)
1248
1283
1249 ret = {
1284 ret = {
1250 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1285 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1251 perm, TEST_USER_GROUP, self.REPO
1286 perm, TEST_USER_GROUP, self.REPO
1252 ),
1287 ),
1253 'success': True
1288 'success': True
1254 }
1289 }
1255 expected = ret
1290 expected = ret
1256 self._compare_ok(id_, expected, given=response.body)
1291 self._compare_ok(id_, expected, given=response.body)
1257
1292
1258 def test_api_grant_users_group_permission_wrong_permission(self):
1293 def test_api_grant_users_group_permission_wrong_permission(self):
1259 perm = 'haha.no.permission'
1294 perm = 'haha.no.permission'
1260 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1295 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1261 repoid=self.REPO,
1296 repoid=self.REPO,
1262 usersgroupid=TEST_USER_GROUP,
1297 usersgroupid=TEST_USER_GROUP,
1263 perm=perm)
1298 perm=perm)
1264 response = api_call(self, params)
1299 response = api_call(self, params)
1265
1300
1266 expected = 'permission `%s` does not exist' % perm
1301 expected = 'permission `%s` does not exist' % perm
1267 self._compare_error(id_, expected, given=response.body)
1302 self._compare_error(id_, expected, given=response.body)
1268
1303
1269 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1304 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1270 def test_api_grant_users_group_permission_exception_when_adding(self):
1305 def test_api_grant_users_group_permission_exception_when_adding(self):
1271 perm = 'repository.read'
1306 perm = 'repository.read'
1272 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1307 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1273 repoid=self.REPO,
1308 repoid=self.REPO,
1274 usersgroupid=TEST_USER_GROUP,
1309 usersgroupid=TEST_USER_GROUP,
1275 perm=perm)
1310 perm=perm)
1276 response = api_call(self, params)
1311 response = api_call(self, params)
1277
1312
1278 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1313 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1279 TEST_USER_GROUP, self.REPO
1314 TEST_USER_GROUP, self.REPO
1280 )
1315 )
1281 self._compare_error(id_, expected, given=response.body)
1316 self._compare_error(id_, expected, given=response.body)
1282
1317
1283 def test_api_revoke_users_group_permission(self):
1318 def test_api_revoke_users_group_permission(self):
1284 RepoModel().grant_users_group_permission(repo=self.REPO,
1319 RepoModel().grant_users_group_permission(repo=self.REPO,
1285 group_name=TEST_USER_GROUP,
1320 group_name=TEST_USER_GROUP,
1286 perm='repository.read')
1321 perm='repository.read')
1287 Session().commit()
1322 Session().commit()
1288 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1323 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1289 repoid=self.REPO,
1324 repoid=self.REPO,
1290 usersgroupid=TEST_USER_GROUP,)
1325 usersgroupid=TEST_USER_GROUP,)
1291 response = api_call(self, params)
1326 response = api_call(self, params)
1292
1327
1293 expected = {
1328 expected = {
1294 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1329 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1295 TEST_USER_GROUP, self.REPO
1330 TEST_USER_GROUP, self.REPO
1296 ),
1331 ),
1297 'success': True
1332 'success': True
1298 }
1333 }
1299 self._compare_ok(id_, expected, given=response.body)
1334 self._compare_ok(id_, expected, given=response.body)
1300
1335
1301 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1336 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1302 def test_api_revoke_users_group_permission_exception_when_adding(self):
1337 def test_api_revoke_users_group_permission_exception_when_adding(self):
1303
1338
1304 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1339 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1305 repoid=self.REPO,
1340 repoid=self.REPO,
1306 usersgroupid=TEST_USER_GROUP,)
1341 usersgroupid=TEST_USER_GROUP,)
1307 response = api_call(self, params)
1342 response = api_call(self, params)
1308
1343
1309 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1344 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1310 TEST_USER_GROUP, self.REPO
1345 TEST_USER_GROUP, self.REPO
1311 )
1346 )
1312 self._compare_error(id_, expected, given=response.body)
1347 self._compare_error(id_, expected, given=response.body)
General Comments 0
You need to be logged in to leave comments. Login now