##// END OF EJS Templates
recursive forks detach...
marcink -
r3641:b4497964 beta
parent child Browse files
Show More
@@ -1,982 +1,985 b''
1 .. _api:
1 .. _api:
2
2
3 ===
3 ===
4 API
4 API
5 ===
5 ===
6
6
7
7
8 Starting from RhodeCode version 1.2 a simple API was implemented.
8 Starting from RhodeCode version 1.2 a simple API was implemented.
9 There's a single schema for calling all api methods. API is implemented
9 There's a single schema for calling all api methods. API is implemented
10 with JSON protocol both ways. An url to send API request to RhodeCode is
10 with JSON protocol both ways. An url to send API request to RhodeCode is
11 <your_server>/_admin/api
11 <your_server>/_admin/api
12
12
13 API ACCESS FOR WEB VIEWS
13 API ACCESS FOR WEB VIEWS
14 ++++++++++++++++++++++++
14 ++++++++++++++++++++++++
15
15
16 API access can also be turned on for each web view in RhodeCode that is
16 API access can also be turned on for each web view in RhodeCode that is
17 decorated with `@LoginRequired` decorator. To enable API access simple change
17 decorated with `@LoginRequired` decorator. To enable API access simple change
18 the standard login decorator to `@LoginRequired(api_access=True)`.
18 the standard login decorator to `@LoginRequired(api_access=True)`.
19 After this change, a rhodecode view can be accessed without login by adding a
19 After this change, a rhodecode view can be accessed without login by adding a
20 GET parameter `?api_key=<api_key>` to url. By default this is only
20 GET parameter `?api_key=<api_key>` to url. By default this is only
21 enabled on RSS/ATOM feed views.
21 enabled on RSS/ATOM feed views.
22
22
23
23
24 API ACCESS
24 API ACCESS
25 ++++++++++
25 ++++++++++
26
26
27 All clients are required to send JSON-RPC spec JSON data::
27 All clients are required to send JSON-RPC spec JSON data::
28
28
29 {
29 {
30 "id:"<id>",
30 "id:"<id>",
31 "api_key":"<api_key>",
31 "api_key":"<api_key>",
32 "method":"<method_name>",
32 "method":"<method_name>",
33 "args":{"<arg_key>":"<arg_val>"}
33 "args":{"<arg_key>":"<arg_val>"}
34 }
34 }
35
35
36 Example call for autopulling remotes repos using curl::
36 Example call for autopulling remotes repos using curl::
37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
37 curl https://server.com/_admin/api -X POST -H 'content-type:text/plain' --data-binary '{"id":1,"api_key":"xe7cdb2v278e4evbdf5vs04v832v0efvcbcve4a3","method":"pull","args":{"repo":"CPython"}}'
38
38
39 Simply provide
39 Simply provide
40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
40 - *id* A value of any type, which is used to match the response with the request that it is replying to.
41 - *api_key* for access and permission validation.
41 - *api_key* for access and permission validation.
42 - *method* is name of method to call
42 - *method* is name of method to call
43 - *args* is an key:value list of arguments to pass to method
43 - *args* is an key:value list of arguments to pass to method
44
44
45 .. note::
45 .. note::
46
46
47 api_key can be found in your user account page
47 api_key can be found in your user account page
48
48
49
49
50 RhodeCode API will return always a JSON-RPC response::
50 RhodeCode API will return always a JSON-RPC response::
51
51
52 {
52 {
53 "id":<id>, # matching id sent by request
53 "id":<id>, # matching id sent by request
54 "result": "<result>"|null, # JSON formatted result, null if any errors
54 "result": "<result>"|null, # JSON formatted result, null if any errors
55 "error": "null"|<error_message> # JSON formatted error (if any)
55 "error": "null"|<error_message> # JSON formatted error (if any)
56 }
56 }
57
57
58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
58 All responses from API will be `HTTP/1.0 200 OK`, if there's an error while
59 calling api *error* key from response will contain failure description
59 calling api *error* key from response will contain failure description
60 and result will be null.
60 and result will be null.
61
61
62
62
63 API CLIENT
63 API CLIENT
64 ++++++++++
64 ++++++++++
65
65
66 From version 1.4 RhodeCode adds a script that allows to easily
66 From version 1.4 RhodeCode adds a script that allows to easily
67 communicate with API. After installing RhodeCode a `rhodecode-api` script
67 communicate with API. After installing RhodeCode a `rhodecode-api` script
68 will be available.
68 will be available.
69
69
70 To get started quickly simply run::
70 To get started quickly simply run::
71
71
72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
72 rhodecode-api _create_config --apikey=<youapikey> --apihost=<rhodecode host>
73
73
74 This will create a file named .config in the directory you executed it storing
74 This will create a file named .config in the directory you executed it storing
75 json config file with credentials. You can skip this step and always provide
75 json config file with credentials. You can skip this step and always provide
76 both of the arguments to be able to communicate with server
76 both of the arguments to be able to communicate with server
77
77
78
78
79 after that simply run any api command for example get_repo::
79 after that simply run any api command for example get_repo::
80
80
81 rhodecode-api get_repo
81 rhodecode-api get_repo
82
82
83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
83 calling {"api_key": "<apikey>", "id": 75, "args": {}, "method": "get_repo"} to http://127.0.0.1:5000
84 rhodecode said:
84 rhodecode said:
85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
85 {'error': 'Missing non optional `repoid` arg in JSON DATA',
86 'id': 75,
86 'id': 75,
87 'result': None}
87 'result': None}
88
88
89 Ups looks like we forgot to add an argument
89 Ups looks like we forgot to add an argument
90
90
91 Let's try again now giving the repoid as parameters::
91 Let's try again now giving the repoid as parameters::
92
92
93 rhodecode-api get_repo repoid:rhodecode
93 rhodecode-api get_repo repoid:rhodecode
94
94
95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
95 calling {"api_key": "<apikey>", "id": 39, "args": {"repoid": "rhodecode"}, "method": "get_repo"} to http://127.0.0.1:5000
96 rhodecode said:
96 rhodecode said:
97 {'error': None,
97 {'error': None,
98 'id': 39,
98 'id': 39,
99 'result': <json data...>}
99 'result': <json data...>}
100
100
101
101
102
102
103 API METHODS
103 API METHODS
104 +++++++++++
104 +++++++++++
105
105
106
106
107 pull
107 pull
108 ----
108 ----
109
109
110 Pulls given repo from remote location. Can be used to automatically keep
110 Pulls given repo from remote location. Can be used to automatically keep
111 remote repos up to date. This command can be executed only using api_key
111 remote repos up to date. This command can be executed only using api_key
112 belonging to user with admin rights
112 belonging to user with admin rights
113
113
114 INPUT::
114 INPUT::
115
115
116 id : <id_for_response>
116 id : <id_for_response>
117 api_key : "<api_key>"
117 api_key : "<api_key>"
118 method : "pull"
118 method : "pull"
119 args : {
119 args : {
120 "repoid" : "<reponame or repo_id>"
120 "repoid" : "<reponame or repo_id>"
121 }
121 }
122
122
123 OUTPUT::
123 OUTPUT::
124
124
125 id : <id_given_in_input>
125 id : <id_given_in_input>
126 result : "Pulled from `<reponame>`"
126 result : "Pulled from `<reponame>`"
127 error : null
127 error : null
128
128
129
129
130 rescan_repos
130 rescan_repos
131 ------------
131 ------------
132
132
133 Dispatch rescan repositories action. If remove_obsolete is set
133 Dispatch rescan repositories action. If remove_obsolete is set
134 RhodeCode will delete repos that are in database but not in the filesystem.
134 RhodeCode will delete repos that are in database but not in the filesystem.
135 This command can be executed only using api_key belonging to user with admin
135 This command can be executed only using api_key belonging to user with admin
136 rights.
136 rights.
137
137
138 INPUT::
138 INPUT::
139
139
140 id : <id_for_response>
140 id : <id_for_response>
141 api_key : "<api_key>"
141 api_key : "<api_key>"
142 method : "rescan_repos"
142 method : "rescan_repos"
143 args : {
143 args : {
144 "remove_obsolete" : "<boolean = Optional(False)>"
144 "remove_obsolete" : "<boolean = Optional(False)>"
145 }
145 }
146
146
147 OUTPUT::
147 OUTPUT::
148
148
149 id : <id_given_in_input>
149 id : <id_given_in_input>
150 result : "{'added': [<list of names of added repos>],
150 result : "{'added': [<list of names of added repos>],
151 'removed': [<list of names of removed repos>]}"
151 'removed': [<list of names of removed repos>]}"
152 error : null
152 error : null
153
153
154
154
155 invalidate_cache
155 invalidate_cache
156 ----------------
156 ----------------
157
157
158 Invalidate cache for repository.
158 Invalidate cache for repository.
159 This command can be executed only using api_key belonging to user with admin
159 This command can be executed only using api_key belonging to user with admin
160 rights or regular user that have write or admin or write access to repository.
160 rights or regular user that have write or admin or write access to repository.
161
161
162 INPUT::
162 INPUT::
163
163
164 id : <id_for_response>
164 id : <id_for_response>
165 api_key : "<api_key>"
165 api_key : "<api_key>"
166 method : "invalidate_cache"
166 method : "invalidate_cache"
167 args : {
167 args : {
168 "repoid" : "<reponame or repo_id>"
168 "repoid" : "<reponame or repo_id>"
169 }
169 }
170
170
171 OUTPUT::
171 OUTPUT::
172
172
173 id : <id_given_in_input>
173 id : <id_given_in_input>
174 result : "Cache for repository `<reponame>` was invalidated: invalidated cache keys: <list_of_cache_keys>"
174 result : "Cache for repository `<reponame>` was invalidated: invalidated cache keys: <list_of_cache_keys>"
175 error : null
175 error : null
176
176
177 lock
177 lock
178 ----
178 ----
179
179
180 Set locking state on given repository by given user. If userid param is skipped
180 Set locking state on given repository by given user. If userid param is skipped
181 , then it is set to id of user whos calling this method. 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 "active":"<bool> = Optional(True)"
522 "active":"<bool> = Optional(True)"
523 }
523 }
524
524
525 OUTPUT::
525 OUTPUT::
526
526
527 id : <id_given_in_input>
527 id : <id_given_in_input>
528 result: {
528 result: {
529 "msg": "created new user group `<groupname>`",
529 "msg": "created new user group `<groupname>`",
530 "users_group": {
530 "users_group": {
531 "users_group_id" : "<id>",
531 "users_group_id" : "<id>",
532 "group_name" : "<groupname>",
532 "group_name" : "<groupname>",
533 "active": "<bool>",
533 "active": "<bool>",
534 },
534 },
535 }
535 }
536 error: null
536 error: null
537
537
538
538
539 add_user_to_users_group
539 add_user_to_users_group
540 -----------------------
540 -----------------------
541
541
542 Adds a user to a user group. If user exists in that group success will be
542 Adds a user to a user group. If user exists in that group success will be
543 `false`. This command can be executed only using api_key
543 `false`. This command can be executed only using api_key
544 belonging to user with admin rights
544 belonging to user with admin rights
545
545
546
546
547 INPUT::
547 INPUT::
548
548
549 id : <id_for_response>
549 id : <id_for_response>
550 api_key : "<api_key>"
550 api_key : "<api_key>"
551 method : "add_user_users_group"
551 method : "add_user_users_group"
552 args: {
552 args: {
553 "usersgroupid" : "<user group id or name>",
553 "usersgroupid" : "<user group id or name>",
554 "userid" : "<user_id or username>",
554 "userid" : "<user_id or username>",
555 }
555 }
556
556
557 OUTPUT::
557 OUTPUT::
558
558
559 id : <id_given_in_input>
559 id : <id_given_in_input>
560 result: {
560 result: {
561 "success": True|False # depends on if member is in group
561 "success": True|False # depends on if member is in group
562 "msg": "added member `<username>` to user group `<groupname>` |
562 "msg": "added member `<username>` to user group `<groupname>` |
563 User is already in that group"
563 User is already in that group"
564 }
564 }
565 error: null
565 error: null
566
566
567
567
568 remove_user_from_users_group
568 remove_user_from_users_group
569 ----------------------------
569 ----------------------------
570
570
571 Removes a user from a user group. If user is not in given group success will
571 Removes a user from a user group. If user is not in given group success will
572 be `false`. This command can be executed only
572 be `false`. This command can be executed only
573 using api_key belonging to user with admin rights
573 using api_key belonging to user with admin rights
574
574
575
575
576 INPUT::
576 INPUT::
577
577
578 id : <id_for_response>
578 id : <id_for_response>
579 api_key : "<api_key>"
579 api_key : "<api_key>"
580 method : "remove_user_from_users_group"
580 method : "remove_user_from_users_group"
581 args: {
581 args: {
582 "usersgroupid" : "<user group id or name>",
582 "usersgroupid" : "<user group id or name>",
583 "userid" : "<user_id or username>",
583 "userid" : "<user_id or username>",
584 }
584 }
585
585
586 OUTPUT::
586 OUTPUT::
587
587
588 id : <id_given_in_input>
588 id : <id_given_in_input>
589 result: {
589 result: {
590 "success": True|False, # depends on if member is in group
590 "success": True|False, # depends on if member is in group
591 "msg": "removed member <username> from user group <groupname> |
591 "msg": "removed member <username> from user group <groupname> |
592 User wasn't in group"
592 User wasn't in group"
593 }
593 }
594 error: null
594 error: null
595
595
596
596
597 get_repo
597 get_repo
598 --------
598 --------
599
599
600 Gets an existing repository by it's name or repository_id. Members will return
600 Gets an existing repository by it's name or repository_id. Members will return
601 either users_group or user associated to that repository. This command can be
601 either users_group or user associated to that repository. This command can be
602 executed only using api_key belonging to user with admin
602 executed only using api_key belonging to user with admin
603 rights or regular user that have at least read access to repository.
603 rights or regular user that have at least read access to repository.
604
604
605
605
606 INPUT::
606 INPUT::
607
607
608 id : <id_for_response>
608 id : <id_for_response>
609 api_key : "<api_key>"
609 api_key : "<api_key>"
610 method : "get_repo"
610 method : "get_repo"
611 args: {
611 args: {
612 "repoid" : "<reponame or repo_id>"
612 "repoid" : "<reponame or repo_id>"
613 }
613 }
614
614
615 OUTPUT::
615 OUTPUT::
616
616
617 id : <id_given_in_input>
617 id : <id_given_in_input>
618 result: None if repository does not exist or
618 result: None if repository does not exist or
619 {
619 {
620 "repo_id" : "<repo_id>",
620 "repo_id" : "<repo_id>",
621 "repo_name" : "<reponame>"
621 "repo_name" : "<reponame>"
622 "repo_type" : "<repo_type>",
622 "repo_type" : "<repo_type>",
623 "clone_uri" : "<clone_uri>",
623 "clone_uri" : "<clone_uri>",
624 "enable_downloads": "<bool>",
624 "enable_downloads": "<bool>",
625 "enable_locking": "<bool>",
625 "enable_locking": "<bool>",
626 "enable_statistics": "<bool>",
626 "enable_statistics": "<bool>",
627 "private": "<bool>",
627 "private": "<bool>",
628 "created_on" : "<date_time_created>",
628 "created_on" : "<date_time_created>",
629 "description" : "<description>",
629 "description" : "<description>",
630 "landing_rev": "<landing_rev>",
630 "landing_rev": "<landing_rev>",
631 "last_changeset": {
631 "last_changeset": {
632 "author": "<full_author>",
632 "author": "<full_author>",
633 "date": "<date_time_of_commit>",
633 "date": "<date_time_of_commit>",
634 "message": "<commit_message>",
634 "message": "<commit_message>",
635 "raw_id": "<raw_id>",
635 "raw_id": "<raw_id>",
636 "revision": "<numeric_revision>",
636 "revision": "<numeric_revision>",
637 "short_id": "<short_id>"
637 "short_id": "<short_id>"
638 }
638 }
639 "owner": "<repo_owner>",
639 "owner": "<repo_owner>",
640 "fork_of": "<name_of_fork_parent>",
640 "fork_of": "<name_of_fork_parent>",
641 "members" : [
641 "members" : [
642 {
642 {
643 "type": "user",
643 "type": "user",
644 "user_id" : "<user_id>",
644 "user_id" : "<user_id>",
645 "username" : "<username>",
645 "username" : "<username>",
646 "firstname": "<firstname>",
646 "firstname": "<firstname>",
647 "lastname" : "<lastname>",
647 "lastname" : "<lastname>",
648 "email" : "<email>",
648 "email" : "<email>",
649 "emails": "<list_of_all_additional_emails>",
649 "emails": "<list_of_all_additional_emails>",
650 "active" : "<bool>",
650 "active" : "<bool>",
651 "admin" :Β  "<bool>",
651 "admin" :Β  "<bool>",
652 "ldap_dn" : "<ldap_dn>",
652 "ldap_dn" : "<ldap_dn>",
653 "last_login": "<last_login>",
653 "last_login": "<last_login>",
654 "permission" : "repository.(read|write|admin)"
654 "permission" : "repository.(read|write|admin)"
655 },
655 },
656 …
656 …
657 {
657 {
658 "type": "users_group",
658 "type": "users_group",
659 "id" : "<usersgroupid>",
659 "id" : "<usersgroupid>",
660 "name" : "<usersgroupname>",
660 "name" : "<usersgroupname>",
661 "active": "<bool>",
661 "active": "<bool>",
662 "permission" : "repository.(read|write|admin)"
662 "permission" : "repository.(read|write|admin)"
663 },
663 },
664 …
664 …
665 ]
665 ]
666 "followers": [
666 "followers": [
667 {
667 {
668 "user_id" : "<user_id>",
668 "user_id" : "<user_id>",
669 "username" : "<username>",
669 "username" : "<username>",
670 "firstname": "<firstname>",
670 "firstname": "<firstname>",
671 "lastname" : "<lastname>",
671 "lastname" : "<lastname>",
672 "email" : "<email>",
672 "email" : "<email>",
673 "emails": "<list_of_all_additional_emails>",
673 "emails": "<list_of_all_additional_emails>",
674 "ip_addresses": "<list_of_ip_addresses_for_user>",
674 "ip_addresses": "<list_of_ip_addresses_for_user>",
675 "active" : "<bool>",
675 "active" : "<bool>",
676 "admin" :Β  "<bool>",
676 "admin" :Β  "<bool>",
677 "ldap_dn" : "<ldap_dn>",
677 "ldap_dn" : "<ldap_dn>",
678 "last_login": "<last_login>",
678 "last_login": "<last_login>",
679 },
679 },
680 …
680 …
681 ]
681 ]
682 }
682 }
683 error: null
683 error: null
684
684
685
685
686 get_repos
686 get_repos
687 ---------
687 ---------
688
688
689 Lists all existing repositories. This command can be executed only using
689 Lists all existing repositories. This command can be executed only using
690 api_key belonging to user with admin rights or regular user that have
690 api_key belonging to user with admin rights or regular user that have
691 admin, write or read access to repository.
691 admin, write or read access to repository.
692
692
693
693
694 INPUT::
694 INPUT::
695
695
696 id : <id_for_response>
696 id : <id_for_response>
697 api_key : "<api_key>"
697 api_key : "<api_key>"
698 method : "get_repos"
698 method : "get_repos"
699 args: { }
699 args: { }
700
700
701 OUTPUT::
701 OUTPUT::
702
702
703 id : <id_given_in_input>
703 id : <id_given_in_input>
704 result: [
704 result: [
705 {
705 {
706 "repo_id" : "<repo_id>",
706 "repo_id" : "<repo_id>",
707 "repo_name" : "<reponame>"
707 "repo_name" : "<reponame>"
708 "repo_type" : "<repo_type>",
708 "repo_type" : "<repo_type>",
709 "clone_uri" : "<clone_uri>",
709 "clone_uri" : "<clone_uri>",
710 "private": : "<bool>",
710 "private": : "<bool>",
711 "created_on" : "<datetimecreated>",
711 "created_on" : "<datetimecreated>",
712 "description" : "<description>",
712 "description" : "<description>",
713 "landing_rev": "<landing_rev>",
713 "landing_rev": "<landing_rev>",
714 "owner": "<repo_owner>",
714 "owner": "<repo_owner>",
715 "fork_of": "<name_of_fork_parent>",
715 "fork_of": "<name_of_fork_parent>",
716 "enable_downloads": "<bool>",
716 "enable_downloads": "<bool>",
717 "enable_locking": "<bool>",
717 "enable_locking": "<bool>",
718 "enable_statistics": "<bool>",
718 "enable_statistics": "<bool>",
719 },
719 },
720 …
720 …
721 ]
721 ]
722 error: null
722 error: null
723
723
724
724
725 get_repo_nodes
725 get_repo_nodes
726 --------------
726 --------------
727
727
728 returns a list of nodes and it's children in a flat list for a given path
728 returns a list of nodes and it's children in a flat list for a given path
729 at given revision. It's possible to specify ret_type to show only `files` or
729 at given revision. It's possible to specify ret_type to show only `files` or
730 `dirs`. This command can be executed only using api_key belonging to user
730 `dirs`. This command can be executed only using api_key belonging to user
731 with admin rights
731 with admin rights
732
732
733
733
734 INPUT::
734 INPUT::
735
735
736 id : <id_for_response>
736 id : <id_for_response>
737 api_key : "<api_key>"
737 api_key : "<api_key>"
738 method : "get_repo_nodes"
738 method : "get_repo_nodes"
739 args: {
739 args: {
740 "repoid" : "<reponame or repo_id>"
740 "repoid" : "<reponame or repo_id>"
741 "revision" : "<revision>",
741 "revision" : "<revision>",
742 "root_path" : "<root_path>",
742 "root_path" : "<root_path>",
743 "ret_type" : "<ret_type> = Optional('all')"
743 "ret_type" : "<ret_type> = Optional('all')"
744 }
744 }
745
745
746 OUTPUT::
746 OUTPUT::
747
747
748 id : <id_given_in_input>
748 id : <id_given_in_input>
749 result: [
749 result: [
750 {
750 {
751 "name" : "<name>"
751 "name" : "<name>"
752 "type" : "<type>",
752 "type" : "<type>",
753 },
753 },
754 …
754 …
755 ]
755 ]
756 error: null
756 error: null
757
757
758
758
759 create_repo
759 create_repo
760 -----------
760 -----------
761
761
762 Creates a repository. If repository name contains "/", all needed repository
762 Creates a repository. If repository name contains "/", all needed repository
763 groups will be created. For example "foo/bar/baz" will create groups
763 groups will be created. For example "foo/bar/baz" will create groups
764 "foo", "bar" (with "foo" as parent), and create "baz" repository with
764 "foo", "bar" (with "foo" as parent), and create "baz" repository with
765 "bar" as group. This command can be executed only using api_key belonging to user with admin
765 "bar" as group. This command can be executed only using api_key belonging to user with admin
766 rights or regular user that have create repository permission. Regular users
766 rights or regular user that have create repository permission. Regular users
767 cannot specify owner parameter
767 cannot specify owner parameter
768
768
769
769
770 INPUT::
770 INPUT::
771
771
772 id : <id_for_response>
772 id : <id_for_response>
773 api_key : "<api_key>"
773 api_key : "<api_key>"
774 method : "create_repo"
774 method : "create_repo"
775 args: {
775 args: {
776 "repo_name" : "<reponame>",
776 "repo_name" : "<reponame>",
777 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
777 "owner" : "<onwer_name_or_id = Optional(=apiuser)>",
778 "repo_type" : "<repo_type> = Optional('hg')",
778 "repo_type" : "<repo_type> = Optional('hg')",
779 "description" : "<description> = Optional('')",
779 "description" : "<description> = Optional('')",
780 "private" : "<bool> = Optional(False)",
780 "private" : "<bool> = Optional(False)",
781 "clone_uri" : "<clone_uri> = Optional(None)",
781 "clone_uri" : "<clone_uri> = Optional(None)",
782 "landing_rev" : "<landing_rev> = Optional('tip')",
782 "landing_rev" : "<landing_rev> = Optional('tip')",
783 "enable_downloads": "<bool> = Optional(False)",
783 "enable_downloads": "<bool> = Optional(False)",
784 "enable_locking": "<bool> = Optional(False)",
784 "enable_locking": "<bool> = Optional(False)",
785 "enable_statistics": "<bool> = Optional(False)",
785 "enable_statistics": "<bool> = Optional(False)",
786 }
786 }
787
787
788 OUTPUT::
788 OUTPUT::
789
789
790 id : <id_given_in_input>
790 id : <id_given_in_input>
791 result: {
791 result: {
792 "msg": "Created new repository `<reponame>`",
792 "msg": "Created new repository `<reponame>`",
793 "repo": {
793 "repo": {
794 "repo_id" : "<repo_id>",
794 "repo_id" : "<repo_id>",
795 "repo_name" : "<reponame>"
795 "repo_name" : "<reponame>"
796 "repo_type" : "<repo_type>",
796 "repo_type" : "<repo_type>",
797 "clone_uri" : "<clone_uri>",
797 "clone_uri" : "<clone_uri>",
798 "private": : "<bool>",
798 "private": : "<bool>",
799 "created_on" : "<datetimecreated>",
799 "created_on" : "<datetimecreated>",
800 "description" : "<description>",
800 "description" : "<description>",
801 "landing_rev": "<landing_rev>",
801 "landing_rev": "<landing_rev>",
802 "owner": "<username or user_id>",
802 "owner": "<username or user_id>",
803 "fork_of": "<name_of_fork_parent>",
803 "fork_of": "<name_of_fork_parent>",
804 "enable_downloads": "<bool>",
804 "enable_downloads": "<bool>",
805 "enable_locking": "<bool>",
805 "enable_locking": "<bool>",
806 "enable_statistics": "<bool>",
806 "enable_statistics": "<bool>",
807 },
807 },
808 }
808 }
809 error: null
809 error: null
810
810
811
811
812 fork_repo
812 fork_repo
813 ---------
813 ---------
814
814
815 Creates a fork of given repo. In case of using celery this will
815 Creates a fork of given repo. In case of using celery this will
816 immidiatelly return success message, while fork is going to be created
816 immidiatelly return success message, while fork is going to be created
817 asynchronous. This command can be executed only using api_key belonging to
817 asynchronous. This command can be executed only using api_key belonging to
818 user with admin rights or regular user that have fork permission, and at least
818 user with admin rights or regular user that have fork permission, and at least
819 read access to forking repository. Regular users cannot specify owner parameter.
819 read access to forking repository. Regular users cannot specify owner parameter.
820
820
821
821
822 INPUT::
822 INPUT::
823
823
824 id : <id_for_response>
824 id : <id_for_response>
825 api_key : "<api_key>"
825 api_key : "<api_key>"
826 method : "fork_repo"
826 method : "fork_repo"
827 args: {
827 args: {
828 "repoid" : "<reponame or repo_id>",
828 "repoid" : "<reponame or repo_id>",
829 "fork_name": "<forkname>",
829 "fork_name": "<forkname>",
830 "owner": "<username or user_id = Optional(=apiuser)>",
830 "owner": "<username or user_id = Optional(=apiuser)>",
831 "description": "<description>",
831 "description": "<description>",
832 "copy_permissions": "<bool>",
832 "copy_permissions": "<bool>",
833 "private": "<bool>",
833 "private": "<bool>",
834 "landing_rev": "<landing_rev>"
834 "landing_rev": "<landing_rev>"
835
835
836 }
836 }
837
837
838 OUTPUT::
838 OUTPUT::
839
839
840 id : <id_given_in_input>
840 id : <id_given_in_input>
841 result: {
841 result: {
842 "msg": "Created fork of `<reponame>` as `<forkname>`",
842 "msg": "Created fork of `<reponame>` as `<forkname>`",
843 "success": true
843 "success": true
844 }
844 }
845 error: null
845 error: null
846
846
847
847
848 delete_repo
848 delete_repo
849 -----------
849 -----------
850
850
851 Deletes a repository. This command can be executed only using api_key belonging to user with admin
851 Deletes a repository. This command can be executed only using api_key belonging
852 rights or regular user that have admin access to repository.
852 to user with admin rights or regular user that have admin access to repository.
853 When `forks` param is set it's possible to detach or delete forks of deleting
854 repository
853
855
854
856
855 INPUT::
857 INPUT::
856
858
857 id : <id_for_response>
859 id : <id_for_response>
858 api_key : "<api_key>"
860 api_key : "<api_key>"
859 method : "delete_repo"
861 method : "delete_repo"
860 args: {
862 args: {
861 "repoid" : "<reponame or repo_id>"
863 "repoid" : "<reponame or repo_id>",
864 "forks" : "`delete` or `detach` = Optional(None)"
862 }
865 }
863
866
864 OUTPUT::
867 OUTPUT::
865
868
866 id : <id_given_in_input>
869 id : <id_given_in_input>
867 result: {
870 result: {
868 "msg": "Deleted repository `<reponame>`",
871 "msg": "Deleted repository `<reponame>`",
869 "success": true
872 "success": true
870 }
873 }
871 error: null
874 error: null
872
875
873
876
874 grant_user_permission
877 grant_user_permission
875 ---------------------
878 ---------------------
876
879
877 Grant permission for user on given repository, or update existing one
880 Grant permission for user on given repository, or update existing one
878 if found. This command can be executed only using api_key belonging to user
881 if found. This command can be executed only using api_key belonging to user
879 with admin rights.
882 with admin rights.
880
883
881
884
882 INPUT::
885 INPUT::
883
886
884 id : <id_for_response>
887 id : <id_for_response>
885 api_key : "<api_key>"
888 api_key : "<api_key>"
886 method : "grant_user_permission"
889 method : "grant_user_permission"
887 args: {
890 args: {
888 "repoid" : "<reponame or repo_id>"
891 "repoid" : "<reponame or repo_id>"
889 "userid" : "<username or user_id>"
892 "userid" : "<username or user_id>"
890 "perm" : "(repository.(none|read|write|admin))",
893 "perm" : "(repository.(none|read|write|admin))",
891 }
894 }
892
895
893 OUTPUT::
896 OUTPUT::
894
897
895 id : <id_given_in_input>
898 id : <id_given_in_input>
896 result: {
899 result: {
897 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
900 "msg" : "Granted perm: `<perm>` for user: `<username>` in repo: `<reponame>`",
898 "success": true
901 "success": true
899 }
902 }
900 error: null
903 error: null
901
904
902
905
903 revoke_user_permission
906 revoke_user_permission
904 ----------------------
907 ----------------------
905
908
906 Revoke permission for user on given repository. This command can be executed
909 Revoke permission for user on given repository. This command can be executed
907 only using api_key belonging to user with admin rights.
910 only using api_key belonging to user with admin rights.
908
911
909
912
910 INPUT::
913 INPUT::
911
914
912 id : <id_for_response>
915 id : <id_for_response>
913 api_key : "<api_key>"
916 api_key : "<api_key>"
914 method : "revoke_user_permission"
917 method : "revoke_user_permission"
915 args: {
918 args: {
916 "repoid" : "<reponame or repo_id>"
919 "repoid" : "<reponame or repo_id>"
917 "userid" : "<username or user_id>"
920 "userid" : "<username or user_id>"
918 }
921 }
919
922
920 OUTPUT::
923 OUTPUT::
921
924
922 id : <id_given_in_input>
925 id : <id_given_in_input>
923 result: {
926 result: {
924 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
927 "msg" : "Revoked perm for user: `<username>` in repo: `<reponame>`",
925 "success": true
928 "success": true
926 }
929 }
927 error: null
930 error: null
928
931
929
932
930 grant_users_group_permission
933 grant_users_group_permission
931 ----------------------------
934 ----------------------------
932
935
933 Grant permission for user group on given repository, or update
936 Grant permission for user group on given repository, or update
934 existing one if found. This command can be executed only using
937 existing one if found. This command can be executed only using
935 api_key belonging to user with admin rights.
938 api_key belonging to user with admin rights.
936
939
937
940
938 INPUT::
941 INPUT::
939
942
940 id : <id_for_response>
943 id : <id_for_response>
941 api_key : "<api_key>"
944 api_key : "<api_key>"
942 method : "grant_users_group_permission"
945 method : "grant_users_group_permission"
943 args: {
946 args: {
944 "repoid" : "<reponame or repo_id>"
947 "repoid" : "<reponame or repo_id>"
945 "usersgroupid" : "<user group id or name>"
948 "usersgroupid" : "<user group id or name>"
946 "perm" : "(repository.(none|read|write|admin))",
949 "perm" : "(repository.(none|read|write|admin))",
947 }
950 }
948
951
949 OUTPUT::
952 OUTPUT::
950
953
951 id : <id_given_in_input>
954 id : <id_given_in_input>
952 result: {
955 result: {
953 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
956 "msg" : "Granted perm: `<perm>` for group: `<usersgroupname>` in repo: `<reponame>`",
954 "success": true
957 "success": true
955 }
958 }
956 error: null
959 error: null
957
960
958
961
959 revoke_users_group_permission
962 revoke_users_group_permission
960 -----------------------------
963 -----------------------------
961
964
962 Revoke permission for user group on given repository.This command can be
965 Revoke permission for user group on given repository.This command can be
963 executed only using api_key belonging to user with admin rights.
966 executed only using api_key belonging to user with admin rights.
964
967
965 INPUT::
968 INPUT::
966
969
967 id : <id_for_response>
970 id : <id_for_response>
968 api_key : "<api_key>"
971 api_key : "<api_key>"
969 method : "revoke_users_group_permission"
972 method : "revoke_users_group_permission"
970 args: {
973 args: {
971 "repoid" : "<reponame or repo_id>"
974 "repoid" : "<reponame or repo_id>"
972 "usersgroupid" : "<user group id or name>"
975 "usersgroupid" : "<user group id or name>"
973 }
976 }
974
977
975 OUTPUT::
978 OUTPUT::
976
979
977 id : <id_given_in_input>
980 id : <id_given_in_input>
978 result: {
981 result: {
979 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
982 "msg" : "Revoked perm for group: `<usersgroupname>` in repo: `<reponame>`",
980 "success": true
983 "success": true
981 }
984 }
982 error: null
985 error: null
@@ -1,617 +1,606 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
43 from rhodecode.lib.utils import invalidate_cache, action_logger, repo_name_slug
44 from rhodecode.lib.helpers import get_token
44 from rhodecode.lib.helpers import get_token
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
47 RhodeCodeSetting, RepositoryField
47 RhodeCodeSetting, RepositoryField
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
49 from rhodecode.model.scm import ScmModel, GroupList
49 from rhodecode.model.scm import ScmModel, GroupList
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from sqlalchemy.sql.expression import func
52 from sqlalchemy.sql.expression import func
53 from rhodecode.lib.exceptions import AttachedForksError
53
54
54 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
55
56
56
57
57 class ReposController(BaseRepoController):
58 class ReposController(BaseRepoController):
58 """
59 """
59 REST Controller styled on the Atom Publishing Protocol"""
60 REST Controller styled on the Atom Publishing Protocol"""
60 # To properly map this controller, ensure your config/routing.py
61 # To properly map this controller, ensure your config/routing.py
61 # file has a resource setup:
62 # file has a resource setup:
62 # map.resource('repo', 'repos')
63 # map.resource('repo', 'repos')
63
64
64 @LoginRequired()
65 @LoginRequired()
65 def __before__(self):
66 def __before__(self):
66 c.admin_user = session.get('admin_user')
67 c.admin_user = session.get('admin_user')
67 c.admin_username = session.get('admin_username')
68 c.admin_username = session.get('admin_username')
68 super(ReposController, self).__before__()
69 super(ReposController, self).__before__()
69
70
70 def __load_defaults(self):
71 def __load_defaults(self):
71 acl_groups = GroupList(RepoGroup.query().all(),
72 acl_groups = GroupList(RepoGroup.query().all(),
72 perm_set=['group.write', 'group.admin'])
73 perm_set=['group.write', 'group.admin'])
73 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
74 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
74 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
75 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
75
76
76 repo_model = RepoModel()
77 repo_model = RepoModel()
77 c.users_array = repo_model.get_users_js()
78 c.users_array = repo_model.get_users_js()
78 c.users_groups_array = repo_model.get_users_groups_js()
79 c.users_groups_array = repo_model.get_users_groups_js()
79 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
80 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
80 c.landing_revs_choices = choices
81 c.landing_revs_choices = choices
81
82
82 def __load_data(self, repo_name=None):
83 def __load_data(self, repo_name=None):
83 """
84 """
84 Load defaults settings for edit, and update
85 Load defaults settings for edit, and update
85
86
86 :param repo_name:
87 :param repo_name:
87 """
88 """
88 self.__load_defaults()
89 self.__load_defaults()
89
90
90 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
91 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
91 repo = db_repo.scm_instance
92 repo = db_repo.scm_instance
92
93
93 if c.repo_info is None:
94 if c.repo_info is None:
94 h.not_mapped_error(repo_name)
95 h.not_mapped_error(repo_name)
95 return redirect(url('repos'))
96 return redirect(url('repos'))
96
97
97 ##override defaults for exact repo info here git/hg etc
98 ##override defaults for exact repo info here git/hg etc
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 c.landing_revs_choices = choices
100 c.landing_revs_choices = choices
100
101
101 c.default_user_id = User.get_by_username('default').user_id
102 c.default_user_id = User.get_by_username('default').user_id
102 c.in_public_journal = UserFollowing.query()\
103 c.in_public_journal = UserFollowing.query()\
103 .filter(UserFollowing.user_id == c.default_user_id)\
104 .filter(UserFollowing.user_id == c.default_user_id)\
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105
106
106 if c.repo_info.stats:
107 if c.repo_info.stats:
107 # this is on what revision we ended up so we add +1 for count
108 # this is on what revision we ended up so we add +1 for count
108 last_rev = c.repo_info.stats.stat_on_revision + 1
109 last_rev = c.repo_info.stats.stat_on_revision + 1
109 else:
110 else:
110 last_rev = 0
111 last_rev = 0
111 c.stats_revision = last_rev
112 c.stats_revision = last_rev
112
113
113 c.repo_last_rev = repo.count() if repo.revisions else 0
114 c.repo_last_rev = repo.count() if repo.revisions else 0
114
115
115 if last_rev == 0 or c.repo_last_rev == 0:
116 if last_rev == 0 or c.repo_last_rev == 0:
116 c.stats_percentage = 0
117 c.stats_percentage = 0
117 else:
118 else:
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 c.repo_last_rev) * 100)
120 c.repo_last_rev) * 100)
120
121
121 c.repo_fields = RepositoryField.query()\
122 c.repo_fields = RepositoryField.query()\
122 .filter(RepositoryField.repository == db_repo).all()
123 .filter(RepositoryField.repository == db_repo).all()
123
124
124 defaults = RepoModel()._get_defaults(repo_name)
125 defaults = RepoModel()._get_defaults(repo_name)
125
126
126 c.repos_list = [('', _('--REMOVE FORK--'))]
127 c.repos_list = [('', _('--REMOVE FORK--'))]
127 c.repos_list += [(x.repo_id, x.repo_name) for x in
128 c.repos_list += [(x.repo_id, x.repo_name) for x in
128 Repository.query().order_by(Repository.repo_name).all()
129 Repository.query().order_by(Repository.repo_name).all()
129 if x.repo_id != c.repo_info.repo_id]
130 if x.repo_id != c.repo_info.repo_id]
130
131
131 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
132 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
132 return defaults
133 return defaults
133
134
134 @HasPermissionAllDecorator('hg.admin')
135 @HasPermissionAllDecorator('hg.admin')
135 def index(self, format='html'):
136 def index(self, format='html'):
136 """GET /repos: All items in the collection"""
137 """GET /repos: All items in the collection"""
137 # url('repos')
138 # url('repos')
138
139
139 c.repos_list = Repository.query()\
140 c.repos_list = Repository.query()\
140 .order_by(func.lower(Repository.repo_name))\
141 .order_by(func.lower(Repository.repo_name))\
141 .all()
142 .all()
142
143
143 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
144 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
144 admin=True,
145 admin=True,
145 super_user_actions=True)
146 super_user_actions=True)
146 #json used to render the grid
147 #json used to render the grid
147 c.data = json.dumps(repos_data)
148 c.data = json.dumps(repos_data)
148
149
149 return render('admin/repos/repos.html')
150 return render('admin/repos/repos.html')
150
151
151 @NotAnonymous()
152 @NotAnonymous()
152 def create(self):
153 def create(self):
153 """
154 """
154 POST /repos: Create a new item"""
155 POST /repos: Create a new item"""
155 # url('repos')
156 # url('repos')
156
157
157 self.__load_defaults()
158 self.__load_defaults()
158 form_result = {}
159 form_result = {}
159 try:
160 try:
160 form_result = RepoForm(repo_groups=c.repo_groups_choices,
161 form_result = RepoForm(repo_groups=c.repo_groups_choices,
161 landing_revs=c.landing_revs_choices)()\
162 landing_revs=c.landing_revs_choices)()\
162 .to_python(dict(request.POST))
163 .to_python(dict(request.POST))
163
164
164 new_repo = RepoModel().create(form_result,
165 new_repo = RepoModel().create(form_result,
165 self.rhodecode_user.user_id)
166 self.rhodecode_user.user_id)
166 if form_result['clone_uri']:
167 if form_result['clone_uri']:
167 h.flash(_('Created repository %s from %s') \
168 h.flash(_('Created repository %s from %s') \
168 % (form_result['repo_name'], form_result['clone_uri']),
169 % (form_result['repo_name'], form_result['clone_uri']),
169 category='success')
170 category='success')
170 else:
171 else:
171 repo_url = h.link_to(form_result['repo_name'],
172 repo_url = h.link_to(form_result['repo_name'],
172 h.url('summary_home', repo_name=form_result['repo_name']))
173 h.url('summary_home', repo_name=form_result['repo_name']))
173 h.flash(h.literal(_('Created repository %s') % repo_url),
174 h.flash(h.literal(_('Created repository %s') % repo_url),
174 category='success')
175 category='success')
175
176
176 if request.POST.get('user_created'):
177 if request.POST.get('user_created'):
177 # created by regular non admin user
178 # created by regular non admin user
178 action_logger(self.rhodecode_user, 'user_created_repo',
179 action_logger(self.rhodecode_user, 'user_created_repo',
179 form_result['repo_name_full'], self.ip_addr,
180 form_result['repo_name_full'], self.ip_addr,
180 self.sa)
181 self.sa)
181 else:
182 else:
182 action_logger(self.rhodecode_user, 'admin_created_repo',
183 action_logger(self.rhodecode_user, 'admin_created_repo',
183 form_result['repo_name_full'], self.ip_addr,
184 form_result['repo_name_full'], self.ip_addr,
184 self.sa)
185 self.sa)
185 Session().commit()
186 Session().commit()
186 except formencode.Invalid, errors:
187 except formencode.Invalid, errors:
187 return htmlfill.render(
188 return htmlfill.render(
188 render('admin/repos/repo_add.html'),
189 render('admin/repos/repo_add.html'),
189 defaults=errors.value,
190 defaults=errors.value,
190 errors=errors.error_dict or {},
191 errors=errors.error_dict or {},
191 prefix_error=False,
192 prefix_error=False,
192 encoding="UTF-8")
193 encoding="UTF-8")
193
194
194 except Exception:
195 except Exception:
195 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
196 msg = _('error occurred during creation of repository %s') \
197 msg = _('error occurred during creation of repository %s') \
197 % form_result.get('repo_name')
198 % form_result.get('repo_name')
198 h.flash(msg, category='error')
199 h.flash(msg, category='error')
199 if c.rhodecode_user.is_admin:
200 if c.rhodecode_user.is_admin:
200 return redirect(url('repos'))
201 return redirect(url('repos'))
201 return redirect(url('home'))
202 return redirect(url('home'))
202 #redirect to our new repo !
203 #redirect to our new repo !
203 return redirect(url('summary_home', repo_name=new_repo.repo_name))
204 return redirect(url('summary_home', repo_name=new_repo.repo_name))
204
205
205 @NotAnonymous()
206 @NotAnonymous()
206 def create_repository(self):
207 def create_repository(self):
207 """GET /_admin/create_repository: Form to create a new item"""
208 """GET /_admin/create_repository: Form to create a new item"""
208 new_repo = request.GET.get('repo', '')
209 new_repo = request.GET.get('repo', '')
209 parent_group = request.GET.get('parent_group')
210 parent_group = request.GET.get('parent_group')
210 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
211 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
211 #you're not super admin nor have global create permissions,
212 #you're not super admin nor have global create permissions,
212 #but maybe you have at least write permission to a parent group ?
213 #but maybe you have at least write permission to a parent group ?
213 _gr = RepoGroup.get(parent_group)
214 _gr = RepoGroup.get(parent_group)
214 gr_name = _gr.group_name if _gr else None
215 gr_name = _gr.group_name if _gr else None
215 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
216 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
216 raise HTTPForbidden
217 raise HTTPForbidden
217
218
218 acl_groups = GroupList(RepoGroup.query().all(),
219 acl_groups = GroupList(RepoGroup.query().all(),
219 perm_set=['group.write', 'group.admin'])
220 perm_set=['group.write', 'group.admin'])
220 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
221 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
221 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
222 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
222 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
223 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
223
224
224 c.new_repo = repo_name_slug(new_repo)
225 c.new_repo = repo_name_slug(new_repo)
225
226
226 ## apply the defaults from defaults page
227 ## apply the defaults from defaults page
227 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
228 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
228 if parent_group:
229 if parent_group:
229 defaults.update({'repo_group': parent_group})
230 defaults.update({'repo_group': parent_group})
230
231
231 return htmlfill.render(
232 return htmlfill.render(
232 render('admin/repos/repo_add.html'),
233 render('admin/repos/repo_add.html'),
233 defaults=defaults,
234 defaults=defaults,
234 errors={},
235 errors={},
235 prefix_error=False,
236 prefix_error=False,
236 encoding="UTF-8"
237 encoding="UTF-8"
237 )
238 )
238
239
239 @HasRepoPermissionAllDecorator('repository.admin')
240 @HasRepoPermissionAllDecorator('repository.admin')
240 def update(self, repo_name):
241 def update(self, repo_name):
241 """
242 """
242 PUT /repos/repo_name: Update an existing item"""
243 PUT /repos/repo_name: Update an existing item"""
243 # Forms posted to this method should contain a hidden field:
244 # Forms posted to this method should contain a hidden field:
244 # <input type="hidden" name="_method" value="PUT" />
245 # <input type="hidden" name="_method" value="PUT" />
245 # Or using helpers:
246 # Or using helpers:
246 # h.form(url('repo', repo_name=ID),
247 # h.form(url('repo', repo_name=ID),
247 # method='put')
248 # method='put')
248 # url('repo', repo_name=ID)
249 # url('repo', repo_name=ID)
249 self.__load_defaults()
250 self.__load_defaults()
250 repo_model = RepoModel()
251 repo_model = RepoModel()
251 changed_name = repo_name
252 changed_name = repo_name
252 #override the choices with extracted revisions !
253 #override the choices with extracted revisions !
253 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
254 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
254 c.landing_revs_choices = choices
255 c.landing_revs_choices = choices
255 repo = Repository.get_by_repo_name(repo_name)
256 repo = Repository.get_by_repo_name(repo_name)
256 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
257 _form = RepoForm(edit=True, old_data={'repo_name': repo_name,
257 'repo_group': repo.group.get_dict() \
258 'repo_group': repo.group.get_dict() \
258 if repo.group else {}},
259 if repo.group else {}},
259 repo_groups=c.repo_groups_choices,
260 repo_groups=c.repo_groups_choices,
260 landing_revs=c.landing_revs_choices)()
261 landing_revs=c.landing_revs_choices)()
261 try:
262 try:
262 form_result = _form.to_python(dict(request.POST))
263 form_result = _form.to_python(dict(request.POST))
263 repo = repo_model.update(repo_name, **form_result)
264 repo = repo_model.update(repo_name, **form_result)
264 invalidate_cache('get_repo_cached_%s' % repo_name)
265 invalidate_cache('get_repo_cached_%s' % repo_name)
265 h.flash(_('Repository %s updated successfully') % repo_name,
266 h.flash(_('Repository %s updated successfully') % repo_name,
266 category='success')
267 category='success')
267 changed_name = repo.repo_name
268 changed_name = repo.repo_name
268 action_logger(self.rhodecode_user, 'admin_updated_repo',
269 action_logger(self.rhodecode_user, 'admin_updated_repo',
269 changed_name, self.ip_addr, self.sa)
270 changed_name, self.ip_addr, self.sa)
270 Session().commit()
271 Session().commit()
271 except formencode.Invalid, errors:
272 except formencode.Invalid, errors:
272 defaults = self.__load_data(repo_name)
273 defaults = self.__load_data(repo_name)
273 defaults.update(errors.value)
274 defaults.update(errors.value)
274 return htmlfill.render(
275 return htmlfill.render(
275 render('admin/repos/repo_edit.html'),
276 render('admin/repos/repo_edit.html'),
276 defaults=defaults,
277 defaults=defaults,
277 errors=errors.error_dict or {},
278 errors=errors.error_dict or {},
278 prefix_error=False,
279 prefix_error=False,
279 encoding="UTF-8")
280 encoding="UTF-8")
280
281
281 except Exception:
282 except Exception:
282 log.error(traceback.format_exc())
283 log.error(traceback.format_exc())
283 h.flash(_('Error occurred during update of repository %s') \
284 h.flash(_('Error occurred during update of repository %s') \
284 % repo_name, category='error')
285 % repo_name, category='error')
285 return redirect(url('edit_repo', repo_name=changed_name))
286 return redirect(url('edit_repo', repo_name=changed_name))
286
287
287 @HasRepoPermissionAllDecorator('repository.admin')
288 @HasRepoPermissionAllDecorator('repository.admin')
288 def delete(self, repo_name):
289 def delete(self, repo_name):
289 """
290 """
290 DELETE /repos/repo_name: Delete an existing item"""
291 DELETE /repos/repo_name: Delete an existing item"""
291 # Forms posted to this method should contain a hidden field:
292 # Forms posted to this method should contain a hidden field:
292 # <input type="hidden" name="_method" value="DELETE" />
293 # <input type="hidden" name="_method" value="DELETE" />
293 # Or using helpers:
294 # Or using helpers:
294 # h.form(url('repo', repo_name=ID),
295 # h.form(url('repo', repo_name=ID),
295 # method='delete')
296 # method='delete')
296 # url('repo', repo_name=ID)
297 # url('repo', repo_name=ID)
297
298
298 repo_model = RepoModel()
299 repo_model = RepoModel()
299 repo = repo_model.get_by_repo_name(repo_name)
300 repo = repo_model.get_by_repo_name(repo_name)
300 if not repo:
301 if not repo:
301 h.not_mapped_error(repo_name)
302 h.not_mapped_error(repo_name)
302 return redirect(url('repos'))
303 return redirect(url('repos'))
303 try:
304 try:
304 _forks = repo.forks.count()
305 _forks = repo.forks.count()
306 handle_forks = None
305 if _forks and request.POST.get('forks'):
307 if _forks and request.POST.get('forks'):
306 do = request.POST['forks']
308 do = request.POST['forks']
307 if do == 'detach_forks':
309 if do == 'detach_forks':
308 for r in repo.forks:
310 handle_forks = 'detach'
309 log.debug('Detaching fork %s from repo %s' % (r, repo))
310 r.fork = None
311 Session().add(r)
312 h.flash(_('Detached %s forks') % _forks, category='success')
311 h.flash(_('Detached %s forks') % _forks, category='success')
313 elif do == 'delete_forks':
312 elif do == 'delete_forks':
314 for r in repo.forks:
313 handle_forks = 'delete'
315 log.debug('Deleting fork %s of repo %s' % (r, repo))
316 repo_model.delete(r)
317 h.flash(_('Deleted %s forks') % _forks, category='success')
314 h.flash(_('Deleted %s forks') % _forks, category='success')
315 repo_model.delete(repo, forks=handle_forks)
318 action_logger(self.rhodecode_user, 'admin_deleted_repo',
316 action_logger(self.rhodecode_user, 'admin_deleted_repo',
319 repo_name, self.ip_addr, self.sa)
317 repo_name, self.ip_addr, self.sa)
320 repo_model.delete(repo)
321 invalidate_cache('get_repo_cached_%s' % repo_name)
318 invalidate_cache('get_repo_cached_%s' % repo_name)
322 h.flash(_('Deleted repository %s') % repo_name, category='success')
319 h.flash(_('Deleted repository %s') % repo_name, category='success')
323 Session().commit()
320 Session().commit()
324 except IntegrityError, e:
321 except AttachedForksError:
325 if e.message.find('repositories_fork_id_fkey') != -1:
322 h.flash(_('Cannot delete %s it still contains attached forks')
326 log.error(traceback.format_exc())
323 % repo_name, category='warning')
327 h.flash(_('Cannot delete %s it still contains attached '
328 'forks') % repo_name,
329 category='warning')
330 else:
331 log.error(traceback.format_exc())
332 h.flash(_('An error occurred during '
333 'deletion of %s') % repo_name,
334 category='error')
335
324
336 except Exception, e:
325 except Exception:
337 log.error(traceback.format_exc())
326 log.error(traceback.format_exc())
338 h.flash(_('An error occurred during deletion of %s') % repo_name,
327 h.flash(_('An error occurred during deletion of %s') % repo_name,
339 category='error')
328 category='error')
340
329
341 return redirect(url('repos'))
330 return redirect(url('repos'))
342
331
343 @HasRepoPermissionAllDecorator('repository.admin')
332 @HasRepoPermissionAllDecorator('repository.admin')
344 def set_repo_perm_member(self, repo_name):
333 def set_repo_perm_member(self, repo_name):
345 form = RepoPermsForm()().to_python(request.POST)
334 form = RepoPermsForm()().to_python(request.POST)
346
335
347 perms_new = form['perms_new']
336 perms_new = form['perms_new']
348 perms_updates = form['perms_updates']
337 perms_updates = form['perms_updates']
349 cur_repo = repo_name
338 cur_repo = repo_name
350
339
351 # update permissions
340 # update permissions
352 for member, perm, member_type in perms_updates:
341 for member, perm, member_type in perms_updates:
353 if member_type == 'user':
342 if member_type == 'user':
354 # this updates existing one
343 # this updates existing one
355 RepoModel().grant_user_permission(
344 RepoModel().grant_user_permission(
356 repo=cur_repo, user=member, perm=perm
345 repo=cur_repo, user=member, perm=perm
357 )
346 )
358 else:
347 else:
359 RepoModel().grant_users_group_permission(
348 RepoModel().grant_users_group_permission(
360 repo=cur_repo, group_name=member, perm=perm
349 repo=cur_repo, group_name=member, perm=perm
361 )
350 )
362 # set new permissions
351 # set new permissions
363 for member, perm, member_type in perms_new:
352 for member, perm, member_type in perms_new:
364 if member_type == 'user':
353 if member_type == 'user':
365 RepoModel().grant_user_permission(
354 RepoModel().grant_user_permission(
366 repo=cur_repo, user=member, perm=perm
355 repo=cur_repo, user=member, perm=perm
367 )
356 )
368 else:
357 else:
369 RepoModel().grant_users_group_permission(
358 RepoModel().grant_users_group_permission(
370 repo=cur_repo, group_name=member, perm=perm
359 repo=cur_repo, group_name=member, perm=perm
371 )
360 )
372 #TODO: implement this
361 #TODO: implement this
373 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
362 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
374 # repo_name, self.ip_addr, self.sa)
363 # repo_name, self.ip_addr, self.sa)
375 Session().commit()
364 Session().commit()
376 h.flash(_('updated repository permissions'), category='success')
365 h.flash(_('updated repository permissions'), category='success')
377 return redirect(url('edit_repo', repo_name=repo_name))
366 return redirect(url('edit_repo', repo_name=repo_name))
378
367
379 @HasRepoPermissionAllDecorator('repository.admin')
368 @HasRepoPermissionAllDecorator('repository.admin')
380 def delete_perm_user(self, repo_name):
369 def delete_perm_user(self, repo_name):
381 """
370 """
382 DELETE an existing repository permission user
371 DELETE an existing repository permission user
383
372
384 :param repo_name:
373 :param repo_name:
385 """
374 """
386 try:
375 try:
387 RepoModel().revoke_user_permission(repo=repo_name,
376 RepoModel().revoke_user_permission(repo=repo_name,
388 user=request.POST['user_id'])
377 user=request.POST['user_id'])
389 #TODO: implement this
378 #TODO: implement this
390 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
379 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
391 # repo_name, self.ip_addr, self.sa)
380 # repo_name, self.ip_addr, self.sa)
392 Session().commit()
381 Session().commit()
393 except Exception:
382 except Exception:
394 log.error(traceback.format_exc())
383 log.error(traceback.format_exc())
395 h.flash(_('An error occurred during deletion of repository user'),
384 h.flash(_('An error occurred during deletion of repository user'),
396 category='error')
385 category='error')
397 raise HTTPInternalServerError()
386 raise HTTPInternalServerError()
398
387
399 @HasRepoPermissionAllDecorator('repository.admin')
388 @HasRepoPermissionAllDecorator('repository.admin')
400 def delete_perm_users_group(self, repo_name):
389 def delete_perm_users_group(self, repo_name):
401 """
390 """
402 DELETE an existing repository permission user group
391 DELETE an existing repository permission user group
403
392
404 :param repo_name:
393 :param repo_name:
405 """
394 """
406
395
407 try:
396 try:
408 RepoModel().revoke_users_group_permission(
397 RepoModel().revoke_users_group_permission(
409 repo=repo_name, group_name=request.POST['users_group_id']
398 repo=repo_name, group_name=request.POST['users_group_id']
410 )
399 )
411 Session().commit()
400 Session().commit()
412 except Exception:
401 except Exception:
413 log.error(traceback.format_exc())
402 log.error(traceback.format_exc())
414 h.flash(_('An error occurred during deletion of repository'
403 h.flash(_('An error occurred during deletion of repository'
415 ' user groups'),
404 ' user groups'),
416 category='error')
405 category='error')
417 raise HTTPInternalServerError()
406 raise HTTPInternalServerError()
418
407
419 @HasRepoPermissionAllDecorator('repository.admin')
408 @HasRepoPermissionAllDecorator('repository.admin')
420 def repo_stats(self, repo_name):
409 def repo_stats(self, repo_name):
421 """
410 """
422 DELETE an existing repository statistics
411 DELETE an existing repository statistics
423
412
424 :param repo_name:
413 :param repo_name:
425 """
414 """
426
415
427 try:
416 try:
428 RepoModel().delete_stats(repo_name)
417 RepoModel().delete_stats(repo_name)
429 Session().commit()
418 Session().commit()
430 except Exception, e:
419 except Exception, e:
431 log.error(traceback.format_exc())
420 log.error(traceback.format_exc())
432 h.flash(_('An error occurred during deletion of repository stats'),
421 h.flash(_('An error occurred during deletion of repository stats'),
433 category='error')
422 category='error')
434 return redirect(url('edit_repo', repo_name=repo_name))
423 return redirect(url('edit_repo', repo_name=repo_name))
435
424
436 @HasRepoPermissionAllDecorator('repository.admin')
425 @HasRepoPermissionAllDecorator('repository.admin')
437 def repo_cache(self, repo_name):
426 def repo_cache(self, repo_name):
438 """
427 """
439 INVALIDATE existing repository cache
428 INVALIDATE existing repository cache
440
429
441 :param repo_name:
430 :param repo_name:
442 """
431 """
443
432
444 try:
433 try:
445 ScmModel().mark_for_invalidation(repo_name)
434 ScmModel().mark_for_invalidation(repo_name)
446 Session().commit()
435 Session().commit()
447 except Exception, e:
436 except Exception, e:
448 log.error(traceback.format_exc())
437 log.error(traceback.format_exc())
449 h.flash(_('An error occurred during cache invalidation'),
438 h.flash(_('An error occurred during cache invalidation'),
450 category='error')
439 category='error')
451 return redirect(url('edit_repo', repo_name=repo_name))
440 return redirect(url('edit_repo', repo_name=repo_name))
452
441
453 @HasRepoPermissionAllDecorator('repository.admin')
442 @HasRepoPermissionAllDecorator('repository.admin')
454 def repo_locking(self, repo_name):
443 def repo_locking(self, repo_name):
455 """
444 """
456 Unlock repository when it is locked !
445 Unlock repository when it is locked !
457
446
458 :param repo_name:
447 :param repo_name:
459 """
448 """
460
449
461 try:
450 try:
462 repo = Repository.get_by_repo_name(repo_name)
451 repo = Repository.get_by_repo_name(repo_name)
463 if request.POST.get('set_lock'):
452 if request.POST.get('set_lock'):
464 Repository.lock(repo, c.rhodecode_user.user_id)
453 Repository.lock(repo, c.rhodecode_user.user_id)
465 elif request.POST.get('set_unlock'):
454 elif request.POST.get('set_unlock'):
466 Repository.unlock(repo)
455 Repository.unlock(repo)
467 except Exception, e:
456 except Exception, e:
468 log.error(traceback.format_exc())
457 log.error(traceback.format_exc())
469 h.flash(_('An error occurred during unlocking'),
458 h.flash(_('An error occurred during unlocking'),
470 category='error')
459 category='error')
471 return redirect(url('edit_repo', repo_name=repo_name))
460 return redirect(url('edit_repo', repo_name=repo_name))
472
461
473 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
462 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
474 def toggle_locking(self, repo_name):
463 def toggle_locking(self, repo_name):
475 """
464 """
476 Toggle locking of repository by simple GET call to url
465 Toggle locking of repository by simple GET call to url
477
466
478 :param repo_name:
467 :param repo_name:
479 """
468 """
480
469
481 try:
470 try:
482 repo = Repository.get_by_repo_name(repo_name)
471 repo = Repository.get_by_repo_name(repo_name)
483
472
484 if repo.enable_locking:
473 if repo.enable_locking:
485 if repo.locked[0]:
474 if repo.locked[0]:
486 Repository.unlock(repo)
475 Repository.unlock(repo)
487 action = _('unlocked')
476 action = _('unlocked')
488 else:
477 else:
489 Repository.lock(repo, c.rhodecode_user.user_id)
478 Repository.lock(repo, c.rhodecode_user.user_id)
490 action = _('locked')
479 action = _('locked')
491
480
492 h.flash(_('Repository has been %s') % action,
481 h.flash(_('Repository has been %s') % action,
493 category='success')
482 category='success')
494 except Exception, e:
483 except Exception, e:
495 log.error(traceback.format_exc())
484 log.error(traceback.format_exc())
496 h.flash(_('An error occurred during unlocking'),
485 h.flash(_('An error occurred during unlocking'),
497 category='error')
486 category='error')
498 return redirect(url('summary_home', repo_name=repo_name))
487 return redirect(url('summary_home', repo_name=repo_name))
499
488
500 @HasRepoPermissionAllDecorator('repository.admin')
489 @HasRepoPermissionAllDecorator('repository.admin')
501 def repo_public_journal(self, repo_name):
490 def repo_public_journal(self, repo_name):
502 """
491 """
503 Set's this repository to be visible in public journal,
492 Set's this repository to be visible in public journal,
504 in other words assing default user to follow this repo
493 in other words assing default user to follow this repo
505
494
506 :param repo_name:
495 :param repo_name:
507 """
496 """
508
497
509 cur_token = request.POST.get('auth_token')
498 cur_token = request.POST.get('auth_token')
510 token = get_token()
499 token = get_token()
511 if cur_token == token:
500 if cur_token == token:
512 try:
501 try:
513 repo_id = Repository.get_by_repo_name(repo_name).repo_id
502 repo_id = Repository.get_by_repo_name(repo_name).repo_id
514 user_id = User.get_by_username('default').user_id
503 user_id = User.get_by_username('default').user_id
515 self.scm_model.toggle_following_repo(repo_id, user_id)
504 self.scm_model.toggle_following_repo(repo_id, user_id)
516 h.flash(_('Updated repository visibility in public journal'),
505 h.flash(_('Updated repository visibility in public journal'),
517 category='success')
506 category='success')
518 Session().commit()
507 Session().commit()
519 except Exception:
508 except Exception:
520 h.flash(_('An error occurred during setting this'
509 h.flash(_('An error occurred during setting this'
521 ' repository in public journal'),
510 ' repository in public journal'),
522 category='error')
511 category='error')
523
512
524 else:
513 else:
525 h.flash(_('Token mismatch'), category='error')
514 h.flash(_('Token mismatch'), category='error')
526 return redirect(url('edit_repo', repo_name=repo_name))
515 return redirect(url('edit_repo', repo_name=repo_name))
527
516
528 @HasRepoPermissionAllDecorator('repository.admin')
517 @HasRepoPermissionAllDecorator('repository.admin')
529 def repo_pull(self, repo_name):
518 def repo_pull(self, repo_name):
530 """
519 """
531 Runs task to update given repository with remote changes,
520 Runs task to update given repository with remote changes,
532 ie. make pull on remote location
521 ie. make pull on remote location
533
522
534 :param repo_name:
523 :param repo_name:
535 """
524 """
536 try:
525 try:
537 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
526 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
538 h.flash(_('Pulled from remote location'), category='success')
527 h.flash(_('Pulled from remote location'), category='success')
539 except Exception, e:
528 except Exception, e:
540 h.flash(_('An error occurred during pull from remote location'),
529 h.flash(_('An error occurred during pull from remote location'),
541 category='error')
530 category='error')
542
531
543 return redirect(url('edit_repo', repo_name=repo_name))
532 return redirect(url('edit_repo', repo_name=repo_name))
544
533
545 @HasRepoPermissionAllDecorator('repository.admin')
534 @HasRepoPermissionAllDecorator('repository.admin')
546 def repo_as_fork(self, repo_name):
535 def repo_as_fork(self, repo_name):
547 """
536 """
548 Mark given repository as a fork of another
537 Mark given repository as a fork of another
549
538
550 :param repo_name:
539 :param repo_name:
551 """
540 """
552 try:
541 try:
553 fork_id = request.POST.get('id_fork_of')
542 fork_id = request.POST.get('id_fork_of')
554 repo = ScmModel().mark_as_fork(repo_name, fork_id,
543 repo = ScmModel().mark_as_fork(repo_name, fork_id,
555 self.rhodecode_user.username)
544 self.rhodecode_user.username)
556 fork = repo.fork.repo_name if repo.fork else _('Nothing')
545 fork = repo.fork.repo_name if repo.fork else _('Nothing')
557 Session().commit()
546 Session().commit()
558 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
547 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
559 category='success')
548 category='success')
560 except Exception, e:
549 except Exception, e:
561 log.error(traceback.format_exc())
550 log.error(traceback.format_exc())
562 h.flash(_('An error occurred during this operation'),
551 h.flash(_('An error occurred during this operation'),
563 category='error')
552 category='error')
564
553
565 return redirect(url('edit_repo', repo_name=repo_name))
554 return redirect(url('edit_repo', repo_name=repo_name))
566
555
567 @HasPermissionAllDecorator('hg.admin')
556 @HasPermissionAllDecorator('hg.admin')
568 def show(self, repo_name, format='html'):
557 def show(self, repo_name, format='html'):
569 """GET /repos/repo_name: Show a specific item"""
558 """GET /repos/repo_name: Show a specific item"""
570 # url('repo', repo_name=ID)
559 # url('repo', repo_name=ID)
571
560
572 @HasRepoPermissionAllDecorator('repository.admin')
561 @HasRepoPermissionAllDecorator('repository.admin')
573 def edit(self, repo_name, format='html'):
562 def edit(self, repo_name, format='html'):
574 """GET /repos/repo_name/edit: Form to edit an existing item"""
563 """GET /repos/repo_name/edit: Form to edit an existing item"""
575 # url('edit_repo', repo_name=ID)
564 # url('edit_repo', repo_name=ID)
576 defaults = self.__load_data(repo_name)
565 defaults = self.__load_data(repo_name)
577
566
578 return htmlfill.render(
567 return htmlfill.render(
579 render('admin/repos/repo_edit.html'),
568 render('admin/repos/repo_edit.html'),
580 defaults=defaults,
569 defaults=defaults,
581 encoding="UTF-8",
570 encoding="UTF-8",
582 force_defaults=False
571 force_defaults=False
583 )
572 )
584
573
585 @HasPermissionAllDecorator('hg.admin')
574 @HasPermissionAllDecorator('hg.admin')
586 def create_repo_field(self, repo_name):
575 def create_repo_field(self, repo_name):
587 try:
576 try:
588 form_result = RepoFieldForm()().to_python(dict(request.POST))
577 form_result = RepoFieldForm()().to_python(dict(request.POST))
589 new_field = RepositoryField()
578 new_field = RepositoryField()
590 new_field.repository = Repository.get_by_repo_name(repo_name)
579 new_field.repository = Repository.get_by_repo_name(repo_name)
591 new_field.field_key = form_result['new_field_key']
580 new_field.field_key = form_result['new_field_key']
592 new_field.field_type = form_result['new_field_type'] # python type
581 new_field.field_type = form_result['new_field_type'] # python type
593 new_field.field_value = form_result['new_field_value'] # set initial blank value
582 new_field.field_value = form_result['new_field_value'] # set initial blank value
594 new_field.field_desc = form_result['new_field_desc']
583 new_field.field_desc = form_result['new_field_desc']
595 new_field.field_label = form_result['new_field_label']
584 new_field.field_label = form_result['new_field_label']
596 Session().add(new_field)
585 Session().add(new_field)
597 Session().commit()
586 Session().commit()
598
587
599 except Exception, e:
588 except Exception, e:
600 log.error(traceback.format_exc())
589 log.error(traceback.format_exc())
601 msg = _('An error occurred during creation of field')
590 msg = _('An error occurred during creation of field')
602 if isinstance(e, formencode.Invalid):
591 if isinstance(e, formencode.Invalid):
603 msg += ". " + e.msg
592 msg += ". " + e.msg
604 h.flash(msg, category='error')
593 h.flash(msg, category='error')
605 return redirect(url('edit_repo', repo_name=repo_name))
594 return redirect(url('edit_repo', repo_name=repo_name))
606
595
607 @HasPermissionAllDecorator('hg.admin')
596 @HasPermissionAllDecorator('hg.admin')
608 def delete_repo_field(self, repo_name, field_id):
597 def delete_repo_field(self, repo_name, field_id):
609 field = RepositoryField.get_or_404(field_id)
598 field = RepositoryField.get_or_404(field_id)
610 try:
599 try:
611 Session().delete(field)
600 Session().delete(field)
612 Session().commit()
601 Session().commit()
613 except Exception, e:
602 except Exception, e:
614 log.error(traceback.format_exc())
603 log.error(traceback.format_exc())
615 msg = _('An error occurred during removal of field')
604 msg = _('An error occurred during removal of field')
616 h.flash(msg, category='error')
605 h.flash(msg, category='error')
617 return redirect(url('edit_repo', repo_name=repo_name))
606 return redirect(url('edit_repo', repo_name=repo_name))
@@ -1,1017 +1,1031 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.permission import PermissionModel
43 from rhodecode.model.permission import PermissionModel
44 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
44 from rhodecode.model.db import Repository, RhodeCodeSetting, UserIpMap
45 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 = PermissionModel().get_permission_by_name(permid)
142 perm = PermissionModel().get_permission_by_name(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 invalidated_keys = ScmModel().mark_for_invalidation(repo.repo_name)
224 Session().commit()
224 Session().commit()
225 return ('Cache for repository `%s` was invalidated: '
225 return ('Cache for repository `%s` was invalidated: '
226 'invalidated cache keys: %s' % (repoid, invalidated_keys))
226 'invalidated cache keys: %s' % (repoid, invalidated_keys))
227 except Exception:
227 except Exception:
228 log.error(traceback.format_exc())
228 log.error(traceback.format_exc())
229 raise JSONRPCError(
229 raise JSONRPCError(
230 'Error occurred during cache invalidation action'
230 'Error occurred during cache invalidation action'
231 )
231 )
232
232
233 def lock(self, apiuser, repoid, locked=Optional(None),
233 def lock(self, apiuser, repoid, locked=Optional(None),
234 userid=Optional(OAttr('apiuser'))):
234 userid=Optional(OAttr('apiuser'))):
235 """
235 """
236 Set locking state on particular repository by given user, if
236 Set locking state on particular repository by given user, if
237 this command is runned by non-admin account userid is set to user
237 this command is runned by non-admin account userid is set to user
238 who is calling this method
238 who is calling this method
239
239
240 :param apiuser:
240 :param apiuser:
241 :param repoid:
241 :param repoid:
242 :param userid:
242 :param userid:
243 :param locked:
243 :param locked:
244 """
244 """
245 repo = get_repo_or_error(repoid)
245 repo = get_repo_or_error(repoid)
246 if HasPermissionAnyApi('hg.admin')(user=apiuser):
246 if HasPermissionAnyApi('hg.admin')(user=apiuser):
247 pass
247 pass
248 elif HasRepoPermissionAnyApi('repository.admin',
248 elif HasRepoPermissionAnyApi('repository.admin',
249 'repository.write')(user=apiuser,
249 'repository.write')(user=apiuser,
250 repo_name=repo.repo_name):
250 repo_name=repo.repo_name):
251 #make sure normal user does not pass someone else userid,
251 #make sure normal user does not pass someone else userid,
252 #he is not allowed to do that
252 #he is not allowed to do that
253 if not isinstance(userid, Optional) and userid != apiuser.user_id:
253 if not isinstance(userid, Optional) and userid != apiuser.user_id:
254 raise JSONRPCError(
254 raise JSONRPCError(
255 'userid is not the same as your user'
255 'userid is not the same as your user'
256 )
256 )
257 else:
257 else:
258 raise JSONRPCError('repository `%s` does not exist' % (repoid))
258 raise JSONRPCError('repository `%s` does not exist' % (repoid))
259
259
260 if isinstance(userid, Optional):
260 if isinstance(userid, Optional):
261 userid = apiuser.user_id
261 userid = apiuser.user_id
262
262
263 user = get_user_or_error(userid)
263 user = get_user_or_error(userid)
264
264
265 if isinstance(locked, Optional):
265 if isinstance(locked, Optional):
266 lockobj = Repository.getlock(repo)
266 lockobj = Repository.getlock(repo)
267
267
268 if lockobj[0] is None:
268 if lockobj[0] is None:
269 return ('Repo `%s` not locked. Locked=`False`.'
269 return ('Repo `%s` not locked. Locked=`False`.'
270 % (repo.repo_name))
270 % (repo.repo_name))
271 else:
271 else:
272 userid, time_ = lockobj
272 userid, time_ = lockobj
273 user = get_user_or_error(userid)
273 user = get_user_or_error(userid)
274
274
275 return ('Repo `%s` locked by `%s`. Locked=`True`. '
275 return ('Repo `%s` locked by `%s`. Locked=`True`. '
276 'Locked since: `%s`'
276 'Locked since: `%s`'
277 % (repo.repo_name, user.username,
277 % (repo.repo_name, user.username,
278 json.dumps(time_to_datetime(time_))))
278 json.dumps(time_to_datetime(time_))))
279
279
280 else:
280 else:
281 locked = str2bool(locked)
281 locked = str2bool(locked)
282 try:
282 try:
283 if locked:
283 if locked:
284 Repository.lock(repo, user.user_id)
284 Repository.lock(repo, user.user_id)
285 else:
285 else:
286 Repository.unlock(repo)
286 Repository.unlock(repo)
287
287
288 return ('User `%s` set lock state for repo `%s` to `%s`'
288 return ('User `%s` set lock state for repo `%s` to `%s`'
289 % (user.username, repo.repo_name, locked))
289 % (user.username, repo.repo_name, locked))
290 except Exception:
290 except Exception:
291 log.error(traceback.format_exc())
291 log.error(traceback.format_exc())
292 raise JSONRPCError(
292 raise JSONRPCError(
293 'Error occurred locking repository `%s`' % repo.repo_name
293 'Error occurred locking repository `%s`' % repo.repo_name
294 )
294 )
295
295
296 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
296 def get_locks(self, apiuser, userid=Optional(OAttr('apiuser'))):
297 """
297 """
298 Get all locks for given userid, if
298 Get all locks for given userid, if
299 this command is runned by non-admin account userid is set to user
299 this command is runned by non-admin account userid is set to user
300 who is calling this method, thus returning locks for himself
300 who is calling this method, thus returning locks for himself
301
301
302 :param apiuser:
302 :param apiuser:
303 :param userid:
303 :param userid:
304 """
304 """
305 if HasPermissionAnyApi('hg.admin')(user=apiuser):
305 if HasPermissionAnyApi('hg.admin')(user=apiuser):
306 pass
306 pass
307 else:
307 else:
308 #make sure normal user does not pass someone else userid,
308 #make sure normal user does not pass someone else userid,
309 #he is not allowed to do that
309 #he is not allowed to do that
310 if not isinstance(userid, Optional) and userid != apiuser.user_id:
310 if not isinstance(userid, Optional) and userid != apiuser.user_id:
311 raise JSONRPCError(
311 raise JSONRPCError(
312 'userid is not the same as your user'
312 'userid is not the same as your user'
313 )
313 )
314 ret = []
314 ret = []
315 if isinstance(userid, Optional):
315 if isinstance(userid, Optional):
316 user = None
316 user = None
317 else:
317 else:
318 user = get_user_or_error(userid)
318 user = get_user_or_error(userid)
319
319
320 #show all locks
320 #show all locks
321 for r in Repository.getAll():
321 for r in Repository.getAll():
322 userid, time_ = r.locked
322 userid, time_ = r.locked
323 if time_:
323 if time_:
324 _api_data = r.get_api_data()
324 _api_data = r.get_api_data()
325 # if we use userfilter just show the locks for this user
325 # if we use userfilter just show the locks for this user
326 if user:
326 if user:
327 if safe_int(userid) == user.user_id:
327 if safe_int(userid) == user.user_id:
328 ret.append(_api_data)
328 ret.append(_api_data)
329 else:
329 else:
330 ret.append(_api_data)
330 ret.append(_api_data)
331
331
332 return ret
332 return ret
333
333
334 @HasPermissionAllDecorator('hg.admin')
334 @HasPermissionAllDecorator('hg.admin')
335 def show_ip(self, apiuser, userid):
335 def show_ip(self, apiuser, userid):
336 """
336 """
337 Shows IP address as seen from RhodeCode server, together with all
337 Shows IP address as seen from RhodeCode server, together with all
338 defined IP addresses for given user
338 defined IP addresses for given user
339
339
340 :param apiuser:
340 :param apiuser:
341 :param userid:
341 :param userid:
342 """
342 """
343 user = get_user_or_error(userid)
343 user = get_user_or_error(userid)
344 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
344 ips = UserIpMap.query().filter(UserIpMap.user == user).all()
345 return dict(
345 return dict(
346 ip_addr_server=self.ip_addr,
346 ip_addr_server=self.ip_addr,
347 user_ips=ips
347 user_ips=ips
348 )
348 )
349
349
350 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
350 def get_user(self, apiuser, userid=Optional(OAttr('apiuser'))):
351 """"
351 """"
352 Get a user by username, or userid, if userid is given
352 Get a user by username, or userid, if userid is given
353
353
354 :param apiuser:
354 :param apiuser:
355 :param userid:
355 :param userid:
356 """
356 """
357 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
357 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
358 #make sure normal user does not pass someone else userid,
358 #make sure normal user does not pass someone else userid,
359 #he is not allowed to do that
359 #he is not allowed to do that
360 if not isinstance(userid, Optional) and userid != apiuser.user_id:
360 if not isinstance(userid, Optional) and userid != apiuser.user_id:
361 raise JSONRPCError(
361 raise JSONRPCError(
362 'userid is not the same as your user'
362 'userid is not the same as your user'
363 )
363 )
364
364
365 if isinstance(userid, Optional):
365 if isinstance(userid, Optional):
366 userid = apiuser.user_id
366 userid = apiuser.user_id
367
367
368 user = get_user_or_error(userid)
368 user = get_user_or_error(userid)
369 data = user.get_api_data()
369 data = user.get_api_data()
370 data['permissions'] = AuthUser(user_id=user.user_id).permissions
370 data['permissions'] = AuthUser(user_id=user.user_id).permissions
371 return data
371 return data
372
372
373 @HasPermissionAllDecorator('hg.admin')
373 @HasPermissionAllDecorator('hg.admin')
374 def get_users(self, apiuser):
374 def get_users(self, apiuser):
375 """"
375 """"
376 Get all users
376 Get all users
377
377
378 :param apiuser:
378 :param apiuser:
379 """
379 """
380
380
381 result = []
381 result = []
382 for user in UserModel().get_all():
382 for user in UserModel().get_all():
383 result.append(user.get_api_data())
383 result.append(user.get_api_data())
384 return result
384 return result
385
385
386 @HasPermissionAllDecorator('hg.admin')
386 @HasPermissionAllDecorator('hg.admin')
387 def create_user(self, apiuser, username, email, password,
387 def create_user(self, apiuser, username, email, password,
388 firstname=Optional(None), lastname=Optional(None),
388 firstname=Optional(None), lastname=Optional(None),
389 active=Optional(True), admin=Optional(False),
389 active=Optional(True), admin=Optional(False),
390 ldap_dn=Optional(None)):
390 ldap_dn=Optional(None)):
391 """
391 """
392 Create new user
392 Create new user
393
393
394 :param apiuser:
394 :param apiuser:
395 :param username:
395 :param username:
396 :param email:
396 :param email:
397 :param password:
397 :param password:
398 :param firstname:
398 :param firstname:
399 :param lastname:
399 :param lastname:
400 :param active:
400 :param active:
401 :param admin:
401 :param admin:
402 :param ldap_dn:
402 :param ldap_dn:
403 """
403 """
404
404
405 if UserModel().get_by_username(username):
405 if UserModel().get_by_username(username):
406 raise JSONRPCError("user `%s` already exist" % username)
406 raise JSONRPCError("user `%s` already exist" % username)
407
407
408 if UserModel().get_by_email(email, case_insensitive=True):
408 if UserModel().get_by_email(email, case_insensitive=True):
409 raise JSONRPCError("email `%s` already exist" % email)
409 raise JSONRPCError("email `%s` already exist" % email)
410
410
411 if Optional.extract(ldap_dn):
411 if Optional.extract(ldap_dn):
412 # generate temporary password if ldap_dn
412 # generate temporary password if ldap_dn
413 password = PasswordGenerator().gen_password(length=8)
413 password = PasswordGenerator().gen_password(length=8)
414
414
415 try:
415 try:
416 user = UserModel().create_or_update(
416 user = UserModel().create_or_update(
417 username=Optional.extract(username),
417 username=Optional.extract(username),
418 password=Optional.extract(password),
418 password=Optional.extract(password),
419 email=Optional.extract(email),
419 email=Optional.extract(email),
420 firstname=Optional.extract(firstname),
420 firstname=Optional.extract(firstname),
421 lastname=Optional.extract(lastname),
421 lastname=Optional.extract(lastname),
422 active=Optional.extract(active),
422 active=Optional.extract(active),
423 admin=Optional.extract(admin),
423 admin=Optional.extract(admin),
424 ldap_dn=Optional.extract(ldap_dn)
424 ldap_dn=Optional.extract(ldap_dn)
425 )
425 )
426 Session().commit()
426 Session().commit()
427 return dict(
427 return dict(
428 msg='created new user `%s`' % username,
428 msg='created new user `%s`' % username,
429 user=user.get_api_data()
429 user=user.get_api_data()
430 )
430 )
431 except Exception:
431 except Exception:
432 log.error(traceback.format_exc())
432 log.error(traceback.format_exc())
433 raise JSONRPCError('failed to create user `%s`' % username)
433 raise JSONRPCError('failed to create user `%s`' % username)
434
434
435 @HasPermissionAllDecorator('hg.admin')
435 @HasPermissionAllDecorator('hg.admin')
436 def update_user(self, apiuser, userid, username=Optional(None),
436 def update_user(self, apiuser, userid, username=Optional(None),
437 email=Optional(None), firstname=Optional(None),
437 email=Optional(None), firstname=Optional(None),
438 lastname=Optional(None), active=Optional(None),
438 lastname=Optional(None), active=Optional(None),
439 admin=Optional(None), ldap_dn=Optional(None),
439 admin=Optional(None), ldap_dn=Optional(None),
440 password=Optional(None)):
440 password=Optional(None)):
441 """
441 """
442 Updates given user
442 Updates given user
443
443
444 :param apiuser:
444 :param apiuser:
445 :param userid:
445 :param userid:
446 :param username:
446 :param username:
447 :param email:
447 :param email:
448 :param firstname:
448 :param firstname:
449 :param lastname:
449 :param lastname:
450 :param active:
450 :param active:
451 :param admin:
451 :param admin:
452 :param ldap_dn:
452 :param ldap_dn:
453 :param password:
453 :param password:
454 """
454 """
455
455
456 user = get_user_or_error(userid)
456 user = get_user_or_error(userid)
457
457
458 # call function and store only updated arguments
458 # call function and store only updated arguments
459 updates = {}
459 updates = {}
460
460
461 def store_update(attr, name):
461 def store_update(attr, name):
462 if not isinstance(attr, Optional):
462 if not isinstance(attr, Optional):
463 updates[name] = attr
463 updates[name] = attr
464
464
465 try:
465 try:
466
466
467 store_update(username, 'username')
467 store_update(username, 'username')
468 store_update(password, 'password')
468 store_update(password, 'password')
469 store_update(email, 'email')
469 store_update(email, 'email')
470 store_update(firstname, 'name')
470 store_update(firstname, 'name')
471 store_update(lastname, 'lastname')
471 store_update(lastname, 'lastname')
472 store_update(active, 'active')
472 store_update(active, 'active')
473 store_update(admin, 'admin')
473 store_update(admin, 'admin')
474 store_update(ldap_dn, 'ldap_dn')
474 store_update(ldap_dn, 'ldap_dn')
475
475
476 user = UserModel().update_user(user, **updates)
476 user = UserModel().update_user(user, **updates)
477 Session().commit()
477 Session().commit()
478 return dict(
478 return dict(
479 msg='updated user ID:%s %s' % (user.user_id, user.username),
479 msg='updated user ID:%s %s' % (user.user_id, user.username),
480 user=user.get_api_data()
480 user=user.get_api_data()
481 )
481 )
482 except Exception:
482 except Exception:
483 log.error(traceback.format_exc())
483 log.error(traceback.format_exc())
484 raise JSONRPCError('failed to update user `%s`' % userid)
484 raise JSONRPCError('failed to update user `%s`' % userid)
485
485
486 @HasPermissionAllDecorator('hg.admin')
486 @HasPermissionAllDecorator('hg.admin')
487 def delete_user(self, apiuser, userid):
487 def delete_user(self, apiuser, userid):
488 """"
488 """"
489 Deletes an user
489 Deletes an user
490
490
491 :param apiuser:
491 :param apiuser:
492 :param userid:
492 :param userid:
493 """
493 """
494 user = get_user_or_error(userid)
494 user = get_user_or_error(userid)
495
495
496 try:
496 try:
497 UserModel().delete(userid)
497 UserModel().delete(userid)
498 Session().commit()
498 Session().commit()
499 return dict(
499 return dict(
500 msg='deleted user ID:%s %s' % (user.user_id, user.username),
500 msg='deleted user ID:%s %s' % (user.user_id, user.username),
501 user=None
501 user=None
502 )
502 )
503 except Exception:
503 except Exception:
504 log.error(traceback.format_exc())
504 log.error(traceback.format_exc())
505 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
505 raise JSONRPCError('failed to delete ID:%s %s' % (user.user_id,
506 user.username))
506 user.username))
507
507
508 @HasPermissionAllDecorator('hg.admin')
508 @HasPermissionAllDecorator('hg.admin')
509 def get_users_group(self, apiuser, usersgroupid):
509 def get_users_group(self, apiuser, usersgroupid):
510 """"
510 """"
511 Get user group by name or id
511 Get user group by name or id
512
512
513 :param apiuser:
513 :param apiuser:
514 :param usersgroupid:
514 :param usersgroupid:
515 """
515 """
516 users_group = get_users_group_or_error(usersgroupid)
516 users_group = get_users_group_or_error(usersgroupid)
517
517
518 data = users_group.get_api_data()
518 data = users_group.get_api_data()
519
519
520 members = []
520 members = []
521 for user in users_group.members:
521 for user in users_group.members:
522 user = user.user
522 user = user.user
523 members.append(user.get_api_data())
523 members.append(user.get_api_data())
524 data['members'] = members
524 data['members'] = members
525 return data
525 return data
526
526
527 @HasPermissionAllDecorator('hg.admin')
527 @HasPermissionAllDecorator('hg.admin')
528 def get_users_groups(self, apiuser):
528 def get_users_groups(self, apiuser):
529 """"
529 """"
530 Get all user groups
530 Get all user groups
531
531
532 :param apiuser:
532 :param apiuser:
533 """
533 """
534
534
535 result = []
535 result = []
536 for users_group in UserGroupModel().get_all():
536 for users_group in UserGroupModel().get_all():
537 result.append(users_group.get_api_data())
537 result.append(users_group.get_api_data())
538 return result
538 return result
539
539
540 @HasPermissionAllDecorator('hg.admin')
540 @HasPermissionAllDecorator('hg.admin')
541 def create_users_group(self, apiuser, group_name, active=Optional(True)):
541 def create_users_group(self, apiuser, group_name, active=Optional(True)):
542 """
542 """
543 Creates an new usergroup
543 Creates an new usergroup
544
544
545 :param apiuser:
545 :param apiuser:
546 :param group_name:
546 :param group_name:
547 :param active:
547 :param active:
548 """
548 """
549
549
550 if UserGroupModel().get_by_name(group_name):
550 if UserGroupModel().get_by_name(group_name):
551 raise JSONRPCError("user group `%s` already exist" % group_name)
551 raise JSONRPCError("user group `%s` already exist" % group_name)
552
552
553 try:
553 try:
554 active = Optional.extract(active)
554 active = Optional.extract(active)
555 ug = UserGroupModel().create(name=group_name, active=active)
555 ug = UserGroupModel().create(name=group_name, active=active)
556 Session().commit()
556 Session().commit()
557 return dict(
557 return dict(
558 msg='created new user group `%s`' % group_name,
558 msg='created new user group `%s`' % group_name,
559 users_group=ug.get_api_data()
559 users_group=ug.get_api_data()
560 )
560 )
561 except Exception:
561 except Exception:
562 log.error(traceback.format_exc())
562 log.error(traceback.format_exc())
563 raise JSONRPCError('failed to create group `%s`' % group_name)
563 raise JSONRPCError('failed to create group `%s`' % group_name)
564
564
565 @HasPermissionAllDecorator('hg.admin')
565 @HasPermissionAllDecorator('hg.admin')
566 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
566 def add_user_to_users_group(self, apiuser, usersgroupid, userid):
567 """"
567 """"
568 Add a user to a user group
568 Add a user to a user group
569
569
570 :param apiuser:
570 :param apiuser:
571 :param usersgroupid:
571 :param usersgroupid:
572 :param userid:
572 :param userid:
573 """
573 """
574 user = get_user_or_error(userid)
574 user = get_user_or_error(userid)
575 users_group = get_users_group_or_error(usersgroupid)
575 users_group = get_users_group_or_error(usersgroupid)
576
576
577 try:
577 try:
578 ugm = UserGroupModel().add_user_to_group(users_group, user)
578 ugm = UserGroupModel().add_user_to_group(users_group, user)
579 success = True if ugm != True else False
579 success = True if ugm != True else False
580 msg = 'added member `%s` to user group `%s`' % (
580 msg = 'added member `%s` to user group `%s`' % (
581 user.username, users_group.users_group_name
581 user.username, users_group.users_group_name
582 )
582 )
583 msg = msg if success else 'User is already in that group'
583 msg = msg if success else 'User is already in that group'
584 Session().commit()
584 Session().commit()
585
585
586 return dict(
586 return dict(
587 success=success,
587 success=success,
588 msg=msg
588 msg=msg
589 )
589 )
590 except Exception:
590 except Exception:
591 log.error(traceback.format_exc())
591 log.error(traceback.format_exc())
592 raise JSONRPCError(
592 raise JSONRPCError(
593 'failed to add member to user group `%s`' % (
593 'failed to add member to user group `%s`' % (
594 users_group.users_group_name
594 users_group.users_group_name
595 )
595 )
596 )
596 )
597
597
598 @HasPermissionAllDecorator('hg.admin')
598 @HasPermissionAllDecorator('hg.admin')
599 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
599 def remove_user_from_users_group(self, apiuser, usersgroupid, userid):
600 """
600 """
601 Remove user from a group
601 Remove user from a group
602
602
603 :param apiuser:
603 :param apiuser:
604 :param usersgroupid:
604 :param usersgroupid:
605 :param userid:
605 :param userid:
606 """
606 """
607 user = get_user_or_error(userid)
607 user = get_user_or_error(userid)
608 users_group = get_users_group_or_error(usersgroupid)
608 users_group = get_users_group_or_error(usersgroupid)
609
609
610 try:
610 try:
611 success = UserGroupModel().remove_user_from_group(users_group,
611 success = UserGroupModel().remove_user_from_group(users_group,
612 user)
612 user)
613 msg = 'removed member `%s` from user group `%s`' % (
613 msg = 'removed member `%s` from user group `%s`' % (
614 user.username, users_group.users_group_name
614 user.username, users_group.users_group_name
615 )
615 )
616 msg = msg if success else "User wasn't in group"
616 msg = msg if success else "User wasn't in group"
617 Session().commit()
617 Session().commit()
618 return dict(success=success, msg=msg)
618 return dict(success=success, msg=msg)
619 except Exception:
619 except Exception:
620 log.error(traceback.format_exc())
620 log.error(traceback.format_exc())
621 raise JSONRPCError(
621 raise JSONRPCError(
622 'failed to remove member from user group `%s`' % (
622 'failed to remove member from user group `%s`' % (
623 users_group.users_group_name
623 users_group.users_group_name
624 )
624 )
625 )
625 )
626
626
627 def get_repo(self, apiuser, repoid):
627 def get_repo(self, apiuser, repoid):
628 """"
628 """"
629 Get repository by name
629 Get repository by name
630
630
631 :param apiuser:
631 :param apiuser:
632 :param repoid:
632 :param repoid:
633 """
633 """
634 repo = get_repo_or_error(repoid)
634 repo = get_repo_or_error(repoid)
635
635
636 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
636 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
637 # check if we have admin permission for this repo !
637 # check if we have admin permission for this repo !
638 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
638 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
639 repo_name=repo.repo_name) is False:
639 repo_name=repo.repo_name) is False:
640 raise JSONRPCError('repository `%s` does not exist' % (repoid))
640 raise JSONRPCError('repository `%s` does not exist' % (repoid))
641
641
642 members = []
642 members = []
643 followers = []
643 followers = []
644 for user in repo.repo_to_perm:
644 for user in repo.repo_to_perm:
645 perm = user.permission.permission_name
645 perm = user.permission.permission_name
646 user = user.user
646 user = user.user
647 user_data = user.get_api_data()
647 user_data = user.get_api_data()
648 user_data['type'] = "user"
648 user_data['type'] = "user"
649 user_data['permission'] = perm
649 user_data['permission'] = perm
650 members.append(user_data)
650 members.append(user_data)
651
651
652 for users_group in repo.users_group_to_perm:
652 for users_group in repo.users_group_to_perm:
653 perm = users_group.permission.permission_name
653 perm = users_group.permission.permission_name
654 users_group = users_group.users_group
654 users_group = users_group.users_group
655 users_group_data = users_group.get_api_data()
655 users_group_data = users_group.get_api_data()
656 users_group_data['type'] = "users_group"
656 users_group_data['type'] = "users_group"
657 users_group_data['permission'] = perm
657 users_group_data['permission'] = perm
658 members.append(users_group_data)
658 members.append(users_group_data)
659
659
660 for user in repo.followers:
660 for user in repo.followers:
661 followers.append(user.user.get_api_data())
661 followers.append(user.user.get_api_data())
662
662
663 data = repo.get_api_data()
663 data = repo.get_api_data()
664 data['members'] = members
664 data['members'] = members
665 data['followers'] = followers
665 data['followers'] = followers
666 return data
666 return data
667
667
668 def get_repos(self, apiuser):
668 def get_repos(self, apiuser):
669 """"
669 """"
670 Get all repositories
670 Get all repositories
671
671
672 :param apiuser:
672 :param apiuser:
673 """
673 """
674 result = []
674 result = []
675 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
675 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
676 repos = RepoModel().get_all_user_repos(user=apiuser)
676 repos = RepoModel().get_all_user_repos(user=apiuser)
677 else:
677 else:
678 repos = RepoModel().get_all()
678 repos = RepoModel().get_all()
679
679
680 for repo in repos:
680 for repo in repos:
681 result.append(repo.get_api_data())
681 result.append(repo.get_api_data())
682 return result
682 return result
683
683
684 @HasPermissionAllDecorator('hg.admin')
684 @HasPermissionAllDecorator('hg.admin')
685 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
685 def get_repo_nodes(self, apiuser, repoid, revision, root_path,
686 ret_type='all'):
686 ret_type='all'):
687 """
687 """
688 returns a list of nodes and it's children
688 returns a list of nodes and it's children
689 for a given path at given revision. It's possible to specify ret_type
689 for a given path at given revision. It's possible to specify ret_type
690 to show only files or dirs
690 to show only files or dirs
691
691
692 :param apiuser:
692 :param apiuser:
693 :param repoid: name or id of repository
693 :param repoid: name or id of repository
694 :param revision: revision for which listing should be done
694 :param revision: revision for which listing should be done
695 :param root_path: path from which start displaying
695 :param root_path: path from which start displaying
696 :param ret_type: return type 'all|files|dirs' nodes
696 :param ret_type: return type 'all|files|dirs' nodes
697 """
697 """
698 repo = get_repo_or_error(repoid)
698 repo = get_repo_or_error(repoid)
699 try:
699 try:
700 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
700 _d, _f = ScmModel().get_nodes(repo, revision, root_path,
701 flat=False)
701 flat=False)
702 _map = {
702 _map = {
703 'all': _d + _f,
703 'all': _d + _f,
704 'files': _f,
704 'files': _f,
705 'dirs': _d,
705 'dirs': _d,
706 }
706 }
707 return _map[ret_type]
707 return _map[ret_type]
708 except KeyError:
708 except KeyError:
709 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
709 raise JSONRPCError('ret_type must be one of %s' % _map.keys())
710 except Exception:
710 except Exception:
711 log.error(traceback.format_exc())
711 log.error(traceback.format_exc())
712 raise JSONRPCError(
712 raise JSONRPCError(
713 'failed to get repo: `%s` nodes' % repo.repo_name
713 'failed to get repo: `%s` nodes' % repo.repo_name
714 )
714 )
715
715
716 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
716 @HasPermissionAnyDecorator('hg.admin', 'hg.create.repository')
717 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
717 def create_repo(self, apiuser, repo_name, owner=Optional(OAttr('apiuser')),
718 repo_type=Optional('hg'),
718 repo_type=Optional('hg'),
719 description=Optional(''), private=Optional(False),
719 description=Optional(''), private=Optional(False),
720 clone_uri=Optional(None), landing_rev=Optional('tip'),
720 clone_uri=Optional(None), landing_rev=Optional('tip'),
721 enable_statistics=Optional(False),
721 enable_statistics=Optional(False),
722 enable_locking=Optional(False),
722 enable_locking=Optional(False),
723 enable_downloads=Optional(False)):
723 enable_downloads=Optional(False)):
724 """
724 """
725 Create repository, if clone_url is given it makes a remote clone
725 Create repository, if clone_url is given it makes a remote clone
726 if repo_name is within a group name the groups will be created
726 if repo_name is within a group name the groups will be created
727 automatically if they aren't present
727 automatically if they aren't present
728
728
729 :param apiuser:
729 :param apiuser:
730 :param repo_name:
730 :param repo_name:
731 :param onwer:
731 :param onwer:
732 :param repo_type:
732 :param repo_type:
733 :param description:
733 :param description:
734 :param private:
734 :param private:
735 :param clone_uri:
735 :param clone_uri:
736 :param landing_rev:
736 :param landing_rev:
737 """
737 """
738 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
738 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
739 if not isinstance(owner, Optional):
739 if not isinstance(owner, Optional):
740 #forbid setting owner for non-admins
740 #forbid setting owner for non-admins
741 raise JSONRPCError(
741 raise JSONRPCError(
742 'Only RhodeCode admin can specify `owner` param'
742 'Only RhodeCode admin can specify `owner` param'
743 )
743 )
744 if isinstance(owner, Optional):
744 if isinstance(owner, Optional):
745 owner = apiuser.user_id
745 owner = apiuser.user_id
746
746
747 owner = get_user_or_error(owner)
747 owner = get_user_or_error(owner)
748
748
749 if RepoModel().get_by_repo_name(repo_name):
749 if RepoModel().get_by_repo_name(repo_name):
750 raise JSONRPCError("repo `%s` already exist" % repo_name)
750 raise JSONRPCError("repo `%s` already exist" % repo_name)
751
751
752 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
752 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
753 if isinstance(private, Optional):
753 if isinstance(private, Optional):
754 private = defs.get('repo_private') or Optional.extract(private)
754 private = defs.get('repo_private') or Optional.extract(private)
755 if isinstance(repo_type, Optional):
755 if isinstance(repo_type, Optional):
756 repo_type = defs.get('repo_type')
756 repo_type = defs.get('repo_type')
757 if isinstance(enable_statistics, Optional):
757 if isinstance(enable_statistics, Optional):
758 enable_statistics = defs.get('repo_enable_statistics')
758 enable_statistics = defs.get('repo_enable_statistics')
759 if isinstance(enable_locking, Optional):
759 if isinstance(enable_locking, Optional):
760 enable_locking = defs.get('repo_enable_locking')
760 enable_locking = defs.get('repo_enable_locking')
761 if isinstance(enable_downloads, Optional):
761 if isinstance(enable_downloads, Optional):
762 enable_downloads = defs.get('repo_enable_downloads')
762 enable_downloads = defs.get('repo_enable_downloads')
763
763
764 clone_uri = Optional.extract(clone_uri)
764 clone_uri = Optional.extract(clone_uri)
765 description = Optional.extract(description)
765 description = Optional.extract(description)
766 landing_rev = Optional.extract(landing_rev)
766 landing_rev = Optional.extract(landing_rev)
767
767
768 try:
768 try:
769 # create structure of groups and return the last group
769 # create structure of groups and return the last group
770 group = map_groups(repo_name)
770 group = map_groups(repo_name)
771
771
772 repo = RepoModel().create_repo(
772 repo = RepoModel().create_repo(
773 repo_name=repo_name,
773 repo_name=repo_name,
774 repo_type=repo_type,
774 repo_type=repo_type,
775 description=description,
775 description=description,
776 owner=owner,
776 owner=owner,
777 private=private,
777 private=private,
778 clone_uri=clone_uri,
778 clone_uri=clone_uri,
779 repos_group=group,
779 repos_group=group,
780 landing_rev=landing_rev,
780 landing_rev=landing_rev,
781 enable_statistics=enable_statistics,
781 enable_statistics=enable_statistics,
782 enable_downloads=enable_downloads,
782 enable_downloads=enable_downloads,
783 enable_locking=enable_locking
783 enable_locking=enable_locking
784 )
784 )
785
785
786 Session().commit()
786 Session().commit()
787 return dict(
787 return dict(
788 msg="Created new repository `%s`" % (repo.repo_name),
788 msg="Created new repository `%s`" % (repo.repo_name),
789 repo=repo.get_api_data()
789 repo=repo.get_api_data()
790 )
790 )
791 except Exception:
791 except Exception:
792 log.error(traceback.format_exc())
792 log.error(traceback.format_exc())
793 raise JSONRPCError('failed to create repository `%s`' % repo_name)
793 raise JSONRPCError('failed to create repository `%s`' % repo_name)
794
794
795 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
795 @HasPermissionAnyDecorator('hg.admin', 'hg.fork.repository')
796 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
796 def fork_repo(self, apiuser, repoid, fork_name, owner=Optional(OAttr('apiuser')),
797 description=Optional(''), copy_permissions=Optional(False),
797 description=Optional(''), copy_permissions=Optional(False),
798 private=Optional(False), landing_rev=Optional('tip')):
798 private=Optional(False), landing_rev=Optional('tip')):
799 repo = get_repo_or_error(repoid)
799 repo = get_repo_or_error(repoid)
800 repo_name = repo.repo_name
800 repo_name = repo.repo_name
801
801
802 _repo = RepoModel().get_by_repo_name(fork_name)
802 _repo = RepoModel().get_by_repo_name(fork_name)
803 if _repo:
803 if _repo:
804 type_ = 'fork' if _repo.fork else 'repo'
804 type_ = 'fork' if _repo.fork else 'repo'
805 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
805 raise JSONRPCError("%s `%s` already exist" % (type_, fork_name))
806
806
807 if HasPermissionAnyApi('hg.admin')(user=apiuser):
807 if HasPermissionAnyApi('hg.admin')(user=apiuser):
808 pass
808 pass
809 elif HasRepoPermissionAnyApi('repository.admin',
809 elif HasRepoPermissionAnyApi('repository.admin',
810 'repository.write',
810 'repository.write',
811 'repository.read')(user=apiuser,
811 'repository.read')(user=apiuser,
812 repo_name=repo.repo_name):
812 repo_name=repo.repo_name):
813 if not isinstance(owner, Optional):
813 if not isinstance(owner, Optional):
814 #forbid setting owner for non-admins
814 #forbid setting owner for non-admins
815 raise JSONRPCError(
815 raise JSONRPCError(
816 'Only RhodeCode admin can specify `owner` param'
816 'Only RhodeCode admin can specify `owner` param'
817 )
817 )
818 else:
818 else:
819 raise JSONRPCError('repository `%s` does not exist' % (repoid))
819 raise JSONRPCError('repository `%s` does not exist' % (repoid))
820
820
821 if isinstance(owner, Optional):
821 if isinstance(owner, Optional):
822 owner = apiuser.user_id
822 owner = apiuser.user_id
823
823
824 owner = get_user_or_error(owner)
824 owner = get_user_or_error(owner)
825
825
826 try:
826 try:
827 # create structure of groups and return the last group
827 # create structure of groups and return the last group
828 group = map_groups(fork_name)
828 group = map_groups(fork_name)
829
829
830 form_data = dict(
830 form_data = dict(
831 repo_name=fork_name,
831 repo_name=fork_name,
832 repo_name_full=fork_name,
832 repo_name_full=fork_name,
833 repo_group=group,
833 repo_group=group,
834 repo_type=repo.repo_type,
834 repo_type=repo.repo_type,
835 description=Optional.extract(description),
835 description=Optional.extract(description),
836 private=Optional.extract(private),
836 private=Optional.extract(private),
837 copy_permissions=Optional.extract(copy_permissions),
837 copy_permissions=Optional.extract(copy_permissions),
838 landing_rev=Optional.extract(landing_rev),
838 landing_rev=Optional.extract(landing_rev),
839 update_after_clone=False,
839 update_after_clone=False,
840 fork_parent_id=repo.repo_id,
840 fork_parent_id=repo.repo_id,
841 )
841 )
842 RepoModel().create_fork(form_data, cur_user=owner)
842 RepoModel().create_fork(form_data, cur_user=owner)
843 return dict(
843 return dict(
844 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
844 msg='Created fork of `%s` as `%s`' % (repo.repo_name,
845 fork_name),
845 fork_name),
846 success=True # cannot return the repo data here since fork
846 success=True # cannot return the repo data here since fork
847 # cann be done async
847 # cann be done async
848 )
848 )
849 except Exception:
849 except Exception:
850 log.error(traceback.format_exc())
850 log.error(traceback.format_exc())
851 raise JSONRPCError(
851 raise JSONRPCError(
852 'failed to fork repository `%s` as `%s`' % (repo_name,
852 'failed to fork repository `%s` as `%s`' % (repo_name,
853 fork_name)
853 fork_name)
854 )
854 )
855
855
856 def delete_repo(self, apiuser, repoid):
856 def delete_repo(self, apiuser, repoid, forks=Optional(None)):
857 """
857 """
858 Deletes a given repository
858 Deletes a given repository
859
859
860 :param apiuser:
860 :param apiuser:
861 :param repoid:
861 :param repoid:
862 :param forks: detach or delete, what do do with attached forks for repo
862 """
863 """
863 repo = get_repo_or_error(repoid)
864 repo = get_repo_or_error(repoid)
864
865
865 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
866 if HasPermissionAnyApi('hg.admin')(user=apiuser) is False:
866 # check if we have admin permission for this repo !
867 # check if we have admin permission for this repo !
867 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
868 if HasRepoPermissionAnyApi('repository.admin')(user=apiuser,
868 repo_name=repo.repo_name) is False:
869 repo_name=repo.repo_name) is False:
869 raise JSONRPCError('repository `%s` does not exist' % (repoid))
870 raise JSONRPCError('repository `%s` does not exist' % (repoid))
870
871
871 try:
872 try:
872 RepoModel().delete(repo)
873 handle_forks = Optional.extract(forks)
874 _forks_msg = ''
875 _forks = [f for f in repo.forks]
876 if handle_forks == 'detach':
877 _forks_msg = ' ' + _('Detached %s forks') % len(_forks)
878 elif handle_forks == 'delete':
879 _forks_msg = ' ' + _('Deleted %s forks') % len(_forks)
880 elif _forks:
881 raise JSONRPCError(
882 'Cannot delete `%s` it still contains attached forks'
883 % repo.repo_name
884 )
885
886 RepoModel().delete(repo, forks=forks)
873 Session().commit()
887 Session().commit()
874 return dict(
888 return dict(
875 msg='Deleted repository `%s`' % repo.repo_name,
889 msg='Deleted repository `%s`%s' % (repo.repo_name, _forks_msg),
876 success=True
890 success=True
877 )
891 )
878 except Exception:
892 except Exception:
879 log.error(traceback.format_exc())
893 log.error(traceback.format_exc())
880 raise JSONRPCError(
894 raise JSONRPCError(
881 'failed to delete repository `%s`' % repo.repo_name
895 'failed to delete repository `%s`' % repo.repo_name
882 )
896 )
883
897
884 @HasPermissionAllDecorator('hg.admin')
898 @HasPermissionAllDecorator('hg.admin')
885 def grant_user_permission(self, apiuser, repoid, userid, perm):
899 def grant_user_permission(self, apiuser, repoid, userid, perm):
886 """
900 """
887 Grant permission for user on given repository, or update existing one
901 Grant permission for user on given repository, or update existing one
888 if found
902 if found
889
903
890 :param repoid:
904 :param repoid:
891 :param userid:
905 :param userid:
892 :param perm:
906 :param perm:
893 """
907 """
894 repo = get_repo_or_error(repoid)
908 repo = get_repo_or_error(repoid)
895 user = get_user_or_error(userid)
909 user = get_user_or_error(userid)
896 perm = get_perm_or_error(perm)
910 perm = get_perm_or_error(perm)
897
911
898 try:
912 try:
899
913
900 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
914 RepoModel().grant_user_permission(repo=repo, user=user, perm=perm)
901
915
902 Session().commit()
916 Session().commit()
903 return dict(
917 return dict(
904 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
918 msg='Granted perm: `%s` for user: `%s` in repo: `%s`' % (
905 perm.permission_name, user.username, repo.repo_name
919 perm.permission_name, user.username, repo.repo_name
906 ),
920 ),
907 success=True
921 success=True
908 )
922 )
909 except Exception:
923 except Exception:
910 log.error(traceback.format_exc())
924 log.error(traceback.format_exc())
911 raise JSONRPCError(
925 raise JSONRPCError(
912 'failed to edit permission for user: `%s` in repo: `%s`' % (
926 'failed to edit permission for user: `%s` in repo: `%s`' % (
913 userid, repoid
927 userid, repoid
914 )
928 )
915 )
929 )
916
930
917 @HasPermissionAllDecorator('hg.admin')
931 @HasPermissionAllDecorator('hg.admin')
918 def revoke_user_permission(self, apiuser, repoid, userid):
932 def revoke_user_permission(self, apiuser, repoid, userid):
919 """
933 """
920 Revoke permission for user on given repository
934 Revoke permission for user on given repository
921
935
922 :param apiuser:
936 :param apiuser:
923 :param repoid:
937 :param repoid:
924 :param userid:
938 :param userid:
925 """
939 """
926
940
927 repo = get_repo_or_error(repoid)
941 repo = get_repo_or_error(repoid)
928 user = get_user_or_error(userid)
942 user = get_user_or_error(userid)
929 try:
943 try:
930
944
931 RepoModel().revoke_user_permission(repo=repo, user=user)
945 RepoModel().revoke_user_permission(repo=repo, user=user)
932
946
933 Session().commit()
947 Session().commit()
934 return dict(
948 return dict(
935 msg='Revoked perm for user: `%s` in repo: `%s`' % (
949 msg='Revoked perm for user: `%s` in repo: `%s`' % (
936 user.username, repo.repo_name
950 user.username, repo.repo_name
937 ),
951 ),
938 success=True
952 success=True
939 )
953 )
940 except Exception:
954 except Exception:
941 log.error(traceback.format_exc())
955 log.error(traceback.format_exc())
942 raise JSONRPCError(
956 raise JSONRPCError(
943 'failed to edit permission for user: `%s` in repo: `%s`' % (
957 'failed to edit permission for user: `%s` in repo: `%s`' % (
944 userid, repoid
958 userid, repoid
945 )
959 )
946 )
960 )
947
961
948 @HasPermissionAllDecorator('hg.admin')
962 @HasPermissionAllDecorator('hg.admin')
949 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
963 def grant_users_group_permission(self, apiuser, repoid, usersgroupid,
950 perm):
964 perm):
951 """
965 """
952 Grant permission for user group on given repository, or update
966 Grant permission for user group on given repository, or update
953 existing one if found
967 existing one if found
954
968
955 :param apiuser:
969 :param apiuser:
956 :param repoid:
970 :param repoid:
957 :param usersgroupid:
971 :param usersgroupid:
958 :param perm:
972 :param perm:
959 """
973 """
960 repo = get_repo_or_error(repoid)
974 repo = get_repo_or_error(repoid)
961 perm = get_perm_or_error(perm)
975 perm = get_perm_or_error(perm)
962 users_group = get_users_group_or_error(usersgroupid)
976 users_group = get_users_group_or_error(usersgroupid)
963
977
964 try:
978 try:
965 RepoModel().grant_users_group_permission(repo=repo,
979 RepoModel().grant_users_group_permission(repo=repo,
966 group_name=users_group,
980 group_name=users_group,
967 perm=perm)
981 perm=perm)
968
982
969 Session().commit()
983 Session().commit()
970 return dict(
984 return dict(
971 msg='Granted perm: `%s` for user group: `%s` in '
985 msg='Granted perm: `%s` for user group: `%s` in '
972 'repo: `%s`' % (
986 'repo: `%s`' % (
973 perm.permission_name, users_group.users_group_name,
987 perm.permission_name, users_group.users_group_name,
974 repo.repo_name
988 repo.repo_name
975 ),
989 ),
976 success=True
990 success=True
977 )
991 )
978 except Exception:
992 except Exception:
979 log.error(traceback.format_exc())
993 log.error(traceback.format_exc())
980 raise JSONRPCError(
994 raise JSONRPCError(
981 'failed to edit permission for user group: `%s` in '
995 'failed to edit permission for user group: `%s` in '
982 'repo: `%s`' % (
996 'repo: `%s`' % (
983 usersgroupid, repo.repo_name
997 usersgroupid, repo.repo_name
984 )
998 )
985 )
999 )
986
1000
987 @HasPermissionAllDecorator('hg.admin')
1001 @HasPermissionAllDecorator('hg.admin')
988 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
1002 def revoke_users_group_permission(self, apiuser, repoid, usersgroupid):
989 """
1003 """
990 Revoke permission for user group on given repository
1004 Revoke permission for user group on given repository
991
1005
992 :param apiuser:
1006 :param apiuser:
993 :param repoid:
1007 :param repoid:
994 :param usersgroupid:
1008 :param usersgroupid:
995 """
1009 """
996 repo = get_repo_or_error(repoid)
1010 repo = get_repo_or_error(repoid)
997 users_group = get_users_group_or_error(usersgroupid)
1011 users_group = get_users_group_or_error(usersgroupid)
998
1012
999 try:
1013 try:
1000 RepoModel().revoke_users_group_permission(repo=repo,
1014 RepoModel().revoke_users_group_permission(repo=repo,
1001 group_name=users_group)
1015 group_name=users_group)
1002
1016
1003 Session().commit()
1017 Session().commit()
1004 return dict(
1018 return dict(
1005 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1019 msg='Revoked perm for user group: `%s` in repo: `%s`' % (
1006 users_group.users_group_name, repo.repo_name
1020 users_group.users_group_name, repo.repo_name
1007 ),
1021 ),
1008 success=True
1022 success=True
1009 )
1023 )
1010 except Exception:
1024 except Exception:
1011 log.error(traceback.format_exc())
1025 log.error(traceback.format_exc())
1012 raise JSONRPCError(
1026 raise JSONRPCError(
1013 'failed to edit permission for user group: `%s` in '
1027 'failed to edit permission for user group: `%s` in '
1014 'repo: `%s`' % (
1028 'repo: `%s`' % (
1015 users_group.users_group_name, repo.repo_name
1029 users_group.users_group_name, repo.repo_name
1016 )
1030 )
1017 )
1031 )
@@ -1,76 +1,80 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.exceptions
3 rhodecode.lib.exceptions
4 ~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Set of custom exceptions used in RhodeCode
6 Set of custom exceptions used in RhodeCode
7
7
8 :created_on: Nov 17, 2010
8 :created_on: Nov 17, 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 from webob.exc import HTTPClientError
26 from webob.exc import HTTPClientError
27
27
28
28
29 class LdapUsernameError(Exception):
29 class LdapUsernameError(Exception):
30 pass
30 pass
31
31
32
32
33 class LdapPasswordError(Exception):
33 class LdapPasswordError(Exception):
34 pass
34 pass
35
35
36
36
37 class LdapConnectionError(Exception):
37 class LdapConnectionError(Exception):
38 pass
38 pass
39
39
40
40
41 class LdapImportError(Exception):
41 class LdapImportError(Exception):
42 pass
42 pass
43
43
44
44
45 class DefaultUserException(Exception):
45 class DefaultUserException(Exception):
46 pass
46 pass
47
47
48
48
49 class UserOwnsReposException(Exception):
49 class UserOwnsReposException(Exception):
50 pass
50 pass
51
51
52
52
53 class UserGroupsAssignedException(Exception):
53 class UserGroupsAssignedException(Exception):
54 pass
54 pass
55
55
56
56
57 class StatusChangeOnClosedPullRequestError(Exception):
57 class StatusChangeOnClosedPullRequestError(Exception):
58 pass
58 pass
59
59
60
60
61 class AttachedForksError(Exception):
62 pass
63
64
61 class HTTPLockedRC(HTTPClientError):
65 class HTTPLockedRC(HTTPClientError):
62 """
66 """
63 Special Exception For locked Repos in RhodeCode, the return code can
67 Special Exception For locked Repos in RhodeCode, the return code can
64 be overwritten by _code keyword argument passed into constructors
68 be overwritten by _code keyword argument passed into constructors
65 """
69 """
66 code = 423
70 code = 423
67 title = explanation = 'Repository Locked'
71 title = explanation = 'Repository Locked'
68
72
69 def __init__(self, reponame, username, *args, **kwargs):
73 def __init__(self, reponame, username, *args, **kwargs):
70 from rhodecode import CONFIG
74 from rhodecode import CONFIG
71 from rhodecode.lib.utils2 import safe_int
75 from rhodecode.lib.utils2 import safe_int
72 _code = CONFIG.get('lock_ret_code')
76 _code = CONFIG.get('lock_ret_code')
73 self.code = safe_int(_code, self.code)
77 self.code = safe_int(_code, self.code)
74 self.title = self.explanation = ('Repository `%s` locked by '
78 self.title = self.explanation = ('Repository `%s` locked by '
75 'user `%s`' % (reponame, username))
79 'user `%s`' % (reponame, username))
76 super(HTTPLockedRC, self).__init__(*args, **kwargs)
80 super(HTTPLockedRC, self).__init__(*args, **kwargs)
@@ -1,684 +1,703 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 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 shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from rhodecode.lib.vcs.backends import get_backend
32 from rhodecode.lib.vcs.backends import get_backend
33 from rhodecode.lib.compat import json
33 from rhodecode.lib.compat import json
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 remove_prefix, obfuscate_url_pw
35 remove_prefix, obfuscate_url_pw
36 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
38
38
39 from rhodecode.model import BaseModel
39 from rhodecode.model import BaseModel
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
41 Statistics, UserGroup, UserGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
42 RhodeCodeSetting, RepositoryField
42 RhodeCodeSetting, RepositoryField
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.auth import HasRepoPermissionAny
45 from rhodecode.lib.exceptions import AttachedForksError
45
46
46 log = logging.getLogger(__name__)
47 log = logging.getLogger(__name__)
47
48
48
49
49 class RepoModel(BaseModel):
50 class RepoModel(BaseModel):
50
51
51 cls = Repository
52 cls = Repository
52 URL_SEPARATOR = Repository.url_sep()
53 URL_SEPARATOR = Repository.url_sep()
53
54
54 def __get_users_group(self, users_group):
55 def __get_users_group(self, users_group):
55 return self._get_instance(UserGroup, users_group,
56 return self._get_instance(UserGroup, users_group,
56 callback=UserGroup.get_by_group_name)
57 callback=UserGroup.get_by_group_name)
57
58
58 def _get_repos_group(self, repos_group):
59 def _get_repos_group(self, repos_group):
59 return self._get_instance(RepoGroup, repos_group,
60 return self._get_instance(RepoGroup, repos_group,
60 callback=RepoGroup.get_by_group_name)
61 callback=RepoGroup.get_by_group_name)
61
62
62 @LazyProperty
63 @LazyProperty
63 def repos_path(self):
64 def repos_path(self):
64 """
65 """
65 Get's the repositories root path from database
66 Get's the repositories root path from database
66 """
67 """
67
68
68 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
69 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
69 return q.ui_value
70 return q.ui_value
70
71
71 def get(self, repo_id, cache=False):
72 def get(self, repo_id, cache=False):
72 repo = self.sa.query(Repository)\
73 repo = self.sa.query(Repository)\
73 .filter(Repository.repo_id == repo_id)
74 .filter(Repository.repo_id == repo_id)
74
75
75 if cache:
76 if cache:
76 repo = repo.options(FromCache("sql_cache_short",
77 repo = repo.options(FromCache("sql_cache_short",
77 "get_repo_%s" % repo_id))
78 "get_repo_%s" % repo_id))
78 return repo.scalar()
79 return repo.scalar()
79
80
80 def get_repo(self, repository):
81 def get_repo(self, repository):
81 return self._get_repo(repository)
82 return self._get_repo(repository)
82
83
83 def get_by_repo_name(self, repo_name, cache=False):
84 def get_by_repo_name(self, repo_name, cache=False):
84 repo = self.sa.query(Repository)\
85 repo = self.sa.query(Repository)\
85 .filter(Repository.repo_name == repo_name)
86 .filter(Repository.repo_name == repo_name)
86
87
87 if cache:
88 if cache:
88 repo = repo.options(FromCache("sql_cache_short",
89 repo = repo.options(FromCache("sql_cache_short",
89 "get_repo_%s" % repo_name))
90 "get_repo_%s" % repo_name))
90 return repo.scalar()
91 return repo.scalar()
91
92
92 def get_all_user_repos(self, user):
93 def get_all_user_repos(self, user):
93 """
94 """
94 Get's all repositories that user have at least read access
95 Get's all repositories that user have at least read access
95
96
96 :param user:
97 :param user:
97 :type user:
98 :type user:
98 """
99 """
99 from rhodecode.lib.auth import AuthUser
100 from rhodecode.lib.auth import AuthUser
100 user = self._get_user(user)
101 user = self._get_user(user)
101 repos = AuthUser(user_id=user.user_id).permissions['repositories']
102 repos = AuthUser(user_id=user.user_id).permissions['repositories']
102 access_check = lambda r: r[1] in ['repository.read',
103 access_check = lambda r: r[1] in ['repository.read',
103 'repository.write',
104 'repository.write',
104 'repository.admin']
105 'repository.admin']
105 repos = [x[0] for x in filter(access_check, repos.items())]
106 repos = [x[0] for x in filter(access_check, repos.items())]
106 return Repository.query().filter(Repository.repo_name.in_(repos))
107 return Repository.query().filter(Repository.repo_name.in_(repos))
107
108
108 def get_users_js(self):
109 def get_users_js(self):
109 users = self.sa.query(User).filter(User.active == True).all()
110 users = self.sa.query(User).filter(User.active == True).all()
110 return json.dumps([
111 return json.dumps([
111 {
112 {
112 'id': u.user_id,
113 'id': u.user_id,
113 'fname': u.name,
114 'fname': u.name,
114 'lname': u.lastname,
115 'lname': u.lastname,
115 'nname': u.username,
116 'nname': u.username,
116 'gravatar_lnk': h.gravatar_url(u.email, 14)
117 'gravatar_lnk': h.gravatar_url(u.email, 14)
117 } for u in users]
118 } for u in users]
118 )
119 )
119
120
120 def get_users_groups_js(self):
121 def get_users_groups_js(self):
121 users_groups = self.sa.query(UserGroup)\
122 users_groups = self.sa.query(UserGroup)\
122 .filter(UserGroup.users_group_active == True).all()
123 .filter(UserGroup.users_group_active == True).all()
123
124
124 return json.dumps([
125 return json.dumps([
125 {
126 {
126 'id': gr.users_group_id,
127 'id': gr.users_group_id,
127 'grname': gr.users_group_name,
128 'grname': gr.users_group_name,
128 'grmembers': len(gr.members),
129 'grmembers': len(gr.members),
129 } for gr in users_groups]
130 } for gr in users_groups]
130 )
131 )
131
132
132 @classmethod
133 @classmethod
133 def _render_datatable(cls, tmpl, *args, **kwargs):
134 def _render_datatable(cls, tmpl, *args, **kwargs):
134 import rhodecode
135 import rhodecode
135 from pylons import tmpl_context as c
136 from pylons import tmpl_context as c
136 from pylons.i18n.translation import _
137 from pylons.i18n.translation import _
137
138
138 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
139 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
139 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
140 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
140
141
141 tmpl = template.get_def(tmpl)
142 tmpl = template.get_def(tmpl)
142 kwargs.update(dict(_=_, h=h, c=c))
143 kwargs.update(dict(_=_, h=h, c=c))
143 return tmpl.render(*args, **kwargs)
144 return tmpl.render(*args, **kwargs)
144
145
145 @classmethod
146 @classmethod
146 def update_repoinfo(cls, repositories=None):
147 def update_repoinfo(cls, repositories=None):
147 if not repositories:
148 if not repositories:
148 repositories = Repository.getAll()
149 repositories = Repository.getAll()
149 for repo in repositories:
150 for repo in repositories:
150 repo.update_changeset_cache()
151 repo.update_changeset_cache()
151
152
152 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
153 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
153 super_user_actions=False):
154 super_user_actions=False):
154 _render = self._render_datatable
155 _render = self._render_datatable
155
156
156 def quick_menu(repo_name):
157 def quick_menu(repo_name):
157 return _render('quick_menu', repo_name)
158 return _render('quick_menu', repo_name)
158
159
159 def repo_lnk(name, rtype, private, fork_of):
160 def repo_lnk(name, rtype, private, fork_of):
160 return _render('repo_name', name, rtype, private, fork_of,
161 return _render('repo_name', name, rtype, private, fork_of,
161 short_name=not admin, admin=False)
162 short_name=not admin, admin=False)
162
163
163 def last_change(last_change):
164 def last_change(last_change):
164 return _render("last_change", last_change)
165 return _render("last_change", last_change)
165
166
166 def rss_lnk(repo_name):
167 def rss_lnk(repo_name):
167 return _render("rss", repo_name)
168 return _render("rss", repo_name)
168
169
169 def atom_lnk(repo_name):
170 def atom_lnk(repo_name):
170 return _render("atom", repo_name)
171 return _render("atom", repo_name)
171
172
172 def last_rev(repo_name, cs_cache):
173 def last_rev(repo_name, cs_cache):
173 return _render('revision', repo_name, cs_cache.get('revision'),
174 return _render('revision', repo_name, cs_cache.get('revision'),
174 cs_cache.get('raw_id'), cs_cache.get('author'),
175 cs_cache.get('raw_id'), cs_cache.get('author'),
175 cs_cache.get('message'))
176 cs_cache.get('message'))
176
177
177 def desc(desc):
178 def desc(desc):
178 from pylons import tmpl_context as c
179 from pylons import tmpl_context as c
179 if c.visual.stylify_metatags:
180 if c.visual.stylify_metatags:
180 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
181 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
181 else:
182 else:
182 return h.urlify_text(h.truncate(desc, 60))
183 return h.urlify_text(h.truncate(desc, 60))
183
184
184 def repo_actions(repo_name):
185 def repo_actions(repo_name):
185 return _render('repo_actions', repo_name, super_user_actions)
186 return _render('repo_actions', repo_name, super_user_actions)
186
187
187 def owner_actions(user_id, username):
188 def owner_actions(user_id, username):
188 return _render('user_name', user_id, username)
189 return _render('user_name', user_id, username)
189
190
190 repos_data = []
191 repos_data = []
191 for repo in repos_list:
192 for repo in repos_list:
192 if perm_check:
193 if perm_check:
193 # check permission at this level
194 # check permission at this level
194 if not HasRepoPermissionAny(
195 if not HasRepoPermissionAny(
195 'repository.read', 'repository.write', 'repository.admin'
196 'repository.read', 'repository.write', 'repository.admin'
196 )(repo.repo_name, 'get_repos_as_dict check'):
197 )(repo.repo_name, 'get_repos_as_dict check'):
197 continue
198 continue
198 cs_cache = repo.changeset_cache
199 cs_cache = repo.changeset_cache
199 row = {
200 row = {
200 "menu": quick_menu(repo.repo_name),
201 "menu": quick_menu(repo.repo_name),
201 "raw_name": repo.repo_name.lower(),
202 "raw_name": repo.repo_name.lower(),
202 "name": repo_lnk(repo.repo_name, repo.repo_type,
203 "name": repo_lnk(repo.repo_name, repo.repo_type,
203 repo.private, repo.fork),
204 repo.private, repo.fork),
204 "last_change": last_change(repo.last_db_change),
205 "last_change": last_change(repo.last_db_change),
205 "last_changeset": last_rev(repo.repo_name, cs_cache),
206 "last_changeset": last_rev(repo.repo_name, cs_cache),
206 "raw_tip": cs_cache.get('revision'),
207 "raw_tip": cs_cache.get('revision'),
207 "desc": desc(repo.description),
208 "desc": desc(repo.description),
208 "owner": h.person(repo.user.username),
209 "owner": h.person(repo.user.username),
209 "rss": rss_lnk(repo.repo_name),
210 "rss": rss_lnk(repo.repo_name),
210 "atom": atom_lnk(repo.repo_name),
211 "atom": atom_lnk(repo.repo_name),
211
212
212 }
213 }
213 if admin:
214 if admin:
214 row.update({
215 row.update({
215 "action": repo_actions(repo.repo_name),
216 "action": repo_actions(repo.repo_name),
216 "owner": owner_actions(repo.user.user_id,
217 "owner": owner_actions(repo.user.user_id,
217 h.person(repo.user.username))
218 h.person(repo.user.username))
218 })
219 })
219 repos_data.append(row)
220 repos_data.append(row)
220
221
221 return {
222 return {
222 "totalRecords": len(repos_list),
223 "totalRecords": len(repos_list),
223 "startIndex": 0,
224 "startIndex": 0,
224 "sort": "name",
225 "sort": "name",
225 "dir": "asc",
226 "dir": "asc",
226 "records": repos_data
227 "records": repos_data
227 }
228 }
228
229
229 def _get_defaults(self, repo_name):
230 def _get_defaults(self, repo_name):
230 """
231 """
231 Get's information about repository, and returns a dict for
232 Get's information about repository, and returns a dict for
232 usage in forms
233 usage in forms
233
234
234 :param repo_name:
235 :param repo_name:
235 """
236 """
236
237
237 repo_info = Repository.get_by_repo_name(repo_name)
238 repo_info = Repository.get_by_repo_name(repo_name)
238
239
239 if repo_info is None:
240 if repo_info is None:
240 return None
241 return None
241
242
242 defaults = repo_info.get_dict()
243 defaults = repo_info.get_dict()
243 group, repo_name, repo_name_full = repo_info.groups_and_repo
244 group, repo_name, repo_name_full = repo_info.groups_and_repo
244 defaults['repo_name'] = repo_name
245 defaults['repo_name'] = repo_name
245 defaults['repo_group'] = getattr(group[-1] if group else None,
246 defaults['repo_group'] = getattr(group[-1] if group else None,
246 'group_id', None)
247 'group_id', None)
247
248
248 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
249 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
249 (1, 'repo_description'), (1, 'repo_enable_locking'),
250 (1, 'repo_description'), (1, 'repo_enable_locking'),
250 (1, 'repo_landing_rev'), (0, 'clone_uri'),
251 (1, 'repo_landing_rev'), (0, 'clone_uri'),
251 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
252 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
252 attr = k
253 attr = k
253 if strip:
254 if strip:
254 attr = remove_prefix(k, 'repo_')
255 attr = remove_prefix(k, 'repo_')
255
256
256 defaults[k] = defaults[attr]
257 defaults[k] = defaults[attr]
257
258
258 # fill owner
259 # fill owner
259 if repo_info.user:
260 if repo_info.user:
260 defaults.update({'user': repo_info.user.username})
261 defaults.update({'user': repo_info.user.username})
261 else:
262 else:
262 replacement_user = User.query().filter(User.admin ==
263 replacement_user = User.query().filter(User.admin ==
263 True).first().username
264 True).first().username
264 defaults.update({'user': replacement_user})
265 defaults.update({'user': replacement_user})
265
266
266 # fill repository users
267 # fill repository users
267 for p in repo_info.repo_to_perm:
268 for p in repo_info.repo_to_perm:
268 defaults.update({'u_perm_%s' % p.user.username:
269 defaults.update({'u_perm_%s' % p.user.username:
269 p.permission.permission_name})
270 p.permission.permission_name})
270
271
271 # fill repository groups
272 # fill repository groups
272 for p in repo_info.users_group_to_perm:
273 for p in repo_info.users_group_to_perm:
273 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
274 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
274 p.permission.permission_name})
275 p.permission.permission_name})
275
276
276 return defaults
277 return defaults
277
278
278 def update(self, org_repo_name, **kwargs):
279 def update(self, org_repo_name, **kwargs):
279 try:
280 try:
280 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
281 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
281
282
282 if 'user' in kwargs:
283 if 'user' in kwargs:
283 cur_repo.user = User.get_by_username(kwargs['user'])
284 cur_repo.user = User.get_by_username(kwargs['user'])
284
285
285 if 'repo_group' in kwargs:
286 if 'repo_group' in kwargs:
286 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
287 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
287
288
288 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
289 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
289 (1, 'repo_description'), (1, 'repo_enable_locking'),
290 (1, 'repo_description'), (1, 'repo_enable_locking'),
290 (1, 'repo_landing_rev'), (0, 'clone_uri'),
291 (1, 'repo_landing_rev'), (0, 'clone_uri'),
291 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
292 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
292 if k in kwargs:
293 if k in kwargs:
293 val = kwargs[k]
294 val = kwargs[k]
294 if strip:
295 if strip:
295 k = remove_prefix(k, 'repo_')
296 k = remove_prefix(k, 'repo_')
296 setattr(cur_repo, k, val)
297 setattr(cur_repo, k, val)
297
298
298 new_name = cur_repo.get_new_name(kwargs['repo_name'])
299 new_name = cur_repo.get_new_name(kwargs['repo_name'])
299 cur_repo.repo_name = new_name
300 cur_repo.repo_name = new_name
300 #if private flag is set, reset default permission to NONE
301 #if private flag is set, reset default permission to NONE
301
302
302 if kwargs.get('repo_private'):
303 if kwargs.get('repo_private'):
303 EMPTY_PERM = 'repository.none'
304 EMPTY_PERM = 'repository.none'
304 RepoModel().grant_user_permission(
305 RepoModel().grant_user_permission(
305 repo=cur_repo, user='default', perm=EMPTY_PERM
306 repo=cur_repo, user='default', perm=EMPTY_PERM
306 )
307 )
307 #handle extra fields
308 #handle extra fields
308 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
309 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
309 k = RepositoryField.un_prefix_key(field)
310 k = RepositoryField.un_prefix_key(field)
310 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
311 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
311 if ex_field:
312 if ex_field:
312 ex_field.field_value = kwargs[field]
313 ex_field.field_value = kwargs[field]
313 self.sa.add(ex_field)
314 self.sa.add(ex_field)
314 self.sa.add(cur_repo)
315 self.sa.add(cur_repo)
315
316
316 if org_repo_name != new_name:
317 if org_repo_name != new_name:
317 # rename repository
318 # rename repository
318 self.__rename_repo(old=org_repo_name, new=new_name)
319 self.__rename_repo(old=org_repo_name, new=new_name)
319
320
320 return cur_repo
321 return cur_repo
321 except Exception:
322 except Exception:
322 log.error(traceback.format_exc())
323 log.error(traceback.format_exc())
323 raise
324 raise
324
325
325 def create_repo(self, repo_name, repo_type, description, owner,
326 def create_repo(self, repo_name, repo_type, description, owner,
326 private=False, clone_uri=None, repos_group=None,
327 private=False, clone_uri=None, repos_group=None,
327 landing_rev='tip', just_db=False, fork_of=None,
328 landing_rev='tip', just_db=False, fork_of=None,
328 copy_fork_permissions=False, enable_statistics=False,
329 copy_fork_permissions=False, enable_statistics=False,
329 enable_locking=False, enable_downloads=False):
330 enable_locking=False, enable_downloads=False):
330 """
331 """
331 Create repository
332 Create repository
332
333
333 """
334 """
334 from rhodecode.model.scm import ScmModel
335 from rhodecode.model.scm import ScmModel
335
336
336 owner = self._get_user(owner)
337 owner = self._get_user(owner)
337 fork_of = self._get_repo(fork_of)
338 fork_of = self._get_repo(fork_of)
338 repos_group = self._get_repos_group(repos_group)
339 repos_group = self._get_repos_group(repos_group)
339 try:
340 try:
340
341
341 # repo name is just a name of repository
342 # repo name is just a name of repository
342 # while repo_name_full is a full qualified name that is combined
343 # while repo_name_full is a full qualified name that is combined
343 # with name and path of group
344 # with name and path of group
344 repo_name_full = repo_name
345 repo_name_full = repo_name
345 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
346 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
346
347
347 new_repo = Repository()
348 new_repo = Repository()
348 new_repo.enable_statistics = False
349 new_repo.enable_statistics = False
349 new_repo.repo_name = repo_name_full
350 new_repo.repo_name = repo_name_full
350 new_repo.repo_type = repo_type
351 new_repo.repo_type = repo_type
351 new_repo.user = owner
352 new_repo.user = owner
352 new_repo.group = repos_group
353 new_repo.group = repos_group
353 new_repo.description = description or repo_name
354 new_repo.description = description or repo_name
354 new_repo.private = private
355 new_repo.private = private
355 new_repo.clone_uri = clone_uri
356 new_repo.clone_uri = clone_uri
356 new_repo.landing_rev = landing_rev
357 new_repo.landing_rev = landing_rev
357
358
358 new_repo.enable_statistics = enable_statistics
359 new_repo.enable_statistics = enable_statistics
359 new_repo.enable_locking = enable_locking
360 new_repo.enable_locking = enable_locking
360 new_repo.enable_downloads = enable_downloads
361 new_repo.enable_downloads = enable_downloads
361
362
362 if repos_group:
363 if repos_group:
363 new_repo.enable_locking = repos_group.enable_locking
364 new_repo.enable_locking = repos_group.enable_locking
364
365
365 if fork_of:
366 if fork_of:
366 parent_repo = fork_of
367 parent_repo = fork_of
367 new_repo.fork = parent_repo
368 new_repo.fork = parent_repo
368
369
369 self.sa.add(new_repo)
370 self.sa.add(new_repo)
370
371
371 def _create_default_perms():
372 def _create_default_perms():
372 # create default permission
373 # create default permission
373 repo_to_perm = UserRepoToPerm()
374 repo_to_perm = UserRepoToPerm()
374 default = 'repository.read'
375 default = 'repository.read'
375 for p in User.get_by_username('default').user_perms:
376 for p in User.get_by_username('default').user_perms:
376 if p.permission.permission_name.startswith('repository.'):
377 if p.permission.permission_name.startswith('repository.'):
377 default = p.permission.permission_name
378 default = p.permission.permission_name
378 break
379 break
379
380
380 default_perm = 'repository.none' if private else default
381 default_perm = 'repository.none' if private else default
381
382
382 repo_to_perm.permission_id = self.sa.query(Permission)\
383 repo_to_perm.permission_id = self.sa.query(Permission)\
383 .filter(Permission.permission_name == default_perm)\
384 .filter(Permission.permission_name == default_perm)\
384 .one().permission_id
385 .one().permission_id
385
386
386 repo_to_perm.repository = new_repo
387 repo_to_perm.repository = new_repo
387 repo_to_perm.user_id = User.get_by_username('default').user_id
388 repo_to_perm.user_id = User.get_by_username('default').user_id
388
389
389 self.sa.add(repo_to_perm)
390 self.sa.add(repo_to_perm)
390
391
391 if fork_of:
392 if fork_of:
392 if copy_fork_permissions:
393 if copy_fork_permissions:
393 repo = fork_of
394 repo = fork_of
394 user_perms = UserRepoToPerm.query()\
395 user_perms = UserRepoToPerm.query()\
395 .filter(UserRepoToPerm.repository == repo).all()
396 .filter(UserRepoToPerm.repository == repo).all()
396 group_perms = UserGroupRepoToPerm.query()\
397 group_perms = UserGroupRepoToPerm.query()\
397 .filter(UserGroupRepoToPerm.repository == repo).all()
398 .filter(UserGroupRepoToPerm.repository == repo).all()
398
399
399 for perm in user_perms:
400 for perm in user_perms:
400 UserRepoToPerm.create(perm.user, new_repo,
401 UserRepoToPerm.create(perm.user, new_repo,
401 perm.permission)
402 perm.permission)
402
403
403 for perm in group_perms:
404 for perm in group_perms:
404 UserGroupRepoToPerm.create(perm.users_group, new_repo,
405 UserGroupRepoToPerm.create(perm.users_group, new_repo,
405 perm.permission)
406 perm.permission)
406 else:
407 else:
407 _create_default_perms()
408 _create_default_perms()
408 else:
409 else:
409 _create_default_perms()
410 _create_default_perms()
410
411
411 if not just_db:
412 if not just_db:
412 self.__create_repo(repo_name, repo_type,
413 self.__create_repo(repo_name, repo_type,
413 repos_group,
414 repos_group,
414 clone_uri)
415 clone_uri)
415 log_create_repository(new_repo.get_dict(),
416 log_create_repository(new_repo.get_dict(),
416 created_by=owner.username)
417 created_by=owner.username)
417
418
418 # now automatically start following this repository as owner
419 # now automatically start following this repository as owner
419 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
420 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
420 owner.user_id)
421 owner.user_id)
421 return new_repo
422 return new_repo
422 except Exception:
423 except Exception:
423 log.error(traceback.format_exc())
424 log.error(traceback.format_exc())
424 raise
425 raise
425
426
426 def create(self, form_data, cur_user, just_db=False, fork=None):
427 def create(self, form_data, cur_user, just_db=False, fork=None):
427 """
428 """
428 Backward compatibility function, just a wrapper on top of create_repo
429 Backward compatibility function, just a wrapper on top of create_repo
429
430
430 :param form_data:
431 :param form_data:
431 :param cur_user:
432 :param cur_user:
432 :param just_db:
433 :param just_db:
433 :param fork:
434 :param fork:
434 """
435 """
435 owner = cur_user
436 owner = cur_user
436 repo_name = form_data['repo_name_full']
437 repo_name = form_data['repo_name_full']
437 repo_type = form_data['repo_type']
438 repo_type = form_data['repo_type']
438 description = form_data['repo_description']
439 description = form_data['repo_description']
439 private = form_data['repo_private']
440 private = form_data['repo_private']
440 clone_uri = form_data.get('clone_uri')
441 clone_uri = form_data.get('clone_uri')
441 repos_group = form_data['repo_group']
442 repos_group = form_data['repo_group']
442 landing_rev = form_data['repo_landing_rev']
443 landing_rev = form_data['repo_landing_rev']
443 copy_fork_permissions = form_data.get('copy_permissions')
444 copy_fork_permissions = form_data.get('copy_permissions')
444 fork_of = form_data.get('fork_parent_id')
445 fork_of = form_data.get('fork_parent_id')
445
446
446 ## repo creation defaults, private and repo_type are filled in form
447 ## repo creation defaults, private and repo_type are filled in form
447 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
448 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
448 enable_statistics = defs.get('repo_enable_statistics')
449 enable_statistics = defs.get('repo_enable_statistics')
449 enable_locking = defs.get('repo_enable_locking')
450 enable_locking = defs.get('repo_enable_locking')
450 enable_downloads = defs.get('repo_enable_downloads')
451 enable_downloads = defs.get('repo_enable_downloads')
451
452
452 return self.create_repo(
453 return self.create_repo(
453 repo_name, repo_type, description, owner, private, clone_uri,
454 repo_name, repo_type, description, owner, private, clone_uri,
454 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
455 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
455 enable_statistics, enable_locking, enable_downloads
456 enable_statistics, enable_locking, enable_downloads
456 )
457 )
457
458
458 def create_fork(self, form_data, cur_user):
459 def create_fork(self, form_data, cur_user):
459 """
460 """
460 Simple wrapper into executing celery task for fork creation
461 Simple wrapper into executing celery task for fork creation
461
462
462 :param form_data:
463 :param form_data:
463 :param cur_user:
464 :param cur_user:
464 """
465 """
465 from rhodecode.lib.celerylib import tasks, run_task
466 from rhodecode.lib.celerylib import tasks, run_task
466 run_task(tasks.create_repo_fork, form_data, cur_user)
467 run_task(tasks.create_repo_fork, form_data, cur_user)
467
468
468 def delete(self, repo):
469 def delete(self, repo, forks=None):
470 """
471 Delete given repository, forks parameter defines what do do with
472 attached forks. Throws AttachedForksError if deleted repo has attached
473 forks
474
475 :param repo:
476 :param forks: str 'delete' or 'detach'
477 """
469 repo = self._get_repo(repo)
478 repo = self._get_repo(repo)
470 if repo:
479 if repo:
480 if forks == 'detach':
481 for r in repo.forks:
482 r.fork = None
483 self.sa.add(r)
484 elif forks == 'delete':
485 for r in repo.forks:
486 self.delete(r, forks='delete')
487 elif [f for f in repo.forks]:
488 raise AttachedForksError()
489
471 old_repo_dict = repo.get_dict()
490 old_repo_dict = repo.get_dict()
472 owner = repo.user
491 owner = repo.user
473 try:
492 try:
474 self.sa.delete(repo)
493 self.sa.delete(repo)
475 self.__delete_repo(repo)
494 self.__delete_repo(repo)
476 log_delete_repository(old_repo_dict,
495 log_delete_repository(old_repo_dict,
477 deleted_by=owner.username)
496 deleted_by=owner.username)
478 except Exception:
497 except Exception:
479 log.error(traceback.format_exc())
498 log.error(traceback.format_exc())
480 raise
499 raise
481
500
482 def grant_user_permission(self, repo, user, perm):
501 def grant_user_permission(self, repo, user, perm):
483 """
502 """
484 Grant permission for user on given repository, or update existing one
503 Grant permission for user on given repository, or update existing one
485 if found
504 if found
486
505
487 :param repo: Instance of Repository, repository_id, or repository name
506 :param repo: Instance of Repository, repository_id, or repository name
488 :param user: Instance of User, user_id or username
507 :param user: Instance of User, user_id or username
489 :param perm: Instance of Permission, or permission_name
508 :param perm: Instance of Permission, or permission_name
490 """
509 """
491 user = self._get_user(user)
510 user = self._get_user(user)
492 repo = self._get_repo(repo)
511 repo = self._get_repo(repo)
493 permission = self._get_perm(perm)
512 permission = self._get_perm(perm)
494
513
495 # check if we have that permission already
514 # check if we have that permission already
496 obj = self.sa.query(UserRepoToPerm)\
515 obj = self.sa.query(UserRepoToPerm)\
497 .filter(UserRepoToPerm.user == user)\
516 .filter(UserRepoToPerm.user == user)\
498 .filter(UserRepoToPerm.repository == repo)\
517 .filter(UserRepoToPerm.repository == repo)\
499 .scalar()
518 .scalar()
500 if obj is None:
519 if obj is None:
501 # create new !
520 # create new !
502 obj = UserRepoToPerm()
521 obj = UserRepoToPerm()
503 obj.repository = repo
522 obj.repository = repo
504 obj.user = user
523 obj.user = user
505 obj.permission = permission
524 obj.permission = permission
506 self.sa.add(obj)
525 self.sa.add(obj)
507 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
526 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
508
527
509 def revoke_user_permission(self, repo, user):
528 def revoke_user_permission(self, repo, user):
510 """
529 """
511 Revoke permission for user on given repository
530 Revoke permission for user on given repository
512
531
513 :param repo: Instance of Repository, repository_id, or repository name
532 :param repo: Instance of Repository, repository_id, or repository name
514 :param user: Instance of User, user_id or username
533 :param user: Instance of User, user_id or username
515 """
534 """
516
535
517 user = self._get_user(user)
536 user = self._get_user(user)
518 repo = self._get_repo(repo)
537 repo = self._get_repo(repo)
519
538
520 obj = self.sa.query(UserRepoToPerm)\
539 obj = self.sa.query(UserRepoToPerm)\
521 .filter(UserRepoToPerm.repository == repo)\
540 .filter(UserRepoToPerm.repository == repo)\
522 .filter(UserRepoToPerm.user == user)\
541 .filter(UserRepoToPerm.user == user)\
523 .scalar()
542 .scalar()
524 if obj:
543 if obj:
525 self.sa.delete(obj)
544 self.sa.delete(obj)
526 log.debug('Revoked perm on %s on %s' % (repo, user))
545 log.debug('Revoked perm on %s on %s' % (repo, user))
527
546
528 def grant_users_group_permission(self, repo, group_name, perm):
547 def grant_users_group_permission(self, repo, group_name, perm):
529 """
548 """
530 Grant permission for user group on given repository, or update
549 Grant permission for user group on given repository, or update
531 existing one if found
550 existing one if found
532
551
533 :param repo: Instance of Repository, repository_id, or repository name
552 :param repo: Instance of Repository, repository_id, or repository name
534 :param group_name: Instance of UserGroup, users_group_id,
553 :param group_name: Instance of UserGroup, users_group_id,
535 or user group name
554 or user group name
536 :param perm: Instance of Permission, or permission_name
555 :param perm: Instance of Permission, or permission_name
537 """
556 """
538 repo = self._get_repo(repo)
557 repo = self._get_repo(repo)
539 group_name = self.__get_users_group(group_name)
558 group_name = self.__get_users_group(group_name)
540 permission = self._get_perm(perm)
559 permission = self._get_perm(perm)
541
560
542 # check if we have that permission already
561 # check if we have that permission already
543 obj = self.sa.query(UserGroupRepoToPerm)\
562 obj = self.sa.query(UserGroupRepoToPerm)\
544 .filter(UserGroupRepoToPerm.users_group == group_name)\
563 .filter(UserGroupRepoToPerm.users_group == group_name)\
545 .filter(UserGroupRepoToPerm.repository == repo)\
564 .filter(UserGroupRepoToPerm.repository == repo)\
546 .scalar()
565 .scalar()
547
566
548 if obj is None:
567 if obj is None:
549 # create new
568 # create new
550 obj = UserGroupRepoToPerm()
569 obj = UserGroupRepoToPerm()
551
570
552 obj.repository = repo
571 obj.repository = repo
553 obj.users_group = group_name
572 obj.users_group = group_name
554 obj.permission = permission
573 obj.permission = permission
555 self.sa.add(obj)
574 self.sa.add(obj)
556 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
575 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
557
576
558 def revoke_users_group_permission(self, repo, group_name):
577 def revoke_users_group_permission(self, repo, group_name):
559 """
578 """
560 Revoke permission for user group on given repository
579 Revoke permission for user group on given repository
561
580
562 :param repo: Instance of Repository, repository_id, or repository name
581 :param repo: Instance of Repository, repository_id, or repository name
563 :param group_name: Instance of UserGroup, users_group_id,
582 :param group_name: Instance of UserGroup, users_group_id,
564 or user group name
583 or user group name
565 """
584 """
566 repo = self._get_repo(repo)
585 repo = self._get_repo(repo)
567 group_name = self.__get_users_group(group_name)
586 group_name = self.__get_users_group(group_name)
568
587
569 obj = self.sa.query(UserGroupRepoToPerm)\
588 obj = self.sa.query(UserGroupRepoToPerm)\
570 .filter(UserGroupRepoToPerm.repository == repo)\
589 .filter(UserGroupRepoToPerm.repository == repo)\
571 .filter(UserGroupRepoToPerm.users_group == group_name)\
590 .filter(UserGroupRepoToPerm.users_group == group_name)\
572 .scalar()
591 .scalar()
573 if obj:
592 if obj:
574 self.sa.delete(obj)
593 self.sa.delete(obj)
575 log.debug('Revoked perm to %s on %s' % (repo, group_name))
594 log.debug('Revoked perm to %s on %s' % (repo, group_name))
576
595
577 def delete_stats(self, repo_name):
596 def delete_stats(self, repo_name):
578 """
597 """
579 removes stats for given repo
598 removes stats for given repo
580
599
581 :param repo_name:
600 :param repo_name:
582 """
601 """
583 repo = self._get_repo(repo_name)
602 repo = self._get_repo(repo_name)
584 try:
603 try:
585 obj = self.sa.query(Statistics)\
604 obj = self.sa.query(Statistics)\
586 .filter(Statistics.repository == repo).scalar()
605 .filter(Statistics.repository == repo).scalar()
587 if obj:
606 if obj:
588 self.sa.delete(obj)
607 self.sa.delete(obj)
589 except Exception:
608 except Exception:
590 log.error(traceback.format_exc())
609 log.error(traceback.format_exc())
591 raise
610 raise
592
611
593 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
612 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
594 """
613 """
595 makes repository on filesystem. It's group aware means it'll create
614 makes repository on filesystem. It's group aware means it'll create
596 a repository within a group, and alter the paths accordingly of
615 a repository within a group, and alter the paths accordingly of
597 group location
616 group location
598
617
599 :param repo_name:
618 :param repo_name:
600 :param alias:
619 :param alias:
601 :param parent_id:
620 :param parent_id:
602 :param clone_uri:
621 :param clone_uri:
603 """
622 """
604 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
623 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
605 from rhodecode.model.scm import ScmModel
624 from rhodecode.model.scm import ScmModel
606
625
607 if parent:
626 if parent:
608 new_parent_path = os.sep.join(parent.full_path_splitted)
627 new_parent_path = os.sep.join(parent.full_path_splitted)
609 else:
628 else:
610 new_parent_path = ''
629 new_parent_path = ''
611
630
612 # we need to make it str for mercurial
631 # we need to make it str for mercurial
613 repo_path = os.path.join(*map(lambda x: safe_str(x),
632 repo_path = os.path.join(*map(lambda x: safe_str(x),
614 [self.repos_path, new_parent_path, repo_name]))
633 [self.repos_path, new_parent_path, repo_name]))
615
634
616 # check if this path is not a repository
635 # check if this path is not a repository
617 if is_valid_repo(repo_path, self.repos_path):
636 if is_valid_repo(repo_path, self.repos_path):
618 raise Exception('This path %s is a valid repository' % repo_path)
637 raise Exception('This path %s is a valid repository' % repo_path)
619
638
620 # check if this path is a group
639 # check if this path is a group
621 if is_valid_repos_group(repo_path, self.repos_path):
640 if is_valid_repos_group(repo_path, self.repos_path):
622 raise Exception('This path %s is a valid group' % repo_path)
641 raise Exception('This path %s is a valid group' % repo_path)
623
642
624 log.info('creating repo %s in %s @ %s' % (
643 log.info('creating repo %s in %s @ %s' % (
625 repo_name, safe_unicode(repo_path),
644 repo_name, safe_unicode(repo_path),
626 obfuscate_url_pw(clone_uri)
645 obfuscate_url_pw(clone_uri)
627 )
646 )
628 )
647 )
629 backend = get_backend(alias)
648 backend = get_backend(alias)
630 if alias == 'hg':
649 if alias == 'hg':
631 backend(repo_path, create=True, src_url=clone_uri)
650 backend(repo_path, create=True, src_url=clone_uri)
632 elif alias == 'git':
651 elif alias == 'git':
633 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
652 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
634 # add rhodecode hook into this repo
653 # add rhodecode hook into this repo
635 ScmModel().install_git_hook(repo=r)
654 ScmModel().install_git_hook(repo=r)
636 else:
655 else:
637 raise Exception('Undefined alias %s' % alias)
656 raise Exception('Undefined alias %s' % alias)
638
657
639 def __rename_repo(self, old, new):
658 def __rename_repo(self, old, new):
640 """
659 """
641 renames repository on filesystem
660 renames repository on filesystem
642
661
643 :param old: old name
662 :param old: old name
644 :param new: new name
663 :param new: new name
645 """
664 """
646 log.info('renaming repo from %s to %s' % (old, new))
665 log.info('renaming repo from %s to %s' % (old, new))
647
666
648 old_path = os.path.join(self.repos_path, old)
667 old_path = os.path.join(self.repos_path, old)
649 new_path = os.path.join(self.repos_path, new)
668 new_path = os.path.join(self.repos_path, new)
650 if os.path.isdir(new_path):
669 if os.path.isdir(new_path):
651 raise Exception(
670 raise Exception(
652 'Was trying to rename to already existing dir %s' % new_path
671 'Was trying to rename to already existing dir %s' % new_path
653 )
672 )
654 shutil.move(old_path, new_path)
673 shutil.move(old_path, new_path)
655
674
656 def __delete_repo(self, repo):
675 def __delete_repo(self, repo):
657 """
676 """
658 removes repo from filesystem, the removal is acctually made by
677 removes repo from filesystem, the removal is acctually made by
659 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
678 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
660 repository is no longer valid for rhodecode, can be undeleted later on
679 repository is no longer valid for rhodecode, can be undeleted later on
661 by reverting the renames on this repository
680 by reverting the renames on this repository
662
681
663 :param repo: repo object
682 :param repo: repo object
664 """
683 """
665 rm_path = os.path.join(self.repos_path, repo.repo_name)
684 rm_path = os.path.join(self.repos_path, repo.repo_name)
666 log.info("Removing %s" % (rm_path))
685 log.info("Removing %s" % (rm_path))
667 # disable hg/git internal that it doesn't get detected as repo
686 # disable hg/git internal that it doesn't get detected as repo
668 alias = repo.repo_type
687 alias = repo.repo_type
669
688
670 bare = getattr(repo.scm_instance, 'bare', False)
689 bare = getattr(repo.scm_instance, 'bare', False)
671
690
672 if not bare:
691 if not bare:
673 # skip this for bare git repos
692 # skip this for bare git repos
674 shutil.move(os.path.join(rm_path, '.%s' % alias),
693 shutil.move(os.path.join(rm_path, '.%s' % alias),
675 os.path.join(rm_path, 'rm__.%s' % alias))
694 os.path.join(rm_path, 'rm__.%s' % alias))
676 # disable repo
695 # disable repo
677 _now = datetime.now()
696 _now = datetime.now()
678 _ms = str(_now.microsecond).rjust(6, '0')
697 _ms = str(_now.microsecond).rjust(6, '0')
679 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
698 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
680 repo.just_name)
699 repo.just_name)
681 if repo.group:
700 if repo.group:
682 args = repo.group.full_path_splitted + [_d]
701 args = repo.group.full_path_splitted + [_d]
683 _d = os.path.join(*args)
702 _d = os.path.join(*args)
684 shutil.move(rm_path, os.path.join(self.repos_path, _d))
703 shutil.move(rm_path, os.path.join(self.repos_path, _d))
General Comments 0
You need to be logged in to leave comments. Login now