##// END OF EJS Templates
changed check_... functions from their stupid names to something less retarded :)
marcink -
r1507:7d687ed1 beta
parent child Browse files
Show More
@@ -1,441 +1,441 b''
1 """
1 """
2 Routes configuration
2 Routes configuration
3
3
4 The more specific and detailed routes should be defined first so they
4 The more specific and detailed routes should be defined first so they
5 may take precedent over the more generic routes. For more information
5 may take precedent over the more generic routes. For more information
6 refer to the routes manual at http://routes.groovie.org/docs/
6 refer to the routes manual at http://routes.groovie.org/docs/
7 """
7 """
8 from __future__ import with_statement
8 from __future__ import with_statement
9 from routes import Mapper
9 from routes import Mapper
10
10
11
11
12 # prefix for non repository related links needs to be prefixed with `/`
12 # prefix for non repository related links needs to be prefixed with `/`
13 ADMIN_PREFIX = '/_admin'
13 ADMIN_PREFIX = '/_admin'
14
14
15
15
16 def make_map(config):
16 def make_map(config):
17 """Create, configure and return the routes Mapper"""
17 """Create, configure and return the routes Mapper"""
18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
18 rmap = Mapper(directory=config['pylons.paths']['controllers'],
19 always_scan=config['debug'])
19 always_scan=config['debug'])
20 rmap.minimization = False
20 rmap.minimization = False
21 rmap.explicit = False
21 rmap.explicit = False
22
22
23 from rhodecode.lib.utils import check_repo_fast
23 from rhodecode.lib.utils import is_valid_repo
24 from rhodecode.lib.utils import check_repos_group_fast
24 from rhodecode.lib.utils import is_valid_repos_group
25
25
26 def check_repo(environ, match_dict):
26 def check_repo(environ, match_dict):
27 """
27 """
28 check for valid repository for proper 404 handling
28 check for valid repository for proper 404 handling
29
29
30 :param environ:
30 :param environ:
31 :param match_dict:
31 :param match_dict:
32 """
32 """
33
33
34 repo_name = match_dict.get('repo_name')
34 repo_name = match_dict.get('repo_name')
35 return check_repo_fast(repo_name, config['base_path'])
35 return is_valid_repo(repo_name, config['base_path'])
36
36
37 def check_group(environ, match_dict):
37 def check_group(environ, match_dict):
38 """
38 """
39 check for valid repositories group for proper 404 handling
39 check for valid repositories group for proper 404 handling
40
40
41 :param environ:
41 :param environ:
42 :param match_dict:
42 :param match_dict:
43 """
43 """
44 repos_group_name = match_dict.get('group_name')
44 repos_group_name = match_dict.get('group_name')
45
45
46 return check_repos_group_fast(repos_group_name, config['base_path'])
46 return is_valid_repos_group(repos_group_name, config['base_path'])
47
47
48
48
49 def check_int(environ, match_dict):
49 def check_int(environ, match_dict):
50 return match_dict.get('id').isdigit()
50 return match_dict.get('id').isdigit()
51
51
52 # The ErrorController route (handles 404/500 error pages); it should
52 # The ErrorController route (handles 404/500 error pages); it should
53 # likely stay at the top, ensuring it can always be resolved
53 # likely stay at the top, ensuring it can always be resolved
54 rmap.connect('/error/{action}', controller='error')
54 rmap.connect('/error/{action}', controller='error')
55 rmap.connect('/error/{action}/{id}', controller='error')
55 rmap.connect('/error/{action}/{id}', controller='error')
56
56
57 #==========================================================================
57 #==========================================================================
58 # CUSTOM ROUTES HERE
58 # CUSTOM ROUTES HERE
59 #==========================================================================
59 #==========================================================================
60
60
61 #MAIN PAGE
61 #MAIN PAGE
62 rmap.connect('home', '/', controller='home', action='index')
62 rmap.connect('home', '/', controller='home', action='index')
63 rmap.connect('repo_switcher', '/repos', controller='home',
63 rmap.connect('repo_switcher', '/repos', controller='home',
64 action='repo_switcher')
64 action='repo_switcher')
65 rmap.connect('bugtracker',
65 rmap.connect('bugtracker',
66 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
66 "http://bitbucket.org/marcinkuzminski/rhodecode/issues",
67 _static=True)
67 _static=True)
68 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
68 rmap.connect('rhodecode_official', "http://rhodecode.org", _static=True)
69
69
70 #ADMIN REPOSITORY REST ROUTES
70 #ADMIN REPOSITORY REST ROUTES
71 with rmap.submapper(path_prefix=ADMIN_PREFIX,
71 with rmap.submapper(path_prefix=ADMIN_PREFIX,
72 controller='admin/repos') as m:
72 controller='admin/repos') as m:
73 m.connect("repos", "/repos",
73 m.connect("repos", "/repos",
74 action="create", conditions=dict(method=["POST"]))
74 action="create", conditions=dict(method=["POST"]))
75 m.connect("repos", "/repos",
75 m.connect("repos", "/repos",
76 action="index", conditions=dict(method=["GET"]))
76 action="index", conditions=dict(method=["GET"]))
77 m.connect("formatted_repos", "/repos.{format}",
77 m.connect("formatted_repos", "/repos.{format}",
78 action="index",
78 action="index",
79 conditions=dict(method=["GET"]))
79 conditions=dict(method=["GET"]))
80 m.connect("new_repo", "/repos/new",
80 m.connect("new_repo", "/repos/new",
81 action="new", conditions=dict(method=["GET"]))
81 action="new", conditions=dict(method=["GET"]))
82 m.connect("formatted_new_repo", "/repos/new.{format}",
82 m.connect("formatted_new_repo", "/repos/new.{format}",
83 action="new", conditions=dict(method=["GET"]))
83 action="new", conditions=dict(method=["GET"]))
84 m.connect("/repos/{repo_name:.*}",
84 m.connect("/repos/{repo_name:.*}",
85 action="update", conditions=dict(method=["PUT"],
85 action="update", conditions=dict(method=["PUT"],
86 function=check_repo))
86 function=check_repo))
87 m.connect("/repos/{repo_name:.*}",
87 m.connect("/repos/{repo_name:.*}",
88 action="delete", conditions=dict(method=["DELETE"],
88 action="delete", conditions=dict(method=["DELETE"],
89 function=check_repo))
89 function=check_repo))
90 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
90 m.connect("edit_repo", "/repos/{repo_name:.*}/edit",
91 action="edit", conditions=dict(method=["GET"],
91 action="edit", conditions=dict(method=["GET"],
92 function=check_repo))
92 function=check_repo))
93 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
93 m.connect("formatted_edit_repo", "/repos/{repo_name:.*}.{format}/edit",
94 action="edit", conditions=dict(method=["GET"],
94 action="edit", conditions=dict(method=["GET"],
95 function=check_repo))
95 function=check_repo))
96 m.connect("repo", "/repos/{repo_name:.*}",
96 m.connect("repo", "/repos/{repo_name:.*}",
97 action="show", conditions=dict(method=["GET"],
97 action="show", conditions=dict(method=["GET"],
98 function=check_repo))
98 function=check_repo))
99 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
99 m.connect("formatted_repo", "/repos/{repo_name:.*}.{format}",
100 action="show", conditions=dict(method=["GET"],
100 action="show", conditions=dict(method=["GET"],
101 function=check_repo))
101 function=check_repo))
102 #ajax delete repo perm user
102 #ajax delete repo perm user
103 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
103 m.connect('delete_repo_user', "/repos_delete_user/{repo_name:.*}",
104 action="delete_perm_user", conditions=dict(method=["DELETE"],
104 action="delete_perm_user", conditions=dict(method=["DELETE"],
105 function=check_repo))
105 function=check_repo))
106 #ajax delete repo perm users_group
106 #ajax delete repo perm users_group
107 m.connect('delete_repo_users_group',
107 m.connect('delete_repo_users_group',
108 "/repos_delete_users_group/{repo_name:.*}",
108 "/repos_delete_users_group/{repo_name:.*}",
109 action="delete_perm_users_group",
109 action="delete_perm_users_group",
110 conditions=dict(method=["DELETE"], function=check_repo))
110 conditions=dict(method=["DELETE"], function=check_repo))
111
111
112 #settings actions
112 #settings actions
113 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
113 m.connect('repo_stats', "/repos_stats/{repo_name:.*}",
114 action="repo_stats", conditions=dict(method=["DELETE"],
114 action="repo_stats", conditions=dict(method=["DELETE"],
115 function=check_repo))
115 function=check_repo))
116 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
116 m.connect('repo_cache', "/repos_cache/{repo_name:.*}",
117 action="repo_cache", conditions=dict(method=["DELETE"],
117 action="repo_cache", conditions=dict(method=["DELETE"],
118 function=check_repo))
118 function=check_repo))
119 m.connect('repo_public_journal',
119 m.connect('repo_public_journal',
120 "/repos_public_journal/{repo_name:.*}",
120 "/repos_public_journal/{repo_name:.*}",
121 action="repo_public_journal", conditions=dict(method=["PUT"],
121 action="repo_public_journal", conditions=dict(method=["PUT"],
122 function=check_repo))
122 function=check_repo))
123 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
123 m.connect('repo_pull', "/repo_pull/{repo_name:.*}",
124 action="repo_pull", conditions=dict(method=["PUT"],
124 action="repo_pull", conditions=dict(method=["PUT"],
125 function=check_repo))
125 function=check_repo))
126
126
127 with rmap.submapper(path_prefix=ADMIN_PREFIX,
127 with rmap.submapper(path_prefix=ADMIN_PREFIX,
128 controller='admin/repos_groups') as m:
128 controller='admin/repos_groups') as m:
129 m.connect("repos_groups", "/repos_groups",
129 m.connect("repos_groups", "/repos_groups",
130 action="create", conditions=dict(method=["POST"]))
130 action="create", conditions=dict(method=["POST"]))
131 m.connect("repos_groups", "/repos_groups",
131 m.connect("repos_groups", "/repos_groups",
132 action="index", conditions=dict(method=["GET"]))
132 action="index", conditions=dict(method=["GET"]))
133 m.connect("formatted_repos_groups", "/repos_groups.{format}",
133 m.connect("formatted_repos_groups", "/repos_groups.{format}",
134 action="index", conditions=dict(method=["GET"]))
134 action="index", conditions=dict(method=["GET"]))
135 m.connect("new_repos_group", "/repos_groups/new",
135 m.connect("new_repos_group", "/repos_groups/new",
136 action="new", conditions=dict(method=["GET"]))
136 action="new", conditions=dict(method=["GET"]))
137 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
137 m.connect("formatted_new_repos_group", "/repos_groups/new.{format}",
138 action="new", conditions=dict(method=["GET"]))
138 action="new", conditions=dict(method=["GET"]))
139 m.connect("update_repos_group", "/repos_groups/{id}",
139 m.connect("update_repos_group", "/repos_groups/{id}",
140 action="update", conditions=dict(method=["PUT"],
140 action="update", conditions=dict(method=["PUT"],
141 function=check_int))
141 function=check_int))
142 m.connect("delete_repos_group", "/repos_groups/{id}",
142 m.connect("delete_repos_group", "/repos_groups/{id}",
143 action="delete", conditions=dict(method=["DELETE"],
143 action="delete", conditions=dict(method=["DELETE"],
144 function=check_int))
144 function=check_int))
145 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
145 m.connect("edit_repos_group", "/repos_groups/{id}/edit",
146 action="edit", conditions=dict(method=["GET"],
146 action="edit", conditions=dict(method=["GET"],
147 function=check_int))
147 function=check_int))
148 m.connect("formatted_edit_repos_group",
148 m.connect("formatted_edit_repos_group",
149 "/repos_groups/{id}.{format}/edit",
149 "/repos_groups/{id}.{format}/edit",
150 action="edit", conditions=dict(method=["GET"],
150 action="edit", conditions=dict(method=["GET"],
151 function=check_int))
151 function=check_int))
152 m.connect("repos_group", "/repos_groups/{id}",
152 m.connect("repos_group", "/repos_groups/{id}",
153 action="show", conditions=dict(method=["GET"],
153 action="show", conditions=dict(method=["GET"],
154 function=check_int))
154 function=check_int))
155 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
155 m.connect("formatted_repos_group", "/repos_groups/{id}.{format}",
156 action="show", conditions=dict(method=["GET"],
156 action="show", conditions=dict(method=["GET"],
157 function=check_int))
157 function=check_int))
158
158
159 #ADMIN USER REST ROUTES
159 #ADMIN USER REST ROUTES
160 with rmap.submapper(path_prefix=ADMIN_PREFIX,
160 with rmap.submapper(path_prefix=ADMIN_PREFIX,
161 controller='admin/users') as m:
161 controller='admin/users') as m:
162 m.connect("users", "/users",
162 m.connect("users", "/users",
163 action="create", conditions=dict(method=["POST"]))
163 action="create", conditions=dict(method=["POST"]))
164 m.connect("users", "/users",
164 m.connect("users", "/users",
165 action="index", conditions=dict(method=["GET"]))
165 action="index", conditions=dict(method=["GET"]))
166 m.connect("formatted_users", "/users.{format}",
166 m.connect("formatted_users", "/users.{format}",
167 action="index", conditions=dict(method=["GET"]))
167 action="index", conditions=dict(method=["GET"]))
168 m.connect("new_user", "/users/new",
168 m.connect("new_user", "/users/new",
169 action="new", conditions=dict(method=["GET"]))
169 action="new", conditions=dict(method=["GET"]))
170 m.connect("formatted_new_user", "/users/new.{format}",
170 m.connect("formatted_new_user", "/users/new.{format}",
171 action="new", conditions=dict(method=["GET"]))
171 action="new", conditions=dict(method=["GET"]))
172 m.connect("update_user", "/users/{id}",
172 m.connect("update_user", "/users/{id}",
173 action="update", conditions=dict(method=["PUT"]))
173 action="update", conditions=dict(method=["PUT"]))
174 m.connect("delete_user", "/users/{id}",
174 m.connect("delete_user", "/users/{id}",
175 action="delete", conditions=dict(method=["DELETE"]))
175 action="delete", conditions=dict(method=["DELETE"]))
176 m.connect("edit_user", "/users/{id}/edit",
176 m.connect("edit_user", "/users/{id}/edit",
177 action="edit", conditions=dict(method=["GET"]))
177 action="edit", conditions=dict(method=["GET"]))
178 m.connect("formatted_edit_user",
178 m.connect("formatted_edit_user",
179 "/users/{id}.{format}/edit",
179 "/users/{id}.{format}/edit",
180 action="edit", conditions=dict(method=["GET"]))
180 action="edit", conditions=dict(method=["GET"]))
181 m.connect("user", "/users/{id}",
181 m.connect("user", "/users/{id}",
182 action="show", conditions=dict(method=["GET"]))
182 action="show", conditions=dict(method=["GET"]))
183 m.connect("formatted_user", "/users/{id}.{format}",
183 m.connect("formatted_user", "/users/{id}.{format}",
184 action="show", conditions=dict(method=["GET"]))
184 action="show", conditions=dict(method=["GET"]))
185
185
186 #EXTRAS USER ROUTES
186 #EXTRAS USER ROUTES
187 m.connect("user_perm", "/users_perm/{id}",
187 m.connect("user_perm", "/users_perm/{id}",
188 action="update_perm", conditions=dict(method=["PUT"]))
188 action="update_perm", conditions=dict(method=["PUT"]))
189
189
190 #ADMIN USERS REST ROUTES
190 #ADMIN USERS REST ROUTES
191 with rmap.submapper(path_prefix=ADMIN_PREFIX,
191 with rmap.submapper(path_prefix=ADMIN_PREFIX,
192 controller='admin/users_groups') as m:
192 controller='admin/users_groups') as m:
193 m.connect("users_groups", "/users_groups",
193 m.connect("users_groups", "/users_groups",
194 action="create", conditions=dict(method=["POST"]))
194 action="create", conditions=dict(method=["POST"]))
195 m.connect("users_groups", "/users_groups",
195 m.connect("users_groups", "/users_groups",
196 action="index", conditions=dict(method=["GET"]))
196 action="index", conditions=dict(method=["GET"]))
197 m.connect("formatted_users_groups", "/users_groups.{format}",
197 m.connect("formatted_users_groups", "/users_groups.{format}",
198 action="index", conditions=dict(method=["GET"]))
198 action="index", conditions=dict(method=["GET"]))
199 m.connect("new_users_group", "/users_groups/new",
199 m.connect("new_users_group", "/users_groups/new",
200 action="new", conditions=dict(method=["GET"]))
200 action="new", conditions=dict(method=["GET"]))
201 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
201 m.connect("formatted_new_users_group", "/users_groups/new.{format}",
202 action="new", conditions=dict(method=["GET"]))
202 action="new", conditions=dict(method=["GET"]))
203 m.connect("update_users_group", "/users_groups/{id}",
203 m.connect("update_users_group", "/users_groups/{id}",
204 action="update", conditions=dict(method=["PUT"]))
204 action="update", conditions=dict(method=["PUT"]))
205 m.connect("delete_users_group", "/users_groups/{id}",
205 m.connect("delete_users_group", "/users_groups/{id}",
206 action="delete", conditions=dict(method=["DELETE"]))
206 action="delete", conditions=dict(method=["DELETE"]))
207 m.connect("edit_users_group", "/users_groups/{id}/edit",
207 m.connect("edit_users_group", "/users_groups/{id}/edit",
208 action="edit", conditions=dict(method=["GET"]))
208 action="edit", conditions=dict(method=["GET"]))
209 m.connect("formatted_edit_users_group",
209 m.connect("formatted_edit_users_group",
210 "/users_groups/{id}.{format}/edit",
210 "/users_groups/{id}.{format}/edit",
211 action="edit", conditions=dict(method=["GET"]))
211 action="edit", conditions=dict(method=["GET"]))
212 m.connect("users_group", "/users_groups/{id}",
212 m.connect("users_group", "/users_groups/{id}",
213 action="show", conditions=dict(method=["GET"]))
213 action="show", conditions=dict(method=["GET"]))
214 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
214 m.connect("formatted_users_group", "/users_groups/{id}.{format}",
215 action="show", conditions=dict(method=["GET"]))
215 action="show", conditions=dict(method=["GET"]))
216
216
217 #EXTRAS USER ROUTES
217 #EXTRAS USER ROUTES
218 m.connect("users_group_perm", "/users_groups_perm/{id}",
218 m.connect("users_group_perm", "/users_groups_perm/{id}",
219 action="update_perm", conditions=dict(method=["PUT"]))
219 action="update_perm", conditions=dict(method=["PUT"]))
220
220
221 #ADMIN GROUP REST ROUTES
221 #ADMIN GROUP REST ROUTES
222 rmap.resource('group', 'groups',
222 rmap.resource('group', 'groups',
223 controller='admin/groups', path_prefix=ADMIN_PREFIX)
223 controller='admin/groups', path_prefix=ADMIN_PREFIX)
224
224
225 #ADMIN PERMISSIONS REST ROUTES
225 #ADMIN PERMISSIONS REST ROUTES
226 rmap.resource('permission', 'permissions',
226 rmap.resource('permission', 'permissions',
227 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
227 controller='admin/permissions', path_prefix=ADMIN_PREFIX)
228
228
229 ##ADMIN LDAP SETTINGS
229 ##ADMIN LDAP SETTINGS
230 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
230 rmap.connect('ldap_settings', '%s/ldap' % ADMIN_PREFIX,
231 controller='admin/ldap_settings', action='ldap_settings',
231 controller='admin/ldap_settings', action='ldap_settings',
232 conditions=dict(method=["POST"]))
232 conditions=dict(method=["POST"]))
233
233
234 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
234 rmap.connect('ldap_home', '%s/ldap' % ADMIN_PREFIX,
235 controller='admin/ldap_settings')
235 controller='admin/ldap_settings')
236
236
237 #ADMIN SETTINGS REST ROUTES
237 #ADMIN SETTINGS REST ROUTES
238 with rmap.submapper(path_prefix=ADMIN_PREFIX,
238 with rmap.submapper(path_prefix=ADMIN_PREFIX,
239 controller='admin/settings') as m:
239 controller='admin/settings') as m:
240 m.connect("admin_settings", "/settings",
240 m.connect("admin_settings", "/settings",
241 action="create", conditions=dict(method=["POST"]))
241 action="create", conditions=dict(method=["POST"]))
242 m.connect("admin_settings", "/settings",
242 m.connect("admin_settings", "/settings",
243 action="index", conditions=dict(method=["GET"]))
243 action="index", conditions=dict(method=["GET"]))
244 m.connect("formatted_admin_settings", "/settings.{format}",
244 m.connect("formatted_admin_settings", "/settings.{format}",
245 action="index", conditions=dict(method=["GET"]))
245 action="index", conditions=dict(method=["GET"]))
246 m.connect("admin_new_setting", "/settings/new",
246 m.connect("admin_new_setting", "/settings/new",
247 action="new", conditions=dict(method=["GET"]))
247 action="new", conditions=dict(method=["GET"]))
248 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
248 m.connect("formatted_admin_new_setting", "/settings/new.{format}",
249 action="new", conditions=dict(method=["GET"]))
249 action="new", conditions=dict(method=["GET"]))
250 m.connect("/settings/{setting_id}",
250 m.connect("/settings/{setting_id}",
251 action="update", conditions=dict(method=["PUT"]))
251 action="update", conditions=dict(method=["PUT"]))
252 m.connect("/settings/{setting_id}",
252 m.connect("/settings/{setting_id}",
253 action="delete", conditions=dict(method=["DELETE"]))
253 action="delete", conditions=dict(method=["DELETE"]))
254 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
254 m.connect("admin_edit_setting", "/settings/{setting_id}/edit",
255 action="edit", conditions=dict(method=["GET"]))
255 action="edit", conditions=dict(method=["GET"]))
256 m.connect("formatted_admin_edit_setting",
256 m.connect("formatted_admin_edit_setting",
257 "/settings/{setting_id}.{format}/edit",
257 "/settings/{setting_id}.{format}/edit",
258 action="edit", conditions=dict(method=["GET"]))
258 action="edit", conditions=dict(method=["GET"]))
259 m.connect("admin_setting", "/settings/{setting_id}",
259 m.connect("admin_setting", "/settings/{setting_id}",
260 action="show", conditions=dict(method=["GET"]))
260 action="show", conditions=dict(method=["GET"]))
261 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
261 m.connect("formatted_admin_setting", "/settings/{setting_id}.{format}",
262 action="show", conditions=dict(method=["GET"]))
262 action="show", conditions=dict(method=["GET"]))
263 m.connect("admin_settings_my_account", "/my_account",
263 m.connect("admin_settings_my_account", "/my_account",
264 action="my_account", conditions=dict(method=["GET"]))
264 action="my_account", conditions=dict(method=["GET"]))
265 m.connect("admin_settings_my_account_update", "/my_account_update",
265 m.connect("admin_settings_my_account_update", "/my_account_update",
266 action="my_account_update", conditions=dict(method=["PUT"]))
266 action="my_account_update", conditions=dict(method=["PUT"]))
267 m.connect("admin_settings_create_repository", "/create_repository",
267 m.connect("admin_settings_create_repository", "/create_repository",
268 action="create_repository", conditions=dict(method=["GET"]))
268 action="create_repository", conditions=dict(method=["GET"]))
269
269
270
270
271 #ADMIN MAIN PAGES
271 #ADMIN MAIN PAGES
272 with rmap.submapper(path_prefix=ADMIN_PREFIX,
272 with rmap.submapper(path_prefix=ADMIN_PREFIX,
273 controller='admin/admin') as m:
273 controller='admin/admin') as m:
274 m.connect('admin_home', '', action='index')
274 m.connect('admin_home', '', action='index')
275 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
275 m.connect('admin_add_repo', '/add_repo/{new_repo:[a-z0-9\. _-]*}',
276 action='add_repo')
276 action='add_repo')
277
277
278 #==========================================================================
278 #==========================================================================
279 # API V1
279 # API V1
280 #==========================================================================
280 #==========================================================================
281 with rmap.submapper(path_prefix=ADMIN_PREFIX,
281 with rmap.submapper(path_prefix=ADMIN_PREFIX,
282 controller='api/api') as m:
282 controller='api/api') as m:
283 m.connect('api', '/api')
283 m.connect('api', '/api')
284
284
285
285
286 #USER JOURNAL
286 #USER JOURNAL
287 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
287 rmap.connect('journal', '%s/journal' % ADMIN_PREFIX, controller='journal')
288
288
289 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
289 rmap.connect('public_journal', '%s/public_journal' % ADMIN_PREFIX,
290 controller='journal', action="public_journal")
290 controller='journal', action="public_journal")
291
291
292 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
292 rmap.connect('public_journal_rss', '%s/public_journal_rss' % ADMIN_PREFIX,
293 controller='journal', action="public_journal_rss")
293 controller='journal', action="public_journal_rss")
294
294
295 rmap.connect('public_journal_atom',
295 rmap.connect('public_journal_atom',
296 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
296 '%s/public_journal_atom' % ADMIN_PREFIX, controller='journal',
297 action="public_journal_atom")
297 action="public_journal_atom")
298
298
299 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
299 rmap.connect('toggle_following', '%s/toggle_following' % ADMIN_PREFIX,
300 controller='journal', action='toggle_following',
300 controller='journal', action='toggle_following',
301 conditions=dict(method=["POST"]))
301 conditions=dict(method=["POST"]))
302
302
303 #SEARCH
303 #SEARCH
304 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
304 rmap.connect('search', '%s/search' % ADMIN_PREFIX, controller='search',)
305 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
305 rmap.connect('search_repo', '%s/search/{search_repo:.*}' % ADMIN_PREFIX,
306 controller='search')
306 controller='search')
307
307
308 #LOGIN/LOGOUT/REGISTER/SIGN IN
308 #LOGIN/LOGOUT/REGISTER/SIGN IN
309 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
309 rmap.connect('login_home', '%s/login' % ADMIN_PREFIX, controller='login')
310 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
310 rmap.connect('logout_home', '%s/logout' % ADMIN_PREFIX, controller='login',
311 action='logout')
311 action='logout')
312
312
313 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
313 rmap.connect('register', '%s/register' % ADMIN_PREFIX, controller='login',
314 action='register')
314 action='register')
315
315
316 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
316 rmap.connect('reset_password', '%s/password_reset' % ADMIN_PREFIX,
317 controller='login', action='password_reset')
317 controller='login', action='password_reset')
318
318
319 rmap.connect('reset_password_confirmation',
319 rmap.connect('reset_password_confirmation',
320 '%s/password_reset_confirmation' % ADMIN_PREFIX,
320 '%s/password_reset_confirmation' % ADMIN_PREFIX,
321 controller='login', action='password_reset_confirmation')
321 controller='login', action='password_reset_confirmation')
322
322
323 #FEEDS
323 #FEEDS
324 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
324 rmap.connect('rss_feed_home', '/{repo_name:.*}/feed/rss',
325 controller='feed', action='rss',
325 controller='feed', action='rss',
326 conditions=dict(function=check_repo))
326 conditions=dict(function=check_repo))
327
327
328 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
328 rmap.connect('atom_feed_home', '/{repo_name:.*}/feed/atom',
329 controller='feed', action='atom',
329 controller='feed', action='atom',
330 conditions=dict(function=check_repo))
330 conditions=dict(function=check_repo))
331
331
332 #==========================================================================
332 #==========================================================================
333 # REPOSITORY ROUTES
333 # REPOSITORY ROUTES
334 #==========================================================================
334 #==========================================================================
335 rmap.connect('summary_home', '/{repo_name:.*}',
335 rmap.connect('summary_home', '/{repo_name:.*}',
336 controller='summary',
336 controller='summary',
337 conditions=dict(function=check_repo))
337 conditions=dict(function=check_repo))
338
338
339 # rmap.connect('repo_group_home', '/{group_name:.*}',
339 # rmap.connect('repo_group_home', '/{group_name:.*}',
340 # controller='admin/repos_groups',action="show_by_name",
340 # controller='admin/repos_groups',action="show_by_name",
341 # conditions=dict(function=check_group))
341 # conditions=dict(function=check_group))
342
342
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
343 rmap.connect('changeset_home', '/{repo_name:.*}/changeset/{revision}',
344 controller='changeset', revision='tip',
344 controller='changeset', revision='tip',
345 conditions=dict(function=check_repo))
345 conditions=dict(function=check_repo))
346
346
347 rmap.connect('raw_changeset_home',
347 rmap.connect('raw_changeset_home',
348 '/{repo_name:.*}/raw-changeset/{revision}',
348 '/{repo_name:.*}/raw-changeset/{revision}',
349 controller='changeset', action='raw_changeset',
349 controller='changeset', action='raw_changeset',
350 revision='tip', conditions=dict(function=check_repo))
350 revision='tip', conditions=dict(function=check_repo))
351
351
352 rmap.connect('summary_home', '/{repo_name:.*}/summary',
352 rmap.connect('summary_home', '/{repo_name:.*}/summary',
353 controller='summary', conditions=dict(function=check_repo))
353 controller='summary', conditions=dict(function=check_repo))
354
354
355 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
355 rmap.connect('shortlog_home', '/{repo_name:.*}/shortlog',
356 controller='shortlog', conditions=dict(function=check_repo))
356 controller='shortlog', conditions=dict(function=check_repo))
357
357
358 rmap.connect('branches_home', '/{repo_name:.*}/branches',
358 rmap.connect('branches_home', '/{repo_name:.*}/branches',
359 controller='branches', conditions=dict(function=check_repo))
359 controller='branches', conditions=dict(function=check_repo))
360
360
361 rmap.connect('tags_home', '/{repo_name:.*}/tags',
361 rmap.connect('tags_home', '/{repo_name:.*}/tags',
362 controller='tags', conditions=dict(function=check_repo))
362 controller='tags', conditions=dict(function=check_repo))
363
363
364 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
364 rmap.connect('changelog_home', '/{repo_name:.*}/changelog',
365 controller='changelog', conditions=dict(function=check_repo))
365 controller='changelog', conditions=dict(function=check_repo))
366
366
367 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
367 rmap.connect('changelog_details', '/{repo_name:.*}/changelog_details/{cs}',
368 controller='changelog', action='changelog_details',
368 controller='changelog', action='changelog_details',
369 conditions=dict(function=check_repo))
369 conditions=dict(function=check_repo))
370
370
371 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
371 rmap.connect('files_home', '/{repo_name:.*}/files/{revision}/{f_path:.*}',
372 controller='files', revision='tip', f_path='',
372 controller='files', revision='tip', f_path='',
373 conditions=dict(function=check_repo))
373 conditions=dict(function=check_repo))
374
374
375 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
375 rmap.connect('files_diff_home', '/{repo_name:.*}/diff/{f_path:.*}',
376 controller='files', action='diff', revision='tip', f_path='',
376 controller='files', action='diff', revision='tip', f_path='',
377 conditions=dict(function=check_repo))
377 conditions=dict(function=check_repo))
378
378
379 rmap.connect('files_rawfile_home',
379 rmap.connect('files_rawfile_home',
380 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
380 '/{repo_name:.*}/rawfile/{revision}/{f_path:.*}',
381 controller='files', action='rawfile', revision='tip',
381 controller='files', action='rawfile', revision='tip',
382 f_path='', conditions=dict(function=check_repo))
382 f_path='', conditions=dict(function=check_repo))
383
383
384 rmap.connect('files_raw_home',
384 rmap.connect('files_raw_home',
385 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
385 '/{repo_name:.*}/raw/{revision}/{f_path:.*}',
386 controller='files', action='raw', revision='tip', f_path='',
386 controller='files', action='raw', revision='tip', f_path='',
387 conditions=dict(function=check_repo))
387 conditions=dict(function=check_repo))
388
388
389 rmap.connect('files_annotate_home',
389 rmap.connect('files_annotate_home',
390 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
390 '/{repo_name:.*}/annotate/{revision}/{f_path:.*}',
391 controller='files', action='annotate', revision='tip',
391 controller='files', action='annotate', revision='tip',
392 f_path='', conditions=dict(function=check_repo))
392 f_path='', conditions=dict(function=check_repo))
393
393
394 rmap.connect('files_edit_home',
394 rmap.connect('files_edit_home',
395 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
395 '/{repo_name:.*}/edit/{revision}/{f_path:.*}',
396 controller='files', action='edit', revision='tip',
396 controller='files', action='edit', revision='tip',
397 f_path='', conditions=dict(function=check_repo))
397 f_path='', conditions=dict(function=check_repo))
398
398
399 rmap.connect('files_add_home',
399 rmap.connect('files_add_home',
400 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
400 '/{repo_name:.*}/add/{revision}/{f_path:.*}',
401 controller='files', action='add', revision='tip',
401 controller='files', action='add', revision='tip',
402 f_path='', conditions=dict(function=check_repo))
402 f_path='', conditions=dict(function=check_repo))
403
403
404 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
404 rmap.connect('files_archive_home', '/{repo_name:.*}/archive/{fname}',
405 controller='files', action='archivefile',
405 controller='files', action='archivefile',
406 conditions=dict(function=check_repo))
406 conditions=dict(function=check_repo))
407
407
408 rmap.connect('files_nodelist_home',
408 rmap.connect('files_nodelist_home',
409 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
409 '/{repo_name:.*}/nodelist/{revision}/{f_path:.*}',
410 controller='files', action='nodelist',
410 controller='files', action='nodelist',
411 conditions=dict(function=check_repo))
411 conditions=dict(function=check_repo))
412
412
413 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
413 rmap.connect('repo_settings_delete', '/{repo_name:.*}/settings',
414 controller='settings', action="delete",
414 controller='settings', action="delete",
415 conditions=dict(method=["DELETE"], function=check_repo))
415 conditions=dict(method=["DELETE"], function=check_repo))
416
416
417 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
417 rmap.connect('repo_settings_update', '/{repo_name:.*}/settings',
418 controller='settings', action="update",
418 controller='settings', action="update",
419 conditions=dict(method=["PUT"], function=check_repo))
419 conditions=dict(method=["PUT"], function=check_repo))
420
420
421 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
421 rmap.connect('repo_settings_home', '/{repo_name:.*}/settings',
422 controller='settings', action='index',
422 controller='settings', action='index',
423 conditions=dict(function=check_repo))
423 conditions=dict(function=check_repo))
424
424
425 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
425 rmap.connect('repo_fork_create_home', '/{repo_name:.*}/fork',
426 controller='settings', action='fork_create',
426 controller='settings', action='fork_create',
427 conditions=dict(function=check_repo, method=["POST"]))
427 conditions=dict(function=check_repo, method=["POST"]))
428
428
429 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
429 rmap.connect('repo_fork_home', '/{repo_name:.*}/fork',
430 controller='settings', action='fork',
430 controller='settings', action='fork',
431 conditions=dict(function=check_repo))
431 conditions=dict(function=check_repo))
432
432
433 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
433 rmap.connect('repo_followers_home', '/{repo_name:.*}/followers',
434 controller='followers', action='followers',
434 controller='followers', action='followers',
435 conditions=dict(function=check_repo))
435 conditions=dict(function=check_repo))
436
436
437 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
437 rmap.connect('repo_forks_home', '/{repo_name:.*}/forks',
438 controller='forks', action='forks',
438 controller='forks', action='forks',
439 conditions=dict(function=check_repo))
439 conditions=dict(function=check_repo))
440
440
441 return rmap
441 return rmap
@@ -1,289 +1,289 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplegit
3 rhodecode.lib.middleware.simplegit
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
6 SimpleGit middleware for handling git protocol request (push/clone etc.)
7 It's implemented with basic auth function
7 It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from dulwich import server as dulserver
31 from dulwich import server as dulserver
32
32
33
33
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
34 class SimpleGitUploadPackHandler(dulserver.UploadPackHandler):
35
35
36 def handle(self):
36 def handle(self):
37 write = lambda x: self.proto.write_sideband(1, x)
37 write = lambda x: self.proto.write_sideband(1, x)
38
38
39 graph_walker = dulserver.ProtocolGraphWalker(self,
39 graph_walker = dulserver.ProtocolGraphWalker(self,
40 self.repo.object_store,
40 self.repo.object_store,
41 self.repo.get_peeled)
41 self.repo.get_peeled)
42 objects_iter = self.repo.fetch_objects(
42 objects_iter = self.repo.fetch_objects(
43 graph_walker.determine_wants, graph_walker, self.progress,
43 graph_walker.determine_wants, graph_walker, self.progress,
44 get_tagged=self.get_tagged)
44 get_tagged=self.get_tagged)
45
45
46 # Do they want any objects?
46 # Do they want any objects?
47 if objects_iter is None or len(objects_iter) == 0:
47 if objects_iter is None or len(objects_iter) == 0:
48 return
48 return
49
49
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
50 self.progress("counting objects: %d, done.\n" % len(objects_iter))
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
51 dulserver.write_pack_objects(dulserver.ProtocolFile(None, write),
52 objects_iter, len(objects_iter))
52 objects_iter, len(objects_iter))
53 messages = []
53 messages = []
54 messages.append('thank you for using rhodecode')
54 messages.append('thank you for using rhodecode')
55
55
56 for msg in messages:
56 for msg in messages:
57 self.progress(msg + "\n")
57 self.progress(msg + "\n")
58 # we are done
58 # we are done
59 self.proto.write("0000")
59 self.proto.write("0000")
60
60
61 dulserver.DEFAULT_HANDLERS = {
61 dulserver.DEFAULT_HANDLERS = {
62 'git-upload-pack': SimpleGitUploadPackHandler,
62 'git-upload-pack': SimpleGitUploadPackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
63 'git-receive-pack': dulserver.ReceivePackHandler,
64 }
64 }
65
65
66 from dulwich.repo import Repo
66 from dulwich.repo import Repo
67 from dulwich.web import HTTPGitApplication
67 from dulwich.web import HTTPGitApplication
68
68
69 from paste.auth.basic import AuthBasicAuthenticator
69 from paste.auth.basic import AuthBasicAuthenticator
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
70 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
71
71
72 from rhodecode.lib import safe_str
72 from rhodecode.lib import safe_str
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
73 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
74 from rhodecode.lib.utils import invalidate_cache, check_repo_fast
74 from rhodecode.lib.utils import invalidate_cache, is_valid_repo
75 from rhodecode.model.db import User
75 from rhodecode.model.db import User
76
76
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
77 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
78
78
79 log = logging.getLogger(__name__)
79 log = logging.getLogger(__name__)
80
80
81
81
82 def is_git(environ):
82 def is_git(environ):
83 """Returns True if request's target is git server.
83 """Returns True if request's target is git server.
84 ``HTTP_USER_AGENT`` would then have git client version given.
84 ``HTTP_USER_AGENT`` would then have git client version given.
85
85
86 :param environ:
86 :param environ:
87 """
87 """
88 http_user_agent = environ.get('HTTP_USER_AGENT')
88 http_user_agent = environ.get('HTTP_USER_AGENT')
89 if http_user_agent and http_user_agent.startswith('git'):
89 if http_user_agent and http_user_agent.startswith('git'):
90 return True
90 return True
91 return False
91 return False
92
92
93
93
94 class SimpleGit(object):
94 class SimpleGit(object):
95
95
96 def __init__(self, application, config):
96 def __init__(self, application, config):
97 self.application = application
97 self.application = application
98 self.config = config
98 self.config = config
99 # base path of repo locations
99 # base path of repo locations
100 self.basepath = self.config['base_path']
100 self.basepath = self.config['base_path']
101 #authenticate this mercurial request using authfunc
101 #authenticate this mercurial request using authfunc
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
102 self.authenticate = AuthBasicAuthenticator('', authfunc)
103
103
104 def __call__(self, environ, start_response):
104 def __call__(self, environ, start_response):
105 if not is_git(environ):
105 if not is_git(environ):
106 return self.application(environ, start_response)
106 return self.application(environ, start_response)
107
107
108 proxy_key = 'HTTP_X_REAL_IP'
108 proxy_key = 'HTTP_X_REAL_IP'
109 def_key = 'REMOTE_ADDR'
109 def_key = 'REMOTE_ADDR'
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
110 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
111 username = None
111 username = None
112 # skip passing error to error controller
112 # skip passing error to error controller
113 environ['pylons.status_code_redirect'] = True
113 environ['pylons.status_code_redirect'] = True
114
114
115 #======================================================================
115 #======================================================================
116 # EXTRACT REPOSITORY NAME FROM ENV
116 # EXTRACT REPOSITORY NAME FROM ENV
117 #======================================================================
117 #======================================================================
118 try:
118 try:
119 repo_name = self.__get_repository(environ)
119 repo_name = self.__get_repository(environ)
120 log.debug('Extracted repo name is %s' % repo_name)
120 log.debug('Extracted repo name is %s' % repo_name)
121 except:
121 except:
122 return HTTPInternalServerError()(environ, start_response)
122 return HTTPInternalServerError()(environ, start_response)
123
123
124 #======================================================================
124 #======================================================================
125 # GET ACTION PULL or PUSH
125 # GET ACTION PULL or PUSH
126 #======================================================================
126 #======================================================================
127 action = self.__get_action(environ)
127 action = self.__get_action(environ)
128
128
129 #======================================================================
129 #======================================================================
130 # CHECK ANONYMOUS PERMISSION
130 # CHECK ANONYMOUS PERMISSION
131 #======================================================================
131 #======================================================================
132 if action in ['pull', 'push']:
132 if action in ['pull', 'push']:
133 anonymous_user = self.__get_user('default')
133 anonymous_user = self.__get_user('default')
134 username = anonymous_user.username
134 username = anonymous_user.username
135 anonymous_perm = self.__check_permission(action,
135 anonymous_perm = self.__check_permission(action,
136 anonymous_user,
136 anonymous_user,
137 repo_name)
137 repo_name)
138
138
139 if anonymous_perm is not True or anonymous_user.active is False:
139 if anonymous_perm is not True or anonymous_user.active is False:
140 if anonymous_perm is not True:
140 if anonymous_perm is not True:
141 log.debug('Not enough credentials to access this '
141 log.debug('Not enough credentials to access this '
142 'repository as anonymous user')
142 'repository as anonymous user')
143 if anonymous_user.active is False:
143 if anonymous_user.active is False:
144 log.debug('Anonymous access is disabled, running '
144 log.debug('Anonymous access is disabled, running '
145 'authentication')
145 'authentication')
146 #==============================================================
146 #==============================================================
147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
147 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
148 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
149 #==============================================================
149 #==============================================================
150
150
151 if not REMOTE_USER(environ):
151 if not REMOTE_USER(environ):
152 self.authenticate.realm = \
152 self.authenticate.realm = \
153 safe_str(self.config['rhodecode_realm'])
153 safe_str(self.config['rhodecode_realm'])
154 result = self.authenticate(environ)
154 result = self.authenticate(environ)
155 if isinstance(result, str):
155 if isinstance(result, str):
156 AUTH_TYPE.update(environ, 'basic')
156 AUTH_TYPE.update(environ, 'basic')
157 REMOTE_USER.update(environ, result)
157 REMOTE_USER.update(environ, result)
158 else:
158 else:
159 return result.wsgi_application(environ, start_response)
159 return result.wsgi_application(environ, start_response)
160
160
161 #==============================================================
161 #==============================================================
162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
162 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
163 # BASIC AUTH
163 # BASIC AUTH
164 #==============================================================
164 #==============================================================
165
165
166 if action in ['pull', 'push']:
166 if action in ['pull', 'push']:
167 username = REMOTE_USER(environ)
167 username = REMOTE_USER(environ)
168 try:
168 try:
169 user = self.__get_user(username)
169 user = self.__get_user(username)
170 username = user.username
170 username = user.username
171 except:
171 except:
172 log.error(traceback.format_exc())
172 log.error(traceback.format_exc())
173 return HTTPInternalServerError()(environ,
173 return HTTPInternalServerError()(environ,
174 start_response)
174 start_response)
175
175
176 #check permissions for this repository
176 #check permissions for this repository
177 perm = self.__check_permission(action, user,
177 perm = self.__check_permission(action, user,
178 repo_name)
178 repo_name)
179 if perm is not True:
179 if perm is not True:
180 return HTTPForbidden()(environ, start_response)
180 return HTTPForbidden()(environ, start_response)
181
181
182 extras = {'ip': ipaddr,
182 extras = {'ip': ipaddr,
183 'username': username,
183 'username': username,
184 'action': action,
184 'action': action,
185 'repository': repo_name}
185 'repository': repo_name}
186
186
187 #===================================================================
187 #===================================================================
188 # GIT REQUEST HANDLING
188 # GIT REQUEST HANDLING
189 #===================================================================
189 #===================================================================
190
190
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
191 repo_path = safe_str(os.path.join(self.basepath, repo_name))
192 log.debug('Repository path is %s' % repo_path)
192 log.debug('Repository path is %s' % repo_path)
193
193
194 # quick check if that dir exists...
194 # quick check if that dir exists...
195 if check_repo_fast(repo_name, self.basepath) is False:
195 if is_valid_repo(repo_name, self.basepath) is False:
196 return HTTPNotFound()(environ, start_response)
196 return HTTPNotFound()(environ, start_response)
197
197
198 try:
198 try:
199 #invalidate cache on push
199 #invalidate cache on push
200 if action == 'push':
200 if action == 'push':
201 self.__invalidate_cache(repo_name)
201 self.__invalidate_cache(repo_name)
202
202
203 app = self.__make_app(repo_name, repo_path)
203 app = self.__make_app(repo_name, repo_path)
204 return app(environ, start_response)
204 return app(environ, start_response)
205 except Exception:
205 except Exception:
206 log.error(traceback.format_exc())
206 log.error(traceback.format_exc())
207 return HTTPInternalServerError()(environ, start_response)
207 return HTTPInternalServerError()(environ, start_response)
208
208
209 def __make_app(self, repo_name, repo_path):
209 def __make_app(self, repo_name, repo_path):
210 """
210 """
211 Make an wsgi application using dulserver
211 Make an wsgi application using dulserver
212
212
213 :param repo_name: name of the repository
213 :param repo_name: name of the repository
214 :param repo_path: full path to the repository
214 :param repo_path: full path to the repository
215 """
215 """
216
216
217 _d = {'/' + repo_name: Repo(repo_path)}
217 _d = {'/' + repo_name: Repo(repo_path)}
218 backend = dulserver.DictBackend(_d)
218 backend = dulserver.DictBackend(_d)
219 gitserve = HTTPGitApplication(backend)
219 gitserve = HTTPGitApplication(backend)
220
220
221 return gitserve
221 return gitserve
222
222
223 def __check_permission(self, action, user, repo_name):
223 def __check_permission(self, action, user, repo_name):
224 """
224 """
225 Checks permissions using action (push/pull) user and repository
225 Checks permissions using action (push/pull) user and repository
226 name
226 name
227
227
228 :param action: push or pull action
228 :param action: push or pull action
229 :param user: user instance
229 :param user: user instance
230 :param repo_name: repository name
230 :param repo_name: repository name
231 """
231 """
232 if action == 'push':
232 if action == 'push':
233 if not HasPermissionAnyMiddleware('repository.write',
233 if not HasPermissionAnyMiddleware('repository.write',
234 'repository.admin')(user,
234 'repository.admin')(user,
235 repo_name):
235 repo_name):
236 return False
236 return False
237
237
238 else:
238 else:
239 #any other action need at least read permission
239 #any other action need at least read permission
240 if not HasPermissionAnyMiddleware('repository.read',
240 if not HasPermissionAnyMiddleware('repository.read',
241 'repository.write',
241 'repository.write',
242 'repository.admin')(user,
242 'repository.admin')(user,
243 repo_name):
243 repo_name):
244 return False
244 return False
245
245
246 return True
246 return True
247
247
248 def __get_repository(self, environ):
248 def __get_repository(self, environ):
249 """
249 """
250 Get's repository name out of PATH_INFO header
250 Get's repository name out of PATH_INFO header
251
251
252 :param environ: environ where PATH_INFO is stored
252 :param environ: environ where PATH_INFO is stored
253 """
253 """
254 try:
254 try:
255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
255 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
256 if repo_name.endswith('/'):
256 if repo_name.endswith('/'):
257 repo_name = repo_name.rstrip('/')
257 repo_name = repo_name.rstrip('/')
258 except:
258 except:
259 log.error(traceback.format_exc())
259 log.error(traceback.format_exc())
260 raise
260 raise
261 repo_name = repo_name.split('/')[0]
261 repo_name = repo_name.split('/')[0]
262 return repo_name
262 return repo_name
263
263
264 def __get_user(self, username):
264 def __get_user(self, username):
265 return User.by_username(username)
265 return User.by_username(username)
266
266
267 def __get_action(self, environ):
267 def __get_action(self, environ):
268 """Maps git request commands into a pull or push command.
268 """Maps git request commands into a pull or push command.
269
269
270 :param environ:
270 :param environ:
271 """
271 """
272 service = environ['QUERY_STRING'].split('=')
272 service = environ['QUERY_STRING'].split('=')
273 if len(service) > 1:
273 if len(service) > 1:
274 service_cmd = service[1]
274 service_cmd = service[1]
275 mapping = {'git-receive-pack': 'push',
275 mapping = {'git-receive-pack': 'push',
276 'git-upload-pack': 'pull',
276 'git-upload-pack': 'pull',
277 }
277 }
278
278
279 return mapping.get(service_cmd,
279 return mapping.get(service_cmd,
280 service_cmd if service_cmd else 'other')
280 service_cmd if service_cmd else 'other')
281 else:
281 else:
282 return 'other'
282 return 'other'
283
283
284 def __invalidate_cache(self, repo_name):
284 def __invalidate_cache(self, repo_name):
285 """we know that some change was made to repositories and we should
285 """we know that some change was made to repositories and we should
286 invalidate the cache to see the changes right away but only for
286 invalidate the cache to see the changes right away but only for
287 push requests"""
287 push requests"""
288 invalidate_cache('get_repo_cached_%s' % repo_name)
288 invalidate_cache('get_repo_cached_%s' % repo_name)
289
289
@@ -1,287 +1,287 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.middleware.simplehg
3 rhodecode.lib.middleware.simplehg
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 SimpleHG middleware for handling mercurial protocol request
6 SimpleHG middleware for handling mercurial protocol request
7 (push/clone etc.). It's implemented with basic auth function
7 (push/clone etc.). It's implemented with basic auth function
8
8
9 :created_on: Apr 28, 2010
9 :created_on: Apr 28, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26
26
27 import os
27 import os
28 import logging
28 import logging
29 import traceback
29 import traceback
30
30
31 from mercurial.error import RepoError
31 from mercurial.error import RepoError
32 from mercurial.hgweb import hgweb_mod
32 from mercurial.hgweb import hgweb_mod
33
33
34 from paste.auth.basic import AuthBasicAuthenticator
34 from paste.auth.basic import AuthBasicAuthenticator
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
35 from paste.httpheaders import REMOTE_USER, AUTH_TYPE
36
36
37 from rhodecode.lib import safe_str
37 from rhodecode.lib import safe_str
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
38 from rhodecode.lib.auth import authfunc, HasPermissionAnyMiddleware
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
39 from rhodecode.lib.utils import make_ui, invalidate_cache, \
40 check_repo_fast, ui_sections
40 is_valid_repo, ui_sections
41 from rhodecode.model.db import User
41 from rhodecode.model.db import User
42
42
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
43 from webob.exc import HTTPNotFound, HTTPForbidden, HTTPInternalServerError
44
44
45 log = logging.getLogger(__name__)
45 log = logging.getLogger(__name__)
46
46
47
47
48 def is_mercurial(environ):
48 def is_mercurial(environ):
49 """Returns True if request's target is mercurial server - header
49 """Returns True if request's target is mercurial server - header
50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
50 ``HTTP_ACCEPT`` of such request would start with ``application/mercurial``.
51 """
51 """
52 http_accept = environ.get('HTTP_ACCEPT')
52 http_accept = environ.get('HTTP_ACCEPT')
53 if http_accept and http_accept.startswith('application/mercurial'):
53 if http_accept and http_accept.startswith('application/mercurial'):
54 return True
54 return True
55 return False
55 return False
56
56
57
57
58 class SimpleHg(object):
58 class SimpleHg(object):
59
59
60 def __init__(self, application, config):
60 def __init__(self, application, config):
61 self.application = application
61 self.application = application
62 self.config = config
62 self.config = config
63 # base path of repo locations
63 # base path of repo locations
64 self.basepath = self.config['base_path']
64 self.basepath = self.config['base_path']
65 #authenticate this mercurial request using authfunc
65 #authenticate this mercurial request using authfunc
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
66 self.authenticate = AuthBasicAuthenticator('', authfunc)
67 self.ipaddr = '0.0.0.0'
67 self.ipaddr = '0.0.0.0'
68
68
69 def __call__(self, environ, start_response):
69 def __call__(self, environ, start_response):
70 if not is_mercurial(environ):
70 if not is_mercurial(environ):
71 return self.application(environ, start_response)
71 return self.application(environ, start_response)
72
72
73 proxy_key = 'HTTP_X_REAL_IP'
73 proxy_key = 'HTTP_X_REAL_IP'
74 def_key = 'REMOTE_ADDR'
74 def_key = 'REMOTE_ADDR'
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
75 ipaddr = environ.get(proxy_key, environ.get(def_key, '0.0.0.0'))
76
76
77 # skip passing error to error controller
77 # skip passing error to error controller
78 environ['pylons.status_code_redirect'] = True
78 environ['pylons.status_code_redirect'] = True
79
79
80 #======================================================================
80 #======================================================================
81 # EXTRACT REPOSITORY NAME FROM ENV
81 # EXTRACT REPOSITORY NAME FROM ENV
82 #======================================================================
82 #======================================================================
83 try:
83 try:
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
84 repo_name = environ['REPO_NAME'] = self.__get_repository(environ)
85 log.debug('Extracted repo name is %s' % repo_name)
85 log.debug('Extracted repo name is %s' % repo_name)
86 except:
86 except:
87 return HTTPInternalServerError()(environ, start_response)
87 return HTTPInternalServerError()(environ, start_response)
88
88
89 #======================================================================
89 #======================================================================
90 # GET ACTION PULL or PUSH
90 # GET ACTION PULL or PUSH
91 #======================================================================
91 #======================================================================
92 action = self.__get_action(environ)
92 action = self.__get_action(environ)
93
93
94 #======================================================================
94 #======================================================================
95 # CHECK ANONYMOUS PERMISSION
95 # CHECK ANONYMOUS PERMISSION
96 #======================================================================
96 #======================================================================
97 if action in ['pull', 'push']:
97 if action in ['pull', 'push']:
98 anonymous_user = self.__get_user('default')
98 anonymous_user = self.__get_user('default')
99 username = anonymous_user.username
99 username = anonymous_user.username
100 anonymous_perm = self.__check_permission(action,
100 anonymous_perm = self.__check_permission(action,
101 anonymous_user,
101 anonymous_user,
102 repo_name)
102 repo_name)
103
103
104 if anonymous_perm is not True or anonymous_user.active is False:
104 if anonymous_perm is not True or anonymous_user.active is False:
105 if anonymous_perm is not True:
105 if anonymous_perm is not True:
106 log.debug('Not enough credentials to access this '
106 log.debug('Not enough credentials to access this '
107 'repository as anonymous user')
107 'repository as anonymous user')
108 if anonymous_user.active is False:
108 if anonymous_user.active is False:
109 log.debug('Anonymous access is disabled, running '
109 log.debug('Anonymous access is disabled, running '
110 'authentication')
110 'authentication')
111 #==============================================================
111 #==============================================================
112 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
112 # DEFAULT PERM FAILED OR ANONYMOUS ACCESS IS DISABLED SO WE
113 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
113 # NEED TO AUTHENTICATE AND ASK FOR AUTH USER PERMISSIONS
114 #==============================================================
114 #==============================================================
115
115
116 if not REMOTE_USER(environ):
116 if not REMOTE_USER(environ):
117 self.authenticate.realm = \
117 self.authenticate.realm = \
118 safe_str(self.config['rhodecode_realm'])
118 safe_str(self.config['rhodecode_realm'])
119 result = self.authenticate(environ)
119 result = self.authenticate(environ)
120 if isinstance(result, str):
120 if isinstance(result, str):
121 AUTH_TYPE.update(environ, 'basic')
121 AUTH_TYPE.update(environ, 'basic')
122 REMOTE_USER.update(environ, result)
122 REMOTE_USER.update(environ, result)
123 else:
123 else:
124 return result.wsgi_application(environ, start_response)
124 return result.wsgi_application(environ, start_response)
125
125
126 #==============================================================
126 #==============================================================
127 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
127 # CHECK PERMISSIONS FOR THIS REQUEST USING GIVEN USERNAME FROM
128 # BASIC AUTH
128 # BASIC AUTH
129 #==============================================================
129 #==============================================================
130
130
131 if action in ['pull', 'push']:
131 if action in ['pull', 'push']:
132 username = REMOTE_USER(environ)
132 username = REMOTE_USER(environ)
133 try:
133 try:
134 user = self.__get_user(username)
134 user = self.__get_user(username)
135 username = user.username
135 username = user.username
136 except:
136 except:
137 log.error(traceback.format_exc())
137 log.error(traceback.format_exc())
138 return HTTPInternalServerError()(environ,
138 return HTTPInternalServerError()(environ,
139 start_response)
139 start_response)
140
140
141 #check permissions for this repository
141 #check permissions for this repository
142 perm = self.__check_permission(action, user,
142 perm = self.__check_permission(action, user,
143 repo_name)
143 repo_name)
144 if perm is not True:
144 if perm is not True:
145 return HTTPForbidden()(environ, start_response)
145 return HTTPForbidden()(environ, start_response)
146
146
147 extras = {'ip': ipaddr,
147 extras = {'ip': ipaddr,
148 'username': username,
148 'username': username,
149 'action': action,
149 'action': action,
150 'repository': repo_name}
150 'repository': repo_name}
151
151
152 #======================================================================
152 #======================================================================
153 # MERCURIAL REQUEST HANDLING
153 # MERCURIAL REQUEST HANDLING
154 #======================================================================
154 #======================================================================
155
155
156 repo_path = safe_str(os.path.join(self.basepath, repo_name))
156 repo_path = safe_str(os.path.join(self.basepath, repo_name))
157 log.debug('Repository path is %s' % repo_path)
157 log.debug('Repository path is %s' % repo_path)
158
158
159 baseui = make_ui('db')
159 baseui = make_ui('db')
160 self.__inject_extras(repo_path, baseui, extras)
160 self.__inject_extras(repo_path, baseui, extras)
161
161
162
162
163 # quick check if that dir exists...
163 # quick check if that dir exists...
164 if check_repo_fast(repo_name, self.basepath) is False:
164 if is_valid_repo(repo_name, self.basepath) is False:
165 return HTTPNotFound()(environ, start_response)
165 return HTTPNotFound()(environ, start_response)
166
166
167 try:
167 try:
168 #invalidate cache on push
168 #invalidate cache on push
169 if action == 'push':
169 if action == 'push':
170 self.__invalidate_cache(repo_name)
170 self.__invalidate_cache(repo_name)
171
171
172 app = self.__make_app(repo_path, baseui, extras)
172 app = self.__make_app(repo_path, baseui, extras)
173 return app(environ, start_response)
173 return app(environ, start_response)
174 except RepoError, e:
174 except RepoError, e:
175 if str(e).find('not found') != -1:
175 if str(e).find('not found') != -1:
176 return HTTPNotFound()(environ, start_response)
176 return HTTPNotFound()(environ, start_response)
177 except Exception:
177 except Exception:
178 log.error(traceback.format_exc())
178 log.error(traceback.format_exc())
179 return HTTPInternalServerError()(environ, start_response)
179 return HTTPInternalServerError()(environ, start_response)
180
180
181 def __make_app(self, repo_name, baseui, extras):
181 def __make_app(self, repo_name, baseui, extras):
182 """
182 """
183 Make an wsgi application using hgweb, and inject generated baseui
183 Make an wsgi application using hgweb, and inject generated baseui
184 instance, additionally inject some extras into ui object
184 instance, additionally inject some extras into ui object
185 """
185 """
186 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
186 return hgweb_mod.hgweb(repo_name, name=repo_name, baseui=baseui)
187
187
188
188
189 def __check_permission(self, action, user, repo_name):
189 def __check_permission(self, action, user, repo_name):
190 """
190 """
191 Checks permissions using action (push/pull) user and repository
191 Checks permissions using action (push/pull) user and repository
192 name
192 name
193
193
194 :param action: push or pull action
194 :param action: push or pull action
195 :param user: user instance
195 :param user: user instance
196 :param repo_name: repository name
196 :param repo_name: repository name
197 """
197 """
198 if action == 'push':
198 if action == 'push':
199 if not HasPermissionAnyMiddleware('repository.write',
199 if not HasPermissionAnyMiddleware('repository.write',
200 'repository.admin')(user,
200 'repository.admin')(user,
201 repo_name):
201 repo_name):
202 return False
202 return False
203
203
204 else:
204 else:
205 #any other action need at least read permission
205 #any other action need at least read permission
206 if not HasPermissionAnyMiddleware('repository.read',
206 if not HasPermissionAnyMiddleware('repository.read',
207 'repository.write',
207 'repository.write',
208 'repository.admin')(user,
208 'repository.admin')(user,
209 repo_name):
209 repo_name):
210 return False
210 return False
211
211
212 return True
212 return True
213
213
214 def __get_repository(self, environ):
214 def __get_repository(self, environ):
215 """
215 """
216 Get's repository name out of PATH_INFO header
216 Get's repository name out of PATH_INFO header
217
217
218 :param environ: environ where PATH_INFO is stored
218 :param environ: environ where PATH_INFO is stored
219 """
219 """
220 try:
220 try:
221 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
221 repo_name = '/'.join(environ['PATH_INFO'].split('/')[1:])
222 if repo_name.endswith('/'):
222 if repo_name.endswith('/'):
223 repo_name = repo_name.rstrip('/')
223 repo_name = repo_name.rstrip('/')
224 except:
224 except:
225 log.error(traceback.format_exc())
225 log.error(traceback.format_exc())
226 raise
226 raise
227
227
228 return repo_name
228 return repo_name
229
229
230 def __get_user(self, username):
230 def __get_user(self, username):
231 return User.by_username(username)
231 return User.by_username(username)
232
232
233 def __get_action(self, environ):
233 def __get_action(self, environ):
234 """
234 """
235 Maps mercurial request commands into a clone,pull or push command.
235 Maps mercurial request commands into a clone,pull or push command.
236 This should always return a valid command string
236 This should always return a valid command string
237
237
238 :param environ:
238 :param environ:
239 """
239 """
240 mapping = {'changegroup': 'pull',
240 mapping = {'changegroup': 'pull',
241 'changegroupsubset': 'pull',
241 'changegroupsubset': 'pull',
242 'stream_out': 'pull',
242 'stream_out': 'pull',
243 'listkeys': 'pull',
243 'listkeys': 'pull',
244 'unbundle': 'push',
244 'unbundle': 'push',
245 'pushkey': 'push', }
245 'pushkey': 'push', }
246 for qry in environ['QUERY_STRING'].split('&'):
246 for qry in environ['QUERY_STRING'].split('&'):
247 if qry.startswith('cmd'):
247 if qry.startswith('cmd'):
248 cmd = qry.split('=')[-1]
248 cmd = qry.split('=')[-1]
249 if cmd in mapping:
249 if cmd in mapping:
250 return mapping[cmd]
250 return mapping[cmd]
251 else:
251 else:
252 return 'pull'
252 return 'pull'
253
253
254 def __invalidate_cache(self, repo_name):
254 def __invalidate_cache(self, repo_name):
255 """we know that some change was made to repositories and we should
255 """we know that some change was made to repositories and we should
256 invalidate the cache to see the changes right away but only for
256 invalidate the cache to see the changes right away but only for
257 push requests"""
257 push requests"""
258 invalidate_cache('get_repo_cached_%s' % repo_name)
258 invalidate_cache('get_repo_cached_%s' % repo_name)
259
259
260 def __inject_extras(self,repo_path, baseui, extras={}):
260 def __inject_extras(self,repo_path, baseui, extras={}):
261 """
261 """
262 Injects some extra params into baseui instance
262 Injects some extra params into baseui instance
263
263
264 also overwrites global settings with those takes from local hgrc file
264 also overwrites global settings with those takes from local hgrc file
265
265
266 :param baseui: baseui instance
266 :param baseui: baseui instance
267 :param extras: dict with extra params to put into baseui
267 :param extras: dict with extra params to put into baseui
268 """
268 """
269
269
270 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
270 hgrc = os.path.join(repo_path, '.hg', 'hgrc')
271
271
272 # make our hgweb quiet so it doesn't print output
272 # make our hgweb quiet so it doesn't print output
273 baseui.setconfig('ui', 'quiet', 'true')
273 baseui.setconfig('ui', 'quiet', 'true')
274
274
275 #inject some additional parameters that will be available in ui
275 #inject some additional parameters that will be available in ui
276 #for hooks
276 #for hooks
277 for k, v in extras.items():
277 for k, v in extras.items():
278 baseui.setconfig('rhodecode_extras', k, v)
278 baseui.setconfig('rhodecode_extras', k, v)
279
279
280 repoui = make_ui('file', hgrc, False)
280 repoui = make_ui('file', hgrc, False)
281
281
282 if repoui:
282 if repoui:
283 #overwrite our ui instance with the section from hgrc file
283 #overwrite our ui instance with the section from hgrc file
284 for section in ui_sections:
284 for section in ui_sections:
285 for k, v in repoui.configitems(section):
285 for k, v in repoui.configitems(section):
286 baseui.setconfig(section, k, v)
286 baseui.setconfig(section, k, v)
287
287
@@ -1,611 +1,611 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.utils
3 rhodecode.lib.utils
4 ~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~
5
5
6 Utilities library for RhodeCode
6 Utilities library for RhodeCode
7
7
8 :created_on: Apr 18, 2010
8 :created_on: Apr 18, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import os
26 import os
27 import logging
27 import logging
28 import datetime
28 import datetime
29 import traceback
29 import traceback
30 import paste
30 import paste
31 import beaker
31 import beaker
32 from os.path import dirname as dn, join as jn
32 from os.path import dirname as dn, join as jn
33
33
34 from paste.script.command import Command, BadCommand
34 from paste.script.command import Command, BadCommand
35
35
36 from mercurial import ui, config
36 from mercurial import ui, config
37
37
38 from webhelpers.text import collapse, remove_formatting, strip_tags
38 from webhelpers.text import collapse, remove_formatting, strip_tags
39
39
40 from vcs import get_backend
40 from vcs import get_backend
41 from vcs.backends.base import BaseChangeset
41 from vcs.backends.base import BaseChangeset
42 from vcs.utils.lazy import LazyProperty
42 from vcs.utils.lazy import LazyProperty
43 from vcs.utils.helpers import get_scm
43 from vcs.utils.helpers import get_scm
44 from vcs.exceptions import VCSError
44 from vcs.exceptions import VCSError
45
45
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.caching_query import FromCache
47 from rhodecode.model.caching_query import FromCache
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
48 from rhodecode.model.db import Repository, User, RhodeCodeUi, UserLog, Group, \
49 RhodeCodeSettings
49 RhodeCodeSettings
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 def recursive_replace(str, replace=' '):
55 def recursive_replace(str, replace=' '):
56 """Recursive replace of given sign to just one instance
56 """Recursive replace of given sign to just one instance
57
57
58 :param str: given string
58 :param str: given string
59 :param replace: char to find and replace multiple instances
59 :param replace: char to find and replace multiple instances
60
60
61 Examples::
61 Examples::
62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
62 >>> recursive_replace("Mighty---Mighty-Bo--sstones",'-')
63 'Mighty-Mighty-Bo-sstones'
63 'Mighty-Mighty-Bo-sstones'
64 """
64 """
65
65
66 if str.find(replace * 2) == -1:
66 if str.find(replace * 2) == -1:
67 return str
67 return str
68 else:
68 else:
69 str = str.replace(replace * 2, replace)
69 str = str.replace(replace * 2, replace)
70 return recursive_replace(str, replace)
70 return recursive_replace(str, replace)
71
71
72
72
73 def repo_name_slug(value):
73 def repo_name_slug(value):
74 """Return slug of name of repository
74 """Return slug of name of repository
75 This function is called on each creation/modification
75 This function is called on each creation/modification
76 of repository to prevent bad names in repo
76 of repository to prevent bad names in repo
77 """
77 """
78
78
79 slug = remove_formatting(value)
79 slug = remove_formatting(value)
80 slug = strip_tags(slug)
80 slug = strip_tags(slug)
81
81
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
82 for c in """=[]\;'"<>,/~!@#$%^&*()+{}|: """:
83 slug = slug.replace(c, '-')
83 slug = slug.replace(c, '-')
84 slug = recursive_replace(slug, '-')
84 slug = recursive_replace(slug, '-')
85 slug = collapse(slug, '-')
85 slug = collapse(slug, '-')
86 return slug
86 return slug
87
87
88
88
89 def get_repo_slug(request):
89 def get_repo_slug(request):
90 return request.environ['pylons.routes_dict'].get('repo_name')
90 return request.environ['pylons.routes_dict'].get('repo_name')
91
91
92
92
93 def action_logger(user, action, repo, ipaddr='', sa=None):
93 def action_logger(user, action, repo, ipaddr='', sa=None):
94 """
94 """
95 Action logger for various actions made by users
95 Action logger for various actions made by users
96
96
97 :param user: user that made this action, can be a unique username string or
97 :param user: user that made this action, can be a unique username string or
98 object containing user_id attribute
98 object containing user_id attribute
99 :param action: action to log, should be on of predefined unique actions for
99 :param action: action to log, should be on of predefined unique actions for
100 easy translations
100 easy translations
101 :param repo: string name of repository or object containing repo_id,
101 :param repo: string name of repository or object containing repo_id,
102 that action was made on
102 that action was made on
103 :param ipaddr: optional ip address from what the action was made
103 :param ipaddr: optional ip address from what the action was made
104 :param sa: optional sqlalchemy session
104 :param sa: optional sqlalchemy session
105
105
106 """
106 """
107
107
108 if not sa:
108 if not sa:
109 sa = meta.Session()
109 sa = meta.Session()
110
110
111 try:
111 try:
112 if hasattr(user, 'user_id'):
112 if hasattr(user, 'user_id'):
113 user_obj = user
113 user_obj = user
114 elif isinstance(user, basestring):
114 elif isinstance(user, basestring):
115 user_obj = User.by_username(user, cache=False)
115 user_obj = User.by_username(user, cache=False)
116 else:
116 else:
117 raise Exception('You have to provide user object or username')
117 raise Exception('You have to provide user object or username')
118
118
119 rm = RepoModel()
119 rm = RepoModel()
120 if hasattr(repo, 'repo_id'):
120 if hasattr(repo, 'repo_id'):
121 repo_obj = rm.get(repo.repo_id, cache=False)
121 repo_obj = rm.get(repo.repo_id, cache=False)
122 repo_name = repo_obj.repo_name
122 repo_name = repo_obj.repo_name
123 elif isinstance(repo, basestring):
123 elif isinstance(repo, basestring):
124 repo_name = repo.lstrip('/')
124 repo_name = repo.lstrip('/')
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
125 repo_obj = rm.get_by_repo_name(repo_name, cache=False)
126 else:
126 else:
127 raise Exception('You have to provide repository to action logger')
127 raise Exception('You have to provide repository to action logger')
128
128
129 user_log = UserLog()
129 user_log = UserLog()
130 user_log.user_id = user_obj.user_id
130 user_log.user_id = user_obj.user_id
131 user_log.action = action
131 user_log.action = action
132
132
133 user_log.repository_id = repo_obj.repo_id
133 user_log.repository_id = repo_obj.repo_id
134 user_log.repository_name = repo_name
134 user_log.repository_name = repo_name
135
135
136 user_log.action_date = datetime.datetime.now()
136 user_log.action_date = datetime.datetime.now()
137 user_log.user_ip = ipaddr
137 user_log.user_ip = ipaddr
138 sa.add(user_log)
138 sa.add(user_log)
139 sa.commit()
139 sa.commit()
140
140
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
141 log.info('Adding user %s, action %s on %s', user_obj, action, repo)
142 except:
142 except:
143 log.error(traceback.format_exc())
143 log.error(traceback.format_exc())
144 sa.rollback()
144 sa.rollback()
145
145
146
146
147 def get_repos(path, recursive=False):
147 def get_repos(path, recursive=False):
148 """
148 """
149 Scans given path for repos and return (name,(type,path)) tuple
149 Scans given path for repos and return (name,(type,path)) tuple
150
150
151 :param path: path to scann for repositories
151 :param path: path to scann for repositories
152 :param recursive: recursive search and return names with subdirs in front
152 :param recursive: recursive search and return names with subdirs in front
153 """
153 """
154 from vcs.utils.helpers import get_scm
154 from vcs.utils.helpers import get_scm
155 from vcs.exceptions import VCSError
155 from vcs.exceptions import VCSError
156
156
157 if path.endswith(os.sep):
157 if path.endswith(os.sep):
158 #remove ending slash for better results
158 #remove ending slash for better results
159 path = path[:-1]
159 path = path[:-1]
160
160
161 def _get_repos(p):
161 def _get_repos(p):
162 if not os.access(p, os.W_OK):
162 if not os.access(p, os.W_OK):
163 return
163 return
164 for dirpath in os.listdir(p):
164 for dirpath in os.listdir(p):
165 if os.path.isfile(os.path.join(p, dirpath)):
165 if os.path.isfile(os.path.join(p, dirpath)):
166 continue
166 continue
167 cur_path = os.path.join(p, dirpath)
167 cur_path = os.path.join(p, dirpath)
168 try:
168 try:
169 scm_info = get_scm(cur_path)
169 scm_info = get_scm(cur_path)
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
170 yield scm_info[1].split(path)[-1].lstrip(os.sep), scm_info
171 except VCSError:
171 except VCSError:
172 if not recursive:
172 if not recursive:
173 continue
173 continue
174 #check if this dir containts other repos for recursive scan
174 #check if this dir containts other repos for recursive scan
175 rec_path = os.path.join(p, dirpath)
175 rec_path = os.path.join(p, dirpath)
176 if os.path.isdir(rec_path):
176 if os.path.isdir(rec_path):
177 for inner_scm in _get_repos(rec_path):
177 for inner_scm in _get_repos(rec_path):
178 yield inner_scm
178 yield inner_scm
179
179
180 return _get_repos(path)
180 return _get_repos(path)
181
181
182
182
183 def check_repo_fast(repo_name, base_path):
183 def is_valid_repo(repo_name, base_path):
184 """
184 """
185 Returns True if given path is a valid repository False otherwise
185 Returns True if given path is a valid repository False otherwise
186 :param repo_name:
186 :param repo_name:
187 :param base_path:
187 :param base_path:
188
188
189 :return True: if given path is a valid repository
189 :return True: if given path is a valid repository
190 """
190 """
191 full_path = os.path.join(base_path, repo_name)
191 full_path = os.path.join(base_path, repo_name)
192
192
193 try:
193 try:
194 get_scm(full_path)
194 get_scm(full_path)
195 return True
195 return True
196 except VCSError:
196 except VCSError:
197 return False
197 return False
198
198
199 def check_repos_group_fast(repos_group_name, base_path):
199 def is_valid_repos_group(repos_group_name, base_path):
200 """
200 """
201 Returns True if given path is a repos group False otherwise
201 Returns True if given path is a repos group False otherwise
202
202
203 :param repo_name:
203 :param repo_name:
204 :param base_path:
204 :param base_path:
205 """
205 """
206 full_path = os.path.join(base_path, repos_group_name)
206 full_path = os.path.join(base_path, repos_group_name)
207
207
208 # check if it's not a repo
208 # check if it's not a repo
209 if check_repo_fast(repos_group_name, base_path):
209 if is_valid_repo(repos_group_name, base_path):
210 return False
210 return False
211
211
212 # check if it's a valid path
212 # check if it's a valid path
213 if os.path.isdir(full_path):
213 if os.path.isdir(full_path):
214 return True
214 return True
215
215
216 return False
216 return False
217
217
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
218 def ask_ok(prompt, retries=4, complaint='Yes or no, please!'):
219 while True:
219 while True:
220 ok = raw_input(prompt)
220 ok = raw_input(prompt)
221 if ok in ('y', 'ye', 'yes'):
221 if ok in ('y', 'ye', 'yes'):
222 return True
222 return True
223 if ok in ('n', 'no', 'nop', 'nope'):
223 if ok in ('n', 'no', 'nop', 'nope'):
224 return False
224 return False
225 retries = retries - 1
225 retries = retries - 1
226 if retries < 0:
226 if retries < 0:
227 raise IOError
227 raise IOError
228 print complaint
228 print complaint
229
229
230 #propagated from mercurial documentation
230 #propagated from mercurial documentation
231 ui_sections = ['alias', 'auth',
231 ui_sections = ['alias', 'auth',
232 'decode/encode', 'defaults',
232 'decode/encode', 'defaults',
233 'diff', 'email',
233 'diff', 'email',
234 'extensions', 'format',
234 'extensions', 'format',
235 'merge-patterns', 'merge-tools',
235 'merge-patterns', 'merge-tools',
236 'hooks', 'http_proxy',
236 'hooks', 'http_proxy',
237 'smtp', 'patch',
237 'smtp', 'patch',
238 'paths', 'profiling',
238 'paths', 'profiling',
239 'server', 'trusted',
239 'server', 'trusted',
240 'ui', 'web', ]
240 'ui', 'web', ]
241
241
242
242
243 def make_ui(read_from='file', path=None, checkpaths=True):
243 def make_ui(read_from='file', path=None, checkpaths=True):
244 """A function that will read python rc files or database
244 """A function that will read python rc files or database
245 and make an mercurial ui object from read options
245 and make an mercurial ui object from read options
246
246
247 :param path: path to mercurial config file
247 :param path: path to mercurial config file
248 :param checkpaths: check the path
248 :param checkpaths: check the path
249 :param read_from: read from 'file' or 'db'
249 :param read_from: read from 'file' or 'db'
250 """
250 """
251
251
252 baseui = ui.ui()
252 baseui = ui.ui()
253
253
254 #clean the baseui object
254 #clean the baseui object
255 baseui._ocfg = config.config()
255 baseui._ocfg = config.config()
256 baseui._ucfg = config.config()
256 baseui._ucfg = config.config()
257 baseui._tcfg = config.config()
257 baseui._tcfg = config.config()
258
258
259 if read_from == 'file':
259 if read_from == 'file':
260 if not os.path.isfile(path):
260 if not os.path.isfile(path):
261 log.warning('Unable to read config file %s' % path)
261 log.warning('Unable to read config file %s' % path)
262 return False
262 return False
263 log.debug('reading hgrc from %s', path)
263 log.debug('reading hgrc from %s', path)
264 cfg = config.config()
264 cfg = config.config()
265 cfg.read(path)
265 cfg.read(path)
266 for section in ui_sections:
266 for section in ui_sections:
267 for k, v in cfg.items(section):
267 for k, v in cfg.items(section):
268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
268 log.debug('settings ui from file[%s]%s:%s', section, k, v)
269 baseui.setconfig(section, k, v)
269 baseui.setconfig(section, k, v)
270
270
271 elif read_from == 'db':
271 elif read_from == 'db':
272 sa = meta.Session()
272 sa = meta.Session()
273 ret = sa.query(RhodeCodeUi)\
273 ret = sa.query(RhodeCodeUi)\
274 .options(FromCache("sql_cache_short",
274 .options(FromCache("sql_cache_short",
275 "get_hg_ui_settings")).all()
275 "get_hg_ui_settings")).all()
276
276
277 hg_ui = ret
277 hg_ui = ret
278 for ui_ in hg_ui:
278 for ui_ in hg_ui:
279 if ui_.ui_active:
279 if ui_.ui_active:
280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
280 log.debug('settings ui from db[%s]%s:%s', ui_.ui_section,
281 ui_.ui_key, ui_.ui_value)
281 ui_.ui_key, ui_.ui_value)
282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
282 baseui.setconfig(ui_.ui_section, ui_.ui_key, ui_.ui_value)
283
283
284 meta.Session.remove()
284 meta.Session.remove()
285 return baseui
285 return baseui
286
286
287
287
288 def set_rhodecode_config(config):
288 def set_rhodecode_config(config):
289 """Updates pylons config with new settings from database
289 """Updates pylons config with new settings from database
290
290
291 :param config:
291 :param config:
292 """
292 """
293 hgsettings = RhodeCodeSettings.get_app_settings()
293 hgsettings = RhodeCodeSettings.get_app_settings()
294
294
295 for k, v in hgsettings.items():
295 for k, v in hgsettings.items():
296 config[k] = v
296 config[k] = v
297
297
298
298
299 def invalidate_cache(cache_key, *args):
299 def invalidate_cache(cache_key, *args):
300 """Puts cache invalidation task into db for
300 """Puts cache invalidation task into db for
301 further global cache invalidation
301 further global cache invalidation
302 """
302 """
303
303
304 from rhodecode.model.scm import ScmModel
304 from rhodecode.model.scm import ScmModel
305
305
306 if cache_key.startswith('get_repo_cached_'):
306 if cache_key.startswith('get_repo_cached_'):
307 name = cache_key.split('get_repo_cached_')[-1]
307 name = cache_key.split('get_repo_cached_')[-1]
308 ScmModel().mark_for_invalidation(name)
308 ScmModel().mark_for_invalidation(name)
309
309
310
310
311 class EmptyChangeset(BaseChangeset):
311 class EmptyChangeset(BaseChangeset):
312 """
312 """
313 An dummy empty changeset. It's possible to pass hash when creating
313 An dummy empty changeset. It's possible to pass hash when creating
314 an EmptyChangeset
314 an EmptyChangeset
315 """
315 """
316
316
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
317 def __init__(self, cs='0' * 40, repo=None, requested_revision=None, alias=None):
318 self._empty_cs = cs
318 self._empty_cs = cs
319 self.revision = -1
319 self.revision = -1
320 self.message = ''
320 self.message = ''
321 self.author = ''
321 self.author = ''
322 self.date = ''
322 self.date = ''
323 self.repository = repo
323 self.repository = repo
324 self.requested_revision = requested_revision
324 self.requested_revision = requested_revision
325 self.alias = alias
325 self.alias = alias
326
326
327 @LazyProperty
327 @LazyProperty
328 def raw_id(self):
328 def raw_id(self):
329 """Returns raw string identifying this changeset, useful for web
329 """Returns raw string identifying this changeset, useful for web
330 representation.
330 representation.
331 """
331 """
332
332
333 return self._empty_cs
333 return self._empty_cs
334
334
335 @LazyProperty
335 @LazyProperty
336 def branch(self):
336 def branch(self):
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
337 return get_backend(self.alias).DEFAULT_BRANCH_NAME
338
338
339 @LazyProperty
339 @LazyProperty
340 def short_id(self):
340 def short_id(self):
341 return self.raw_id[:12]
341 return self.raw_id[:12]
342
342
343 def get_file_changeset(self, path):
343 def get_file_changeset(self, path):
344 return self
344 return self
345
345
346 def get_file_content(self, path):
346 def get_file_content(self, path):
347 return u''
347 return u''
348
348
349 def get_file_size(self, path):
349 def get_file_size(self, path):
350 return 0
350 return 0
351
351
352
352
353 def map_groups(groups):
353 def map_groups(groups):
354 """Checks for groups existence, and creates groups structures.
354 """Checks for groups existence, and creates groups structures.
355 It returns last group in structure
355 It returns last group in structure
356
356
357 :param groups: list of groups structure
357 :param groups: list of groups structure
358 """
358 """
359 sa = meta.Session()
359 sa = meta.Session()
360
360
361 parent = None
361 parent = None
362 group = None
362 group = None
363 for lvl, group_name in enumerate(groups[:-1]):
363 for lvl, group_name in enumerate(groups[:-1]):
364 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
364 group = sa.query(Group).filter(Group.group_name == group_name).scalar()
365
365
366 if group is None:
366 if group is None:
367 group = Group(group_name, parent)
367 group = Group(group_name, parent)
368 sa.add(group)
368 sa.add(group)
369 sa.commit()
369 sa.commit()
370
370
371 parent = group
371 parent = group
372
372
373 return group
373 return group
374
374
375
375
376 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
376 def repo2db_mapper(initial_repo_list, remove_obsolete=False):
377 """maps all repos given in initial_repo_list, non existing repositories
377 """maps all repos given in initial_repo_list, non existing repositories
378 are created, if remove_obsolete is True it also check for db entries
378 are created, if remove_obsolete is True it also check for db entries
379 that are not in initial_repo_list and removes them.
379 that are not in initial_repo_list and removes them.
380
380
381 :param initial_repo_list: list of repositories found by scanning methods
381 :param initial_repo_list: list of repositories found by scanning methods
382 :param remove_obsolete: check for obsolete entries in database
382 :param remove_obsolete: check for obsolete entries in database
383 """
383 """
384
384
385 sa = meta.Session()
385 sa = meta.Session()
386 rm = RepoModel()
386 rm = RepoModel()
387 user = sa.query(User).filter(User.admin == True).first()
387 user = sa.query(User).filter(User.admin == True).first()
388 added = []
388 added = []
389 for name, repo in initial_repo_list.items():
389 for name, repo in initial_repo_list.items():
390 group = map_groups(name.split(os.sep))
390 group = map_groups(name.split(os.sep))
391 if not rm.get_by_repo_name(name, cache=False):
391 if not rm.get_by_repo_name(name, cache=False):
392 log.info('repository %s not found creating default', name)
392 log.info('repository %s not found creating default', name)
393 added.append(name)
393 added.append(name)
394 form_data = {
394 form_data = {
395 'repo_name': name,
395 'repo_name': name,
396 'repo_name_full': name,
396 'repo_name_full': name,
397 'repo_type': repo.alias,
397 'repo_type': repo.alias,
398 'description': repo.description \
398 'description': repo.description \
399 if repo.description != 'unknown' else \
399 if repo.description != 'unknown' else \
400 '%s repository' % name,
400 '%s repository' % name,
401 'private': False,
401 'private': False,
402 'group_id': getattr(group, 'group_id', None)
402 'group_id': getattr(group, 'group_id', None)
403 }
403 }
404 rm.create(form_data, user, just_db=True)
404 rm.create(form_data, user, just_db=True)
405
405
406 removed = []
406 removed = []
407 if remove_obsolete:
407 if remove_obsolete:
408 #remove from database those repositories that are not in the filesystem
408 #remove from database those repositories that are not in the filesystem
409 for repo in sa.query(Repository).all():
409 for repo in sa.query(Repository).all():
410 if repo.repo_name not in initial_repo_list.keys():
410 if repo.repo_name not in initial_repo_list.keys():
411 removed.append(repo.repo_name)
411 removed.append(repo.repo_name)
412 sa.delete(repo)
412 sa.delete(repo)
413 sa.commit()
413 sa.commit()
414
414
415 return added, removed
415 return added, removed
416
416
417 #set cache regions for beaker so celery can utilise it
417 #set cache regions for beaker so celery can utilise it
418 def add_cache(settings):
418 def add_cache(settings):
419 cache_settings = {'regions': None}
419 cache_settings = {'regions': None}
420 for key in settings.keys():
420 for key in settings.keys():
421 for prefix in ['beaker.cache.', 'cache.']:
421 for prefix in ['beaker.cache.', 'cache.']:
422 if key.startswith(prefix):
422 if key.startswith(prefix):
423 name = key.split(prefix)[1].strip()
423 name = key.split(prefix)[1].strip()
424 cache_settings[name] = settings[key].strip()
424 cache_settings[name] = settings[key].strip()
425 if cache_settings['regions']:
425 if cache_settings['regions']:
426 for region in cache_settings['regions'].split(','):
426 for region in cache_settings['regions'].split(','):
427 region = region.strip()
427 region = region.strip()
428 region_settings = {}
428 region_settings = {}
429 for key, value in cache_settings.items():
429 for key, value in cache_settings.items():
430 if key.startswith(region):
430 if key.startswith(region):
431 region_settings[key.split('.')[1]] = value
431 region_settings[key.split('.')[1]] = value
432 region_settings['expire'] = int(region_settings.get('expire',
432 region_settings['expire'] = int(region_settings.get('expire',
433 60))
433 60))
434 region_settings.setdefault('lock_dir',
434 region_settings.setdefault('lock_dir',
435 cache_settings.get('lock_dir'))
435 cache_settings.get('lock_dir'))
436 region_settings.setdefault('data_dir',
436 region_settings.setdefault('data_dir',
437 cache_settings.get('data_dir'))
437 cache_settings.get('data_dir'))
438
438
439 if 'type' not in region_settings:
439 if 'type' not in region_settings:
440 region_settings['type'] = cache_settings.get('type',
440 region_settings['type'] = cache_settings.get('type',
441 'memory')
441 'memory')
442 beaker.cache.cache_regions[region] = region_settings
442 beaker.cache.cache_regions[region] = region_settings
443
443
444
444
445 def get_current_revision():
445 def get_current_revision():
446 """Returns tuple of (number, id) from repository containing this package
446 """Returns tuple of (number, id) from repository containing this package
447 or None if repository could not be found.
447 or None if repository could not be found.
448 """
448 """
449
449
450 try:
450 try:
451 from vcs import get_repo
451 from vcs import get_repo
452 from vcs.utils.helpers import get_scm
452 from vcs.utils.helpers import get_scm
453 from vcs.exceptions import RepositoryError, VCSError
453 from vcs.exceptions import RepositoryError, VCSError
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
454 repopath = os.path.join(os.path.dirname(__file__), '..', '..')
455 scm = get_scm(repopath)[0]
455 scm = get_scm(repopath)[0]
456 repo = get_repo(path=repopath, alias=scm)
456 repo = get_repo(path=repopath, alias=scm)
457 tip = repo.get_changeset()
457 tip = repo.get_changeset()
458 return (tip.revision, tip.short_id)
458 return (tip.revision, tip.short_id)
459 except (ImportError, RepositoryError, VCSError), err:
459 except (ImportError, RepositoryError, VCSError), err:
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
460 logging.debug("Cannot retrieve rhodecode's revision. Original error "
461 "was: %s" % err)
461 "was: %s" % err)
462 return None
462 return None
463
463
464
464
465 #==============================================================================
465 #==============================================================================
466 # TEST FUNCTIONS AND CREATORS
466 # TEST FUNCTIONS AND CREATORS
467 #==============================================================================
467 #==============================================================================
468 def create_test_index(repo_location, config, full_index):
468 def create_test_index(repo_location, config, full_index):
469 """
469 """
470 Makes default test index
470 Makes default test index
471
471
472 :param config: test config
472 :param config: test config
473 :param full_index:
473 :param full_index:
474 """
474 """
475
475
476 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
476 from rhodecode.lib.indexers.daemon import WhooshIndexingDaemon
477 from rhodecode.lib.pidlock import DaemonLock, LockHeld
477 from rhodecode.lib.pidlock import DaemonLock, LockHeld
478
478
479 repo_location = repo_location
479 repo_location = repo_location
480
480
481 index_location = os.path.join(config['app_conf']['index_dir'])
481 index_location = os.path.join(config['app_conf']['index_dir'])
482 if not os.path.exists(index_location):
482 if not os.path.exists(index_location):
483 os.makedirs(index_location)
483 os.makedirs(index_location)
484
484
485 try:
485 try:
486 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
486 l = DaemonLock(file=jn(dn(index_location), 'make_index.lock'))
487 WhooshIndexingDaemon(index_location=index_location,
487 WhooshIndexingDaemon(index_location=index_location,
488 repo_location=repo_location)\
488 repo_location=repo_location)\
489 .run(full_index=full_index)
489 .run(full_index=full_index)
490 l.release()
490 l.release()
491 except LockHeld:
491 except LockHeld:
492 pass
492 pass
493
493
494
494
495 def create_test_env(repos_test_path, config):
495 def create_test_env(repos_test_path, config):
496 """Makes a fresh database and
496 """Makes a fresh database and
497 install test repository into tmp dir
497 install test repository into tmp dir
498 """
498 """
499 from rhodecode.lib.db_manage import DbManage
499 from rhodecode.lib.db_manage import DbManage
500 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
500 from rhodecode.tests import HG_REPO, GIT_REPO, NEW_HG_REPO, NEW_GIT_REPO, \
501 HG_FORK, GIT_FORK, TESTS_TMP_PATH
501 HG_FORK, GIT_FORK, TESTS_TMP_PATH
502 import tarfile
502 import tarfile
503 import shutil
503 import shutil
504 from os.path import abspath
504 from os.path import abspath
505
505
506 # PART ONE create db
506 # PART ONE create db
507 dbconf = config['sqlalchemy.db1.url']
507 dbconf = config['sqlalchemy.db1.url']
508 log.debug('making test db %s', dbconf)
508 log.debug('making test db %s', dbconf)
509
509
510 # create test dir if it doesn't exist
510 # create test dir if it doesn't exist
511 if not os.path.isdir(repos_test_path):
511 if not os.path.isdir(repos_test_path):
512 log.debug('Creating testdir %s' % repos_test_path)
512 log.debug('Creating testdir %s' % repos_test_path)
513 os.makedirs(repos_test_path)
513 os.makedirs(repos_test_path)
514
514
515 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
515 dbmanage = DbManage(log_sql=True, dbconf=dbconf, root=config['here'],
516 tests=True)
516 tests=True)
517 dbmanage.create_tables(override=True)
517 dbmanage.create_tables(override=True)
518 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
518 dbmanage.create_settings(dbmanage.config_prompt(repos_test_path))
519 dbmanage.create_default_user()
519 dbmanage.create_default_user()
520 dbmanage.admin_prompt()
520 dbmanage.admin_prompt()
521 dbmanage.create_permissions()
521 dbmanage.create_permissions()
522 dbmanage.populate_default_permissions()
522 dbmanage.populate_default_permissions()
523
523
524 # PART TWO make test repo
524 # PART TWO make test repo
525 log.debug('making test vcs repositories')
525 log.debug('making test vcs repositories')
526
526
527 idx_path = config['app_conf']['index_dir']
527 idx_path = config['app_conf']['index_dir']
528 data_path = config['app_conf']['cache_dir']
528 data_path = config['app_conf']['cache_dir']
529
529
530 #clean index and data
530 #clean index and data
531 if idx_path and os.path.exists(idx_path):
531 if idx_path and os.path.exists(idx_path):
532 log.debug('remove %s' % idx_path)
532 log.debug('remove %s' % idx_path)
533 shutil.rmtree(idx_path)
533 shutil.rmtree(idx_path)
534
534
535 if data_path and os.path.exists(data_path):
535 if data_path and os.path.exists(data_path):
536 log.debug('remove %s' % data_path)
536 log.debug('remove %s' % data_path)
537 shutil.rmtree(data_path)
537 shutil.rmtree(data_path)
538
538
539 #CREATE DEFAULT HG REPOSITORY
539 #CREATE DEFAULT HG REPOSITORY
540 cur_dir = dn(dn(abspath(__file__)))
540 cur_dir = dn(dn(abspath(__file__)))
541 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
541 tar = tarfile.open(jn(cur_dir, 'tests', "vcs_test_hg.tar.gz"))
542 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
542 tar.extractall(jn(TESTS_TMP_PATH, HG_REPO))
543 tar.close()
543 tar.close()
544
544
545
545
546 #==============================================================================
546 #==============================================================================
547 # PASTER COMMANDS
547 # PASTER COMMANDS
548 #==============================================================================
548 #==============================================================================
549 class BasePasterCommand(Command):
549 class BasePasterCommand(Command):
550 """
550 """
551 Abstract Base Class for paster commands.
551 Abstract Base Class for paster commands.
552
552
553 The celery commands are somewhat aggressive about loading
553 The celery commands are somewhat aggressive about loading
554 celery.conf, and since our module sets the `CELERY_LOADER`
554 celery.conf, and since our module sets the `CELERY_LOADER`
555 environment variable to our loader, we have to bootstrap a bit and
555 environment variable to our loader, we have to bootstrap a bit and
556 make sure we've had a chance to load the pylons config off of the
556 make sure we've had a chance to load the pylons config off of the
557 command line, otherwise everything fails.
557 command line, otherwise everything fails.
558 """
558 """
559 min_args = 1
559 min_args = 1
560 min_args_error = "Please provide a paster config file as an argument."
560 min_args_error = "Please provide a paster config file as an argument."
561 takes_config_file = 1
561 takes_config_file = 1
562 requires_config_file = True
562 requires_config_file = True
563
563
564 def notify_msg(self, msg, log=False):
564 def notify_msg(self, msg, log=False):
565 """Make a notification to user, additionally if logger is passed
565 """Make a notification to user, additionally if logger is passed
566 it logs this action using given logger
566 it logs this action using given logger
567
567
568 :param msg: message that will be printed to user
568 :param msg: message that will be printed to user
569 :param log: logging instance, to use to additionally log this message
569 :param log: logging instance, to use to additionally log this message
570
570
571 """
571 """
572 if log and isinstance(log, logging):
572 if log and isinstance(log, logging):
573 log(msg)
573 log(msg)
574
574
575 def run(self, args):
575 def run(self, args):
576 """
576 """
577 Overrides Command.run
577 Overrides Command.run
578
578
579 Checks for a config file argument and loads it.
579 Checks for a config file argument and loads it.
580 """
580 """
581 if len(args) < self.min_args:
581 if len(args) < self.min_args:
582 raise BadCommand(
582 raise BadCommand(
583 self.min_args_error % {'min_args': self.min_args,
583 self.min_args_error % {'min_args': self.min_args,
584 'actual_args': len(args)})
584 'actual_args': len(args)})
585
585
586 # Decrement because we're going to lob off the first argument.
586 # Decrement because we're going to lob off the first argument.
587 # @@ This is hacky
587 # @@ This is hacky
588 self.min_args -= 1
588 self.min_args -= 1
589 self.bootstrap_config(args[0])
589 self.bootstrap_config(args[0])
590 self.update_parser()
590 self.update_parser()
591 return super(BasePasterCommand, self).run(args[1:])
591 return super(BasePasterCommand, self).run(args[1:])
592
592
593 def update_parser(self):
593 def update_parser(self):
594 """
594 """
595 Abstract method. Allows for the class's parser to be updated
595 Abstract method. Allows for the class's parser to be updated
596 before the superclass's `run` method is called. Necessary to
596 before the superclass's `run` method is called. Necessary to
597 allow options/arguments to be passed through to the underlying
597 allow options/arguments to be passed through to the underlying
598 celery command.
598 celery command.
599 """
599 """
600 raise NotImplementedError("Abstract Method.")
600 raise NotImplementedError("Abstract Method.")
601
601
602 def bootstrap_config(self, conf):
602 def bootstrap_config(self, conf):
603 """
603 """
604 Loads the pylons configuration.
604 Loads the pylons configuration.
605 """
605 """
606 from pylons import config as pylonsconfig
606 from pylons import config as pylonsconfig
607
607
608 path_to_ini_file = os.path.realpath(conf)
608 path_to_ini_file = os.path.realpath(conf)
609 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
609 conf = paste.deploy.appconfig('config:' + path_to_ini_file)
610 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
610 pylonsconfig.init_app(conf.global_conf, conf.local_conf)
611
611
@@ -1,358 +1,358 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) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2009-2011 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 import os
25 import os
26 import shutil
26 import shutil
27 import logging
27 import logging
28 import traceback
28 import traceback
29 from datetime import datetime
29 from datetime import datetime
30
30
31 from sqlalchemy.orm import joinedload, make_transient
31 from sqlalchemy.orm import joinedload, make_transient
32
32
33 from vcs.utils.lazy import LazyProperty
33 from vcs.utils.lazy import LazyProperty
34 from vcs.backends import get_backend
34 from vcs.backends import get_backend
35
35
36 from rhodecode.lib import safe_str
36 from rhodecode.lib import safe_str
37
37
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.caching_query import FromCache
39 from rhodecode.model.caching_query import FromCache
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, RepoToPerm, User, Permission, \
41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group
41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, Group
42 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46
46
47 class RepoModel(BaseModel):
47 class RepoModel(BaseModel):
48
48
49 @LazyProperty
49 @LazyProperty
50 def repos_path(self):
50 def repos_path(self):
51 """Get's the repositories root path from database
51 """Get's the repositories root path from database
52 """
52 """
53
53
54 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
54 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
55 return q.ui_value
55 return q.ui_value
56
56
57 def get(self, repo_id, cache=False):
57 def get(self, repo_id, cache=False):
58 repo = self.sa.query(Repository)\
58 repo = self.sa.query(Repository)\
59 .filter(Repository.repo_id == repo_id)
59 .filter(Repository.repo_id == repo_id)
60
60
61 if cache:
61 if cache:
62 repo = repo.options(FromCache("sql_cache_short",
62 repo = repo.options(FromCache("sql_cache_short",
63 "get_repo_%s" % repo_id))
63 "get_repo_%s" % repo_id))
64 return repo.scalar()
64 return repo.scalar()
65
65
66 def get_by_repo_name(self, repo_name, cache=False):
66 def get_by_repo_name(self, repo_name, cache=False):
67 repo = self.sa.query(Repository)\
67 repo = self.sa.query(Repository)\
68 .filter(Repository.repo_name == repo_name)
68 .filter(Repository.repo_name == repo_name)
69
69
70 if cache:
70 if cache:
71 repo = repo.options(FromCache("sql_cache_short",
71 repo = repo.options(FromCache("sql_cache_short",
72 "get_repo_%s" % repo_name))
72 "get_repo_%s" % repo_name))
73 return repo.scalar()
73 return repo.scalar()
74
74
75
75
76 def get_users_js(self):
76 def get_users_js(self):
77
77
78 users = self.sa.query(User).filter(User.active == True).all()
78 users = self.sa.query(User).filter(User.active == True).all()
79 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
79 u_tmpl = '''{id:%s, fname:"%s", lname:"%s", nname:"%s"},'''
80 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
80 users_array = '[%s]' % '\n'.join([u_tmpl % (u.user_id, u.name,
81 u.lastname, u.username)
81 u.lastname, u.username)
82 for u in users])
82 for u in users])
83 return users_array
83 return users_array
84
84
85 def get_users_groups_js(self):
85 def get_users_groups_js(self):
86 users_groups = self.sa.query(UsersGroup)\
86 users_groups = self.sa.query(UsersGroup)\
87 .filter(UsersGroup.users_group_active == True).all()
87 .filter(UsersGroup.users_group_active == True).all()
88
88
89 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
89 g_tmpl = '''{id:%s, grname:"%s",grmembers:"%s"},'''
90
90
91 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
91 users_groups_array = '[%s]' % '\n'.join([g_tmpl % \
92 (gr.users_group_id, gr.users_group_name,
92 (gr.users_group_id, gr.users_group_name,
93 len(gr.members))
93 len(gr.members))
94 for gr in users_groups])
94 for gr in users_groups])
95 return users_groups_array
95 return users_groups_array
96
96
97 def update(self, repo_name, form_data):
97 def update(self, repo_name, form_data):
98 try:
98 try:
99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
99 cur_repo = self.get_by_repo_name(repo_name, cache=False)
100
100
101 #update permissions
101 #update permissions
102 for member, perm, member_type in form_data['perms_updates']:
102 for member, perm, member_type in form_data['perms_updates']:
103 if member_type == 'user':
103 if member_type == 'user':
104 r2p = self.sa.query(RepoToPerm)\
104 r2p = self.sa.query(RepoToPerm)\
105 .filter(RepoToPerm.user == User.by_username(member))\
105 .filter(RepoToPerm.user == User.by_username(member))\
106 .filter(RepoToPerm.repository == cur_repo)\
106 .filter(RepoToPerm.repository == cur_repo)\
107 .one()
107 .one()
108
108
109 r2p.permission = self.sa.query(Permission)\
109 r2p.permission = self.sa.query(Permission)\
110 .filter(Permission.permission_name ==
110 .filter(Permission.permission_name ==
111 perm).scalar()
111 perm).scalar()
112 self.sa.add(r2p)
112 self.sa.add(r2p)
113 else:
113 else:
114 g2p = self.sa.query(UsersGroupRepoToPerm)\
114 g2p = self.sa.query(UsersGroupRepoToPerm)\
115 .filter(UsersGroupRepoToPerm.users_group ==
115 .filter(UsersGroupRepoToPerm.users_group ==
116 UsersGroup.get_by_group_name(member))\
116 UsersGroup.get_by_group_name(member))\
117 .filter(UsersGroupRepoToPerm.repository ==
117 .filter(UsersGroupRepoToPerm.repository ==
118 cur_repo).one()
118 cur_repo).one()
119
119
120 g2p.permission = self.sa.query(Permission)\
120 g2p.permission = self.sa.query(Permission)\
121 .filter(Permission.permission_name ==
121 .filter(Permission.permission_name ==
122 perm).scalar()
122 perm).scalar()
123 self.sa.add(g2p)
123 self.sa.add(g2p)
124
124
125 #set new permissions
125 #set new permissions
126 for member, perm, member_type in form_data['perms_new']:
126 for member, perm, member_type in form_data['perms_new']:
127 if member_type == 'user':
127 if member_type == 'user':
128 r2p = RepoToPerm()
128 r2p = RepoToPerm()
129 r2p.repository = cur_repo
129 r2p.repository = cur_repo
130 r2p.user = User.by_username(member)
130 r2p.user = User.by_username(member)
131
131
132 r2p.permission = self.sa.query(Permission)\
132 r2p.permission = self.sa.query(Permission)\
133 .filter(Permission.
133 .filter(Permission.
134 permission_name == perm)\
134 permission_name == perm)\
135 .scalar()
135 .scalar()
136 self.sa.add(r2p)
136 self.sa.add(r2p)
137 else:
137 else:
138 g2p = UsersGroupRepoToPerm()
138 g2p = UsersGroupRepoToPerm()
139 g2p.repository = cur_repo
139 g2p.repository = cur_repo
140 g2p.users_group = UsersGroup.get_by_group_name(member)
140 g2p.users_group = UsersGroup.get_by_group_name(member)
141 g2p.permission = self.sa.query(Permission)\
141 g2p.permission = self.sa.query(Permission)\
142 .filter(Permission.
142 .filter(Permission.
143 permission_name == perm)\
143 permission_name == perm)\
144 .scalar()
144 .scalar()
145 self.sa.add(g2p)
145 self.sa.add(g2p)
146
146
147 #update current repo
147 #update current repo
148 for k, v in form_data.items():
148 for k, v in form_data.items():
149 if k == 'user':
149 if k == 'user':
150 cur_repo.user = User.by_username(v)
150 cur_repo.user = User.by_username(v)
151 elif k == 'repo_name':
151 elif k == 'repo_name':
152 cur_repo.repo_name = form_data['repo_name_full']
152 cur_repo.repo_name = form_data['repo_name_full']
153 elif k == 'repo_group':
153 elif k == 'repo_group':
154 cur_repo.group_id = v
154 cur_repo.group_id = v
155
155
156 else:
156 else:
157 setattr(cur_repo, k, v)
157 setattr(cur_repo, k, v)
158
158
159 self.sa.add(cur_repo)
159 self.sa.add(cur_repo)
160
160
161 if repo_name != form_data['repo_name_full']:
161 if repo_name != form_data['repo_name_full']:
162 # rename repository
162 # rename repository
163 self.__rename_repo(old=repo_name,
163 self.__rename_repo(old=repo_name,
164 new=form_data['repo_name_full'])
164 new=form_data['repo_name_full'])
165
165
166 self.sa.commit()
166 self.sa.commit()
167 except:
167 except:
168 log.error(traceback.format_exc())
168 log.error(traceback.format_exc())
169 self.sa.rollback()
169 self.sa.rollback()
170 raise
170 raise
171
171
172 def create(self, form_data, cur_user, just_db=False, fork=False):
172 def create(self, form_data, cur_user, just_db=False, fork=False):
173
173
174 try:
174 try:
175 if fork:
175 if fork:
176 repo_name = form_data['fork_name']
176 repo_name = form_data['fork_name']
177 org_name = form_data['repo_name']
177 org_name = form_data['repo_name']
178 org_full_name = org_name
178 org_full_name = org_name
179
179
180 else:
180 else:
181 org_name = repo_name = form_data['repo_name']
181 org_name = repo_name = form_data['repo_name']
182 repo_name_full = form_data['repo_name_full']
182 repo_name_full = form_data['repo_name_full']
183
183
184 new_repo = Repository()
184 new_repo = Repository()
185 new_repo.enable_statistics = False
185 new_repo.enable_statistics = False
186 for k, v in form_data.items():
186 for k, v in form_data.items():
187 if k == 'repo_name':
187 if k == 'repo_name':
188 if fork:
188 if fork:
189 v = repo_name
189 v = repo_name
190 else:
190 else:
191 v = repo_name_full
191 v = repo_name_full
192 if k == 'repo_group':
192 if k == 'repo_group':
193 k = 'group_id'
193 k = 'group_id'
194
194
195 setattr(new_repo, k, v)
195 setattr(new_repo, k, v)
196
196
197 if fork:
197 if fork:
198 parent_repo = self.sa.query(Repository)\
198 parent_repo = self.sa.query(Repository)\
199 .filter(Repository.repo_name == org_full_name).one()
199 .filter(Repository.repo_name == org_full_name).one()
200 new_repo.fork = parent_repo
200 new_repo.fork = parent_repo
201
201
202 new_repo.user_id = cur_user.user_id
202 new_repo.user_id = cur_user.user_id
203 self.sa.add(new_repo)
203 self.sa.add(new_repo)
204
204
205 #create default permission
205 #create default permission
206 repo_to_perm = RepoToPerm()
206 repo_to_perm = RepoToPerm()
207 default = 'repository.read'
207 default = 'repository.read'
208 for p in UserModel(self.sa).get_by_username('default',
208 for p in UserModel(self.sa).get_by_username('default',
209 cache=False).user_perms:
209 cache=False).user_perms:
210 if p.permission.permission_name.startswith('repository.'):
210 if p.permission.permission_name.startswith('repository.'):
211 default = p.permission.permission_name
211 default = p.permission.permission_name
212 break
212 break
213
213
214 default_perm = 'repository.none' if form_data['private'] else default
214 default_perm = 'repository.none' if form_data['private'] else default
215
215
216 repo_to_perm.permission_id = self.sa.query(Permission)\
216 repo_to_perm.permission_id = self.sa.query(Permission)\
217 .filter(Permission.permission_name == default_perm)\
217 .filter(Permission.permission_name == default_perm)\
218 .one().permission_id
218 .one().permission_id
219
219
220 repo_to_perm.repository = new_repo
220 repo_to_perm.repository = new_repo
221 repo_to_perm.user_id = UserModel(self.sa)\
221 repo_to_perm.user_id = UserModel(self.sa)\
222 .get_by_username('default', cache=False).user_id
222 .get_by_username('default', cache=False).user_id
223
223
224 self.sa.add(repo_to_perm)
224 self.sa.add(repo_to_perm)
225
225
226 if not just_db:
226 if not just_db:
227 self.__create_repo(repo_name, form_data['repo_type'],
227 self.__create_repo(repo_name, form_data['repo_type'],
228 form_data['repo_group'],
228 form_data['repo_group'],
229 form_data['clone_uri'])
229 form_data['clone_uri'])
230
230
231 self.sa.commit()
231 self.sa.commit()
232
232
233 #now automatically start following this repository as owner
233 #now automatically start following this repository as owner
234 from rhodecode.model.scm import ScmModel
234 from rhodecode.model.scm import ScmModel
235 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
235 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
236 cur_user.user_id)
236 cur_user.user_id)
237
237
238 except:
238 except:
239 log.error(traceback.format_exc())
239 log.error(traceback.format_exc())
240 self.sa.rollback()
240 self.sa.rollback()
241 raise
241 raise
242
242
243 def create_fork(self, form_data, cur_user):
243 def create_fork(self, form_data, cur_user):
244 from rhodecode.lib.celerylib import tasks, run_task
244 from rhodecode.lib.celerylib import tasks, run_task
245 run_task(tasks.create_repo_fork, form_data, cur_user)
245 run_task(tasks.create_repo_fork, form_data, cur_user)
246
246
247 def delete(self, repo):
247 def delete(self, repo):
248 try:
248 try:
249 self.sa.delete(repo)
249 self.sa.delete(repo)
250 self.__delete_repo(repo)
250 self.__delete_repo(repo)
251 self.sa.commit()
251 self.sa.commit()
252 except:
252 except:
253 log.error(traceback.format_exc())
253 log.error(traceback.format_exc())
254 self.sa.rollback()
254 self.sa.rollback()
255 raise
255 raise
256
256
257 def delete_perm_user(self, form_data, repo_name):
257 def delete_perm_user(self, form_data, repo_name):
258 try:
258 try:
259 self.sa.query(RepoToPerm)\
259 self.sa.query(RepoToPerm)\
260 .filter(RepoToPerm.repository \
260 .filter(RepoToPerm.repository \
261 == self.get_by_repo_name(repo_name))\
261 == self.get_by_repo_name(repo_name))\
262 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
262 .filter(RepoToPerm.user_id == form_data['user_id']).delete()
263 self.sa.commit()
263 self.sa.commit()
264 except:
264 except:
265 log.error(traceback.format_exc())
265 log.error(traceback.format_exc())
266 self.sa.rollback()
266 self.sa.rollback()
267 raise
267 raise
268
268
269 def delete_perm_users_group(self, form_data, repo_name):
269 def delete_perm_users_group(self, form_data, repo_name):
270 try:
270 try:
271 self.sa.query(UsersGroupRepoToPerm)\
271 self.sa.query(UsersGroupRepoToPerm)\
272 .filter(UsersGroupRepoToPerm.repository \
272 .filter(UsersGroupRepoToPerm.repository \
273 == self.get_by_repo_name(repo_name))\
273 == self.get_by_repo_name(repo_name))\
274 .filter(UsersGroupRepoToPerm.users_group_id \
274 .filter(UsersGroupRepoToPerm.users_group_id \
275 == form_data['users_group_id']).delete()
275 == form_data['users_group_id']).delete()
276 self.sa.commit()
276 self.sa.commit()
277 except:
277 except:
278 log.error(traceback.format_exc())
278 log.error(traceback.format_exc())
279 self.sa.rollback()
279 self.sa.rollback()
280 raise
280 raise
281
281
282 def delete_stats(self, repo_name):
282 def delete_stats(self, repo_name):
283 try:
283 try:
284 self.sa.query(Statistics)\
284 self.sa.query(Statistics)\
285 .filter(Statistics.repository == \
285 .filter(Statistics.repository == \
286 self.get_by_repo_name(repo_name)).delete()
286 self.get_by_repo_name(repo_name)).delete()
287 self.sa.commit()
287 self.sa.commit()
288 except:
288 except:
289 log.error(traceback.format_exc())
289 log.error(traceback.format_exc())
290 self.sa.rollback()
290 self.sa.rollback()
291 raise
291 raise
292
292
293 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
293 def __create_repo(self, repo_name, alias, new_parent_id, clone_uri=False):
294 """
294 """
295 makes repository on filesystem. It's group aware means it'll create
295 makes repository on filesystem. It's group aware means it'll create
296 a repository within a group, and alter the paths accordingly of
296 a repository within a group, and alter the paths accordingly of
297 group location
297 group location
298
298
299 :param repo_name:
299 :param repo_name:
300 :param alias:
300 :param alias:
301 :param parent_id:
301 :param parent_id:
302 :param clone_uri:
302 :param clone_uri:
303 """
303 """
304 from rhodecode.lib.utils import check_repo_fast
304 from rhodecode.lib.utils import is_valid_repo
305
305
306 if new_parent_id:
306 if new_parent_id:
307 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
307 paths = Group.get(new_parent_id).full_path.split(Group.url_sep())
308 new_parent_path = os.sep.join(paths)
308 new_parent_path = os.sep.join(paths)
309 else:
309 else:
310 new_parent_path = ''
310 new_parent_path = ''
311
311
312 repo_path = os.path.join(*map(lambda x:safe_str(x),
312 repo_path = os.path.join(*map(lambda x:safe_str(x),
313 [self.repos_path, new_parent_path, repo_name]))
313 [self.repos_path, new_parent_path, repo_name]))
314
314
315 if check_repo_fast(repo_path, self.repos_path) is False:
315 if is_valid_repo(repo_path, self.repos_path) is False:
316 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
316 log.info('creating repo %s in %s @ %s', repo_name, repo_path,
317 clone_uri)
317 clone_uri)
318 backend = get_backend(alias)
318 backend = get_backend(alias)
319
319
320 backend(repo_path, create=True, src_url=clone_uri)
320 backend(repo_path, create=True, src_url=clone_uri)
321
321
322
322
323 def __rename_repo(self, old, new):
323 def __rename_repo(self, old, new):
324 """
324 """
325 renames repository on filesystem
325 renames repository on filesystem
326
326
327 :param old: old name
327 :param old: old name
328 :param new: new name
328 :param new: new name
329 """
329 """
330 log.info('renaming repo from %s to %s', old, new)
330 log.info('renaming repo from %s to %s', old, new)
331
331
332 old_path = os.path.join(self.repos_path, old)
332 old_path = os.path.join(self.repos_path, old)
333 new_path = os.path.join(self.repos_path, new)
333 new_path = os.path.join(self.repos_path, new)
334 if os.path.isdir(new_path):
334 if os.path.isdir(new_path):
335 raise Exception('Was trying to rename to already existing dir %s' \
335 raise Exception('Was trying to rename to already existing dir %s' \
336 % new_path)
336 % new_path)
337 shutil.move(old_path, new_path)
337 shutil.move(old_path, new_path)
338
338
339 def __delete_repo(self, repo):
339 def __delete_repo(self, repo):
340 """
340 """
341 removes repo from filesystem, the removal is acctually made by
341 removes repo from filesystem, the removal is acctually made by
342 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
342 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
343 repository is no longer valid for rhodecode, can be undeleted later on
343 repository is no longer valid for rhodecode, can be undeleted later on
344 by reverting the renames on this repository
344 by reverting the renames on this repository
345
345
346 :param repo: repo object
346 :param repo: repo object
347 """
347 """
348 rm_path = os.path.join(self.repos_path, repo.repo_name)
348 rm_path = os.path.join(self.repos_path, repo.repo_name)
349 log.info("Removing %s", rm_path)
349 log.info("Removing %s", rm_path)
350 #disable hg/git
350 #disable hg/git
351 alias = repo.repo_type
351 alias = repo.repo_type
352 shutil.move(os.path.join(rm_path, '.%s' % alias),
352 shutil.move(os.path.join(rm_path, '.%s' % alias),
353 os.path.join(rm_path, 'rm__.%s' % alias))
353 os.path.join(rm_path, 'rm__.%s' % alias))
354 #disable repo
354 #disable repo
355 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
355 shutil.move(rm_path, os.path.join(self.repos_path, 'rm__%s__%s' \
356 % (datetime.today()\
356 % (datetime.today()\
357 .strftime('%Y%m%d_%H%M%S_%f'),
357 .strftime('%Y%m%d_%H%M%S_%f'),
358 repo.repo_name)))
358 repo.repo_name)))
General Comments 0
You need to be logged in to leave comments. Login now