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