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