##// END OF EJS Templates
show admin menu and list for users who are admins of repos....
marcink -
r3865:100be698 beta
parent child Browse files
Show More
@@ -1,579 +1,578 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.controllers.admin.repos
3 rhodecode.controllers.admin.repos
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
5
5
6 Repositories controller for RhodeCode
6 Repositories controller for RhodeCode
7
7
8 :created_on: Apr 7, 2010
8 :created_on: Apr 7, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import formencode
28 import formencode
29 from formencode import htmlfill
29 from formencode import htmlfill
30
30
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
31 from webob.exc import HTTPInternalServerError, HTTPForbidden
32 from pylons import request, session, tmpl_context as c, url
32 from pylons import request, session, tmpl_context as c, url
33 from pylons.controllers.util import redirect
33 from pylons.controllers.util import redirect
34 from pylons.i18n.translation import _
34 from pylons.i18n.translation import _
35 from sqlalchemy.exc import IntegrityError
35 from sqlalchemy.exc import IntegrityError
36
36
37 import rhodecode
37 import rhodecode
38 from rhodecode.lib import helpers as h
38 from rhodecode.lib import helpers as h
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
39 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator, \
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
40 HasPermissionAnyDecorator, HasRepoPermissionAllDecorator, NotAnonymous,\
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
41 HasPermissionAny, HasReposGroupPermissionAny, HasRepoPermissionAnyDecorator
42 from rhodecode.lib.base import BaseRepoController, render
42 from rhodecode.lib.base import BaseRepoController, render
43 from rhodecode.lib.utils import action_logger, repo_name_slug
43 from rhodecode.lib.utils import action_logger, repo_name_slug
44 from rhodecode.lib.helpers import get_token
44 from rhodecode.lib.helpers import get_token
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
46 from rhodecode.model.db import User, Repository, UserFollowing, RepoGroup,\
47 RhodeCodeSetting, RepositoryField
47 RhodeCodeSetting, RepositoryField
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
48 from rhodecode.model.forms import RepoForm, RepoFieldForm, RepoPermsForm
49 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
49 from rhodecode.model.scm import ScmModel, RepoGroupList, RepoList
50 from rhodecode.model.repo import RepoModel
50 from rhodecode.model.repo import RepoModel
51 from rhodecode.lib.compat import json
51 from rhodecode.lib.compat import json
52 from sqlalchemy.sql.expression import func
52 from sqlalchemy.sql.expression import func
53 from rhodecode.lib.exceptions import AttachedForksError
53 from rhodecode.lib.exceptions import AttachedForksError
54 from rhodecode.lib.utils2 import safe_int
54 from rhodecode.lib.utils2 import safe_int
55
55
56 log = logging.getLogger(__name__)
56 log = logging.getLogger(__name__)
57
57
58
58
59 class ReposController(BaseRepoController):
59 class ReposController(BaseRepoController):
60 """
60 """
61 REST Controller styled on the Atom Publishing Protocol"""
61 REST Controller styled on the Atom Publishing Protocol"""
62 # To properly map this controller, ensure your config/routing.py
62 # To properly map this controller, ensure your config/routing.py
63 # file has a resource setup:
63 # file has a resource setup:
64 # map.resource('repo', 'repos')
64 # map.resource('repo', 'repos')
65
65
66 @LoginRequired()
66 @LoginRequired()
67 def __before__(self):
67 def __before__(self):
68 super(ReposController, self).__before__()
68 super(ReposController, self).__before__()
69
69
70 def __load_defaults(self):
70 def __load_defaults(self):
71 acl_groups = RepoGroupList(RepoGroup.query().all(),
71 acl_groups = RepoGroupList(RepoGroup.query().all(),
72 perm_set=['group.write', 'group.admin'])
72 perm_set=['group.write', 'group.admin'])
73 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
73 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
74 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
74 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
75
75
76 repo_model = RepoModel()
76 repo_model = RepoModel()
77 c.users_array = repo_model.get_users_js()
77 c.users_array = repo_model.get_users_js()
78 c.users_groups_array = repo_model.get_users_groups_js()
78 c.users_groups_array = repo_model.get_users_groups_js()
79 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
79 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
80 c.landing_revs_choices = choices
80 c.landing_revs_choices = choices
81
81
82 def __load_data(self, repo_name=None):
82 def __load_data(self, repo_name=None):
83 """
83 """
84 Load defaults settings for edit, and update
84 Load defaults settings for edit, and update
85
85
86 :param repo_name:
86 :param repo_name:
87 """
87 """
88 self.__load_defaults()
88 self.__load_defaults()
89
89
90 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
90 c.repo_info = db_repo = Repository.get_by_repo_name(repo_name)
91 repo = db_repo.scm_instance
91 repo = db_repo.scm_instance
92
92
93 if c.repo_info is None:
93 if c.repo_info is None:
94 h.not_mapped_error(repo_name)
94 h.not_mapped_error(repo_name)
95 return redirect(url('repos'))
95 return redirect(url('repos'))
96
96
97 ##override defaults for exact repo info here git/hg etc
97 ##override defaults for exact repo info here git/hg etc
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
98 choices, c.landing_revs = ScmModel().get_repo_landing_revs(c.repo_info)
99 c.landing_revs_choices = choices
99 c.landing_revs_choices = choices
100
100
101 c.default_user_id = User.get_default_user().user_id
101 c.default_user_id = User.get_default_user().user_id
102 c.in_public_journal = UserFollowing.query()\
102 c.in_public_journal = UserFollowing.query()\
103 .filter(UserFollowing.user_id == c.default_user_id)\
103 .filter(UserFollowing.user_id == c.default_user_id)\
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
104 .filter(UserFollowing.follows_repository == c.repo_info).scalar()
105
105
106 if c.repo_info.stats:
106 if c.repo_info.stats:
107 # this is on what revision we ended up so we add +1 for count
107 # this is on what revision we ended up so we add +1 for count
108 last_rev = c.repo_info.stats.stat_on_revision + 1
108 last_rev = c.repo_info.stats.stat_on_revision + 1
109 else:
109 else:
110 last_rev = 0
110 last_rev = 0
111 c.stats_revision = last_rev
111 c.stats_revision = last_rev
112
112
113 c.repo_last_rev = repo.count() if repo.revisions else 0
113 c.repo_last_rev = repo.count() if repo.revisions else 0
114
114
115 if last_rev == 0 or c.repo_last_rev == 0:
115 if last_rev == 0 or c.repo_last_rev == 0:
116 c.stats_percentage = 0
116 c.stats_percentage = 0
117 else:
117 else:
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
118 c.stats_percentage = '%.2f' % ((float((last_rev)) /
119 c.repo_last_rev) * 100)
119 c.repo_last_rev) * 100)
120
120
121 c.repo_fields = RepositoryField.query()\
121 c.repo_fields = RepositoryField.query()\
122 .filter(RepositoryField.repository == db_repo).all()
122 .filter(RepositoryField.repository == db_repo).all()
123
123
124 defaults = RepoModel()._get_defaults(repo_name)
124 defaults = RepoModel()._get_defaults(repo_name)
125
125
126 _repos = Repository.query().order_by(Repository.repo_name).all()
126 _repos = Repository.query().order_by(Repository.repo_name).all()
127 read_access_repos = RepoList(_repos)
127 read_access_repos = RepoList(_repos)
128 c.repos_list = [('', _('--REMOVE FORK--'))]
128 c.repos_list = [('', _('--REMOVE FORK--'))]
129 c.repos_list += [(x.repo_id, x.repo_name)
129 c.repos_list += [(x.repo_id, x.repo_name)
130 for x in read_access_repos
130 for x in read_access_repos
131 if x.repo_id != c.repo_info.repo_id]
131 if x.repo_id != c.repo_info.repo_id]
132
132
133 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
133 defaults['id_fork_of'] = db_repo.fork.repo_id if db_repo.fork else ''
134 return defaults
134 return defaults
135
135
136 @HasPermissionAllDecorator('hg.admin')
137 def index(self, format='html'):
136 def index(self, format='html'):
138 """GET /repos: All items in the collection"""
137 """GET /repos: All items in the collection"""
139 # url('repos')
138 # url('repos')
139 repo_list = Repository.query()\
140 .order_by(func.lower(Repository.repo_name))\
141 .all()
140
142
141 c.repos_list = Repository.query()\
143 c.repos_list = RepoList(repo_list, perm_set=['repository.admin'])
142 .order_by(func.lower(Repository.repo_name))\
143 .all()
144
145 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
144 repos_data = RepoModel().get_repos_as_dict(repos_list=c.repos_list,
146 admin=True,
145 admin=True,
147 super_user_actions=True)
146 super_user_actions=True)
148 #json used to render the grid
147 #json used to render the grid
149 c.data = json.dumps(repos_data)
148 c.data = json.dumps(repos_data)
150
149
151 return render('admin/repos/repos.html')
150 return render('admin/repos/repos.html')
152
151
153 @NotAnonymous()
152 @NotAnonymous()
154 def create(self):
153 def create(self):
155 """
154 """
156 POST /repos: Create a new item"""
155 POST /repos: Create a new item"""
157 # url('repos')
156 # url('repos')
158
157
159 self.__load_defaults()
158 self.__load_defaults()
160 form_result = {}
159 form_result = {}
161 try:
160 try:
162 form_result = RepoForm(repo_groups=c.repo_groups_choices,
161 form_result = RepoForm(repo_groups=c.repo_groups_choices,
163 landing_revs=c.landing_revs_choices)()\
162 landing_revs=c.landing_revs_choices)()\
164 .to_python(dict(request.POST))
163 .to_python(dict(request.POST))
165
164
166 new_repo = RepoModel().create(form_result,
165 new_repo = RepoModel().create(form_result,
167 self.rhodecode_user.user_id)
166 self.rhodecode_user.user_id)
168 if form_result['clone_uri']:
167 if form_result['clone_uri']:
169 h.flash(_('Created repository %s from %s') \
168 h.flash(_('Created repository %s from %s') \
170 % (form_result['repo_name'], form_result['clone_uri']),
169 % (form_result['repo_name'], form_result['clone_uri']),
171 category='success')
170 category='success')
172 else:
171 else:
173 repo_url = h.link_to(form_result['repo_name'],
172 repo_url = h.link_to(form_result['repo_name'],
174 h.url('summary_home', repo_name=form_result['repo_name_full']))
173 h.url('summary_home', repo_name=form_result['repo_name_full']))
175 h.flash(h.literal(_('Created repository %s') % repo_url),
174 h.flash(h.literal(_('Created repository %s') % repo_url),
176 category='success')
175 category='success')
177
176
178 if request.POST.get('user_created'):
177 if request.POST.get('user_created'):
179 # created by regular non admin user
178 # created by regular non admin user
180 action_logger(self.rhodecode_user, 'user_created_repo',
179 action_logger(self.rhodecode_user, 'user_created_repo',
181 form_result['repo_name_full'], self.ip_addr,
180 form_result['repo_name_full'], self.ip_addr,
182 self.sa)
181 self.sa)
183 else:
182 else:
184 action_logger(self.rhodecode_user, 'admin_created_repo',
183 action_logger(self.rhodecode_user, 'admin_created_repo',
185 form_result['repo_name_full'], self.ip_addr,
184 form_result['repo_name_full'], self.ip_addr,
186 self.sa)
185 self.sa)
187 Session().commit()
186 Session().commit()
188 except formencode.Invalid, errors:
187 except formencode.Invalid, errors:
189 return htmlfill.render(
188 return htmlfill.render(
190 render('admin/repos/repo_add.html'),
189 render('admin/repos/repo_add.html'),
191 defaults=errors.value,
190 defaults=errors.value,
192 errors=errors.error_dict or {},
191 errors=errors.error_dict or {},
193 prefix_error=False,
192 prefix_error=False,
194 encoding="UTF-8")
193 encoding="UTF-8")
195
194
196 except Exception:
195 except Exception:
197 log.error(traceback.format_exc())
196 log.error(traceback.format_exc())
198 msg = _('Error creating repository %s') \
197 msg = _('Error creating repository %s') \
199 % form_result.get('repo_name')
198 % form_result.get('repo_name')
200 h.flash(msg, category='error')
199 h.flash(msg, category='error')
201 if c.rhodecode_user.is_admin:
200 if c.rhodecode_user.is_admin:
202 return redirect(url('repos'))
201 return redirect(url('repos'))
203 return redirect(url('home'))
202 return redirect(url('home'))
204 #redirect to our new repo !
203 #redirect to our new repo !
205 return redirect(url('summary_home', repo_name=new_repo.repo_name))
204 return redirect(url('summary_home', repo_name=new_repo.repo_name))
206
205
207 @NotAnonymous()
206 @NotAnonymous()
208 def create_repository(self):
207 def create_repository(self):
209 """GET /_admin/create_repository: Form to create a new item"""
208 """GET /_admin/create_repository: Form to create a new item"""
210 new_repo = request.GET.get('repo', '')
209 new_repo = request.GET.get('repo', '')
211 parent_group = request.GET.get('parent_group')
210 parent_group = request.GET.get('parent_group')
212 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
211 if not HasPermissionAny('hg.admin', 'hg.create.repository')():
213 #you're not super admin nor have global create permissions,
212 #you're not super admin nor have global create permissions,
214 #but maybe you have at least write permission to a parent group ?
213 #but maybe you have at least write permission to a parent group ?
215 _gr = RepoGroup.get(parent_group)
214 _gr = RepoGroup.get(parent_group)
216 gr_name = _gr.group_name if _gr else None
215 gr_name = _gr.group_name if _gr else None
217 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
216 if not HasReposGroupPermissionAny('group.admin', 'group.write')(group_name=gr_name):
218 raise HTTPForbidden
217 raise HTTPForbidden
219
218
220 acl_groups = RepoGroupList(RepoGroup.query().all(),
219 acl_groups = RepoGroupList(RepoGroup.query().all(),
221 perm_set=['group.write', 'group.admin'])
220 perm_set=['group.write', 'group.admin'])
222 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
221 c.repo_groups = RepoGroup.groups_choices(groups=acl_groups)
223 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
222 c.repo_groups_choices = map(lambda k: unicode(k[0]), c.repo_groups)
224 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
223 choices, c.landing_revs = ScmModel().get_repo_landing_revs()
225
224
226 c.new_repo = repo_name_slug(new_repo)
225 c.new_repo = repo_name_slug(new_repo)
227
226
228 ## apply the defaults from defaults page
227 ## apply the defaults from defaults page
229 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
228 defaults = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
230 if parent_group:
229 if parent_group:
231 defaults.update({'repo_group': parent_group})
230 defaults.update({'repo_group': parent_group})
232
231
233 return htmlfill.render(
232 return htmlfill.render(
234 render('admin/repos/repo_add.html'),
233 render('admin/repos/repo_add.html'),
235 defaults=defaults,
234 defaults=defaults,
236 errors={},
235 errors={},
237 prefix_error=False,
236 prefix_error=False,
238 encoding="UTF-8"
237 encoding="UTF-8"
239 )
238 )
240
239
241 @HasRepoPermissionAllDecorator('repository.admin')
240 @HasRepoPermissionAllDecorator('repository.admin')
242 def update(self, repo_name):
241 def update(self, repo_name):
243 """
242 """
244 PUT /repos/repo_name: Update an existing item"""
243 PUT /repos/repo_name: Update an existing item"""
245 # Forms posted to this method should contain a hidden field:
244 # Forms posted to this method should contain a hidden field:
246 # <input type="hidden" name="_method" value="PUT" />
245 # <input type="hidden" name="_method" value="PUT" />
247 # Or using helpers:
246 # Or using helpers:
248 # h.form(url('repo', repo_name=ID),
247 # h.form(url('repo', repo_name=ID),
249 # method='put')
248 # method='put')
250 # url('repo', repo_name=ID)
249 # url('repo', repo_name=ID)
251 self.__load_defaults()
250 self.__load_defaults()
252 repo_model = RepoModel()
251 repo_model = RepoModel()
253 changed_name = repo_name
252 changed_name = repo_name
254 #override the choices with extracted revisions !
253 #override the choices with extracted revisions !
255 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
254 choices, c.landing_revs = ScmModel().get_repo_landing_revs(repo_name)
256 c.landing_revs_choices = choices
255 c.landing_revs_choices = choices
257 repo = Repository.get_by_repo_name(repo_name)
256 repo = Repository.get_by_repo_name(repo_name)
258 old_data = {
257 old_data = {
259 'repo_name': repo_name,
258 'repo_name': repo_name,
260 'repo_group': repo.group.get_dict() if repo.group else {},
259 'repo_group': repo.group.get_dict() if repo.group else {},
261 'repo_type': repo.repo_type,
260 'repo_type': repo.repo_type,
262 }
261 }
263 _form = RepoForm(edit=True, old_data=old_data,
262 _form = RepoForm(edit=True, old_data=old_data,
264 repo_groups=c.repo_groups_choices,
263 repo_groups=c.repo_groups_choices,
265 landing_revs=c.landing_revs_choices)()
264 landing_revs=c.landing_revs_choices)()
266
265
267 try:
266 try:
268 form_result = _form.to_python(dict(request.POST))
267 form_result = _form.to_python(dict(request.POST))
269 repo = repo_model.update(repo_name, **form_result)
268 repo = repo_model.update(repo_name, **form_result)
270 ScmModel().mark_for_invalidation(repo_name)
269 ScmModel().mark_for_invalidation(repo_name)
271 h.flash(_('Repository %s updated successfully') % repo_name,
270 h.flash(_('Repository %s updated successfully') % repo_name,
272 category='success')
271 category='success')
273 changed_name = repo.repo_name
272 changed_name = repo.repo_name
274 action_logger(self.rhodecode_user, 'admin_updated_repo',
273 action_logger(self.rhodecode_user, 'admin_updated_repo',
275 changed_name, self.ip_addr, self.sa)
274 changed_name, self.ip_addr, self.sa)
276 Session().commit()
275 Session().commit()
277 except formencode.Invalid, errors:
276 except formencode.Invalid, errors:
278 defaults = self.__load_data(repo_name)
277 defaults = self.__load_data(repo_name)
279 defaults.update(errors.value)
278 defaults.update(errors.value)
280 return htmlfill.render(
279 return htmlfill.render(
281 render('admin/repos/repo_edit.html'),
280 render('admin/repos/repo_edit.html'),
282 defaults=defaults,
281 defaults=defaults,
283 errors=errors.error_dict or {},
282 errors=errors.error_dict or {},
284 prefix_error=False,
283 prefix_error=False,
285 encoding="UTF-8")
284 encoding="UTF-8")
286
285
287 except Exception:
286 except Exception:
288 log.error(traceback.format_exc())
287 log.error(traceback.format_exc())
289 h.flash(_('Error occurred during update of repository %s') \
288 h.flash(_('Error occurred during update of repository %s') \
290 % repo_name, category='error')
289 % repo_name, category='error')
291 return redirect(url('edit_repo', repo_name=changed_name))
290 return redirect(url('edit_repo', repo_name=changed_name))
292
291
293 @HasRepoPermissionAllDecorator('repository.admin')
292 @HasRepoPermissionAllDecorator('repository.admin')
294 def delete(self, repo_name):
293 def delete(self, repo_name):
295 """
294 """
296 DELETE /repos/repo_name: Delete an existing item"""
295 DELETE /repos/repo_name: Delete an existing item"""
297 # Forms posted to this method should contain a hidden field:
296 # Forms posted to this method should contain a hidden field:
298 # <input type="hidden" name="_method" value="DELETE" />
297 # <input type="hidden" name="_method" value="DELETE" />
299 # Or using helpers:
298 # Or using helpers:
300 # h.form(url('repo', repo_name=ID),
299 # h.form(url('repo', repo_name=ID),
301 # method='delete')
300 # method='delete')
302 # url('repo', repo_name=ID)
301 # url('repo', repo_name=ID)
303
302
304 repo_model = RepoModel()
303 repo_model = RepoModel()
305 repo = repo_model.get_by_repo_name(repo_name)
304 repo = repo_model.get_by_repo_name(repo_name)
306 if not repo:
305 if not repo:
307 h.not_mapped_error(repo_name)
306 h.not_mapped_error(repo_name)
308 return redirect(url('repos'))
307 return redirect(url('repos'))
309 try:
308 try:
310 _forks = repo.forks.count()
309 _forks = repo.forks.count()
311 handle_forks = None
310 handle_forks = None
312 if _forks and request.POST.get('forks'):
311 if _forks and request.POST.get('forks'):
313 do = request.POST['forks']
312 do = request.POST['forks']
314 if do == 'detach_forks':
313 if do == 'detach_forks':
315 handle_forks = 'detach'
314 handle_forks = 'detach'
316 h.flash(_('Detached %s forks') % _forks, category='success')
315 h.flash(_('Detached %s forks') % _forks, category='success')
317 elif do == 'delete_forks':
316 elif do == 'delete_forks':
318 handle_forks = 'delete'
317 handle_forks = 'delete'
319 h.flash(_('Deleted %s forks') % _forks, category='success')
318 h.flash(_('Deleted %s forks') % _forks, category='success')
320 repo_model.delete(repo, forks=handle_forks)
319 repo_model.delete(repo, forks=handle_forks)
321 action_logger(self.rhodecode_user, 'admin_deleted_repo',
320 action_logger(self.rhodecode_user, 'admin_deleted_repo',
322 repo_name, self.ip_addr, self.sa)
321 repo_name, self.ip_addr, self.sa)
323 ScmModel().mark_for_invalidation(repo_name)
322 ScmModel().mark_for_invalidation(repo_name)
324 h.flash(_('Deleted repository %s') % repo_name, category='success')
323 h.flash(_('Deleted repository %s') % repo_name, category='success')
325 Session().commit()
324 Session().commit()
326 except AttachedForksError:
325 except AttachedForksError:
327 h.flash(_('Cannot delete %s it still contains attached forks')
326 h.flash(_('Cannot delete %s it still contains attached forks')
328 % repo_name, category='warning')
327 % repo_name, category='warning')
329
328
330 except Exception:
329 except Exception:
331 log.error(traceback.format_exc())
330 log.error(traceback.format_exc())
332 h.flash(_('An error occurred during deletion of %s') % repo_name,
331 h.flash(_('An error occurred during deletion of %s') % repo_name,
333 category='error')
332 category='error')
334
333
335 return redirect(url('repos'))
334 return redirect(url('repos'))
336
335
337 @HasRepoPermissionAllDecorator('repository.admin')
336 @HasRepoPermissionAllDecorator('repository.admin')
338 def set_repo_perm_member(self, repo_name):
337 def set_repo_perm_member(self, repo_name):
339 form = RepoPermsForm()().to_python(request.POST)
338 form = RepoPermsForm()().to_python(request.POST)
340 RepoModel()._update_permissions(repo_name, form['perms_new'],
339 RepoModel()._update_permissions(repo_name, form['perms_new'],
341 form['perms_updates'])
340 form['perms_updates'])
342 #TODO: implement this
341 #TODO: implement this
343 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
342 #action_logger(self.rhodecode_user, 'admin_changed_repo_permissions',
344 # repo_name, self.ip_addr, self.sa)
343 # repo_name, self.ip_addr, self.sa)
345 Session().commit()
344 Session().commit()
346 h.flash(_('Repository permissions updated'), category='success')
345 h.flash(_('Repository permissions updated'), category='success')
347 return redirect(url('edit_repo', repo_name=repo_name))
346 return redirect(url('edit_repo', repo_name=repo_name))
348
347
349 @HasRepoPermissionAllDecorator('repository.admin')
348 @HasRepoPermissionAllDecorator('repository.admin')
350 def delete_repo_perm_member(self, repo_name):
349 def delete_repo_perm_member(self, repo_name):
351 """
350 """
352 DELETE an existing repository permission user
351 DELETE an existing repository permission user
353
352
354 :param repo_name:
353 :param repo_name:
355 """
354 """
356 try:
355 try:
357 obj_type = request.POST.get('obj_type')
356 obj_type = request.POST.get('obj_type')
358 obj_id = None
357 obj_id = None
359 if obj_type == 'user':
358 if obj_type == 'user':
360 obj_id = safe_int(request.POST.get('user_id'))
359 obj_id = safe_int(request.POST.get('user_id'))
361 elif obj_type == 'user_group':
360 elif obj_type == 'user_group':
362 obj_id = safe_int(request.POST.get('user_group_id'))
361 obj_id = safe_int(request.POST.get('user_group_id'))
363
362
364 if obj_type == 'user':
363 if obj_type == 'user':
365 RepoModel().revoke_user_permission(repo=repo_name, user=obj_id)
364 RepoModel().revoke_user_permission(repo=repo_name, user=obj_id)
366 elif obj_type == 'user_group':
365 elif obj_type == 'user_group':
367 RepoModel().revoke_users_group_permission(
366 RepoModel().revoke_users_group_permission(
368 repo=repo_name, group_name=obj_id
367 repo=repo_name, group_name=obj_id
369 )
368 )
370 #TODO: implement this
369 #TODO: implement this
371 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
370 #action_logger(self.rhodecode_user, 'admin_revoked_repo_permissions',
372 # repo_name, self.ip_addr, self.sa)
371 # repo_name, self.ip_addr, self.sa)
373 Session().commit()
372 Session().commit()
374 except Exception:
373 except Exception:
375 log.error(traceback.format_exc())
374 log.error(traceback.format_exc())
376 h.flash(_('An error occurred during revoking of permission'),
375 h.flash(_('An error occurred during revoking of permission'),
377 category='error')
376 category='error')
378 raise HTTPInternalServerError()
377 raise HTTPInternalServerError()
379
378
380 @HasRepoPermissionAllDecorator('repository.admin')
379 @HasRepoPermissionAllDecorator('repository.admin')
381 def repo_stats(self, repo_name):
380 def repo_stats(self, repo_name):
382 """
381 """
383 DELETE an existing repository statistics
382 DELETE an existing repository statistics
384
383
385 :param repo_name:
384 :param repo_name:
386 """
385 """
387
386
388 try:
387 try:
389 RepoModel().delete_stats(repo_name)
388 RepoModel().delete_stats(repo_name)
390 Session().commit()
389 Session().commit()
391 except Exception, e:
390 except Exception, e:
392 log.error(traceback.format_exc())
391 log.error(traceback.format_exc())
393 h.flash(_('An error occurred during deletion of repository stats'),
392 h.flash(_('An error occurred during deletion of repository stats'),
394 category='error')
393 category='error')
395 return redirect(url('edit_repo', repo_name=repo_name))
394 return redirect(url('edit_repo', repo_name=repo_name))
396
395
397 @HasRepoPermissionAllDecorator('repository.admin')
396 @HasRepoPermissionAllDecorator('repository.admin')
398 def repo_cache(self, repo_name):
397 def repo_cache(self, repo_name):
399 """
398 """
400 INVALIDATE existing repository cache
399 INVALIDATE existing repository cache
401
400
402 :param repo_name:
401 :param repo_name:
403 """
402 """
404
403
405 try:
404 try:
406 ScmModel().mark_for_invalidation(repo_name)
405 ScmModel().mark_for_invalidation(repo_name)
407 Session().commit()
406 Session().commit()
408 except Exception, e:
407 except Exception, e:
409 log.error(traceback.format_exc())
408 log.error(traceback.format_exc())
410 h.flash(_('An error occurred during cache invalidation'),
409 h.flash(_('An error occurred during cache invalidation'),
411 category='error')
410 category='error')
412 return redirect(url('edit_repo', repo_name=repo_name))
411 return redirect(url('edit_repo', repo_name=repo_name))
413
412
414 @HasRepoPermissionAllDecorator('repository.admin')
413 @HasRepoPermissionAllDecorator('repository.admin')
415 def repo_locking(self, repo_name):
414 def repo_locking(self, repo_name):
416 """
415 """
417 Unlock repository when it is locked !
416 Unlock repository when it is locked !
418
417
419 :param repo_name:
418 :param repo_name:
420 """
419 """
421
420
422 try:
421 try:
423 repo = Repository.get_by_repo_name(repo_name)
422 repo = Repository.get_by_repo_name(repo_name)
424 if request.POST.get('set_lock'):
423 if request.POST.get('set_lock'):
425 Repository.lock(repo, c.rhodecode_user.user_id)
424 Repository.lock(repo, c.rhodecode_user.user_id)
426 elif request.POST.get('set_unlock'):
425 elif request.POST.get('set_unlock'):
427 Repository.unlock(repo)
426 Repository.unlock(repo)
428 except Exception, e:
427 except Exception, e:
429 log.error(traceback.format_exc())
428 log.error(traceback.format_exc())
430 h.flash(_('An error occurred during unlocking'),
429 h.flash(_('An error occurred during unlocking'),
431 category='error')
430 category='error')
432 return redirect(url('edit_repo', repo_name=repo_name))
431 return redirect(url('edit_repo', repo_name=repo_name))
433
432
434 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
433 @HasRepoPermissionAnyDecorator('repository.write', 'repository.admin')
435 def toggle_locking(self, repo_name):
434 def toggle_locking(self, repo_name):
436 """
435 """
437 Toggle locking of repository by simple GET call to url
436 Toggle locking of repository by simple GET call to url
438
437
439 :param repo_name:
438 :param repo_name:
440 """
439 """
441
440
442 try:
441 try:
443 repo = Repository.get_by_repo_name(repo_name)
442 repo = Repository.get_by_repo_name(repo_name)
444
443
445 if repo.enable_locking:
444 if repo.enable_locking:
446 if repo.locked[0]:
445 if repo.locked[0]:
447 Repository.unlock(repo)
446 Repository.unlock(repo)
448 action = _('Unlocked')
447 action = _('Unlocked')
449 else:
448 else:
450 Repository.lock(repo, c.rhodecode_user.user_id)
449 Repository.lock(repo, c.rhodecode_user.user_id)
451 action = _('Locked')
450 action = _('Locked')
452
451
453 h.flash(_('Repository has been %s') % action,
452 h.flash(_('Repository has been %s') % action,
454 category='success')
453 category='success')
455 except Exception, e:
454 except Exception, e:
456 log.error(traceback.format_exc())
455 log.error(traceback.format_exc())
457 h.flash(_('An error occurred during unlocking'),
456 h.flash(_('An error occurred during unlocking'),
458 category='error')
457 category='error')
459 return redirect(url('summary_home', repo_name=repo_name))
458 return redirect(url('summary_home', repo_name=repo_name))
460
459
461 @HasRepoPermissionAllDecorator('repository.admin')
460 @HasRepoPermissionAllDecorator('repository.admin')
462 def repo_public_journal(self, repo_name):
461 def repo_public_journal(self, repo_name):
463 """
462 """
464 Set's this repository to be visible in public journal,
463 Set's this repository to be visible in public journal,
465 in other words assing default user to follow this repo
464 in other words assing default user to follow this repo
466
465
467 :param repo_name:
466 :param repo_name:
468 """
467 """
469
468
470 cur_token = request.POST.get('auth_token')
469 cur_token = request.POST.get('auth_token')
471 token = get_token()
470 token = get_token()
472 if cur_token == token:
471 if cur_token == token:
473 try:
472 try:
474 repo_id = Repository.get_by_repo_name(repo_name).repo_id
473 repo_id = Repository.get_by_repo_name(repo_name).repo_id
475 user_id = User.get_default_user().user_id
474 user_id = User.get_default_user().user_id
476 self.scm_model.toggle_following_repo(repo_id, user_id)
475 self.scm_model.toggle_following_repo(repo_id, user_id)
477 h.flash(_('Updated repository visibility in public journal'),
476 h.flash(_('Updated repository visibility in public journal'),
478 category='success')
477 category='success')
479 Session().commit()
478 Session().commit()
480 except Exception:
479 except Exception:
481 h.flash(_('An error occurred during setting this'
480 h.flash(_('An error occurred during setting this'
482 ' repository in public journal'),
481 ' repository in public journal'),
483 category='error')
482 category='error')
484
483
485 else:
484 else:
486 h.flash(_('Token mismatch'), category='error')
485 h.flash(_('Token mismatch'), category='error')
487 return redirect(url('edit_repo', repo_name=repo_name))
486 return redirect(url('edit_repo', repo_name=repo_name))
488
487
489 @HasRepoPermissionAllDecorator('repository.admin')
488 @HasRepoPermissionAllDecorator('repository.admin')
490 def repo_pull(self, repo_name):
489 def repo_pull(self, repo_name):
491 """
490 """
492 Runs task to update given repository with remote changes,
491 Runs task to update given repository with remote changes,
493 ie. make pull on remote location
492 ie. make pull on remote location
494
493
495 :param repo_name:
494 :param repo_name:
496 """
495 """
497 try:
496 try:
498 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
497 ScmModel().pull_changes(repo_name, self.rhodecode_user.username)
499 h.flash(_('Pulled from remote location'), category='success')
498 h.flash(_('Pulled from remote location'), category='success')
500 except Exception, e:
499 except Exception, e:
501 log.error(traceback.format_exc())
500 log.error(traceback.format_exc())
502 h.flash(_('An error occurred during pull from remote location'),
501 h.flash(_('An error occurred during pull from remote location'),
503 category='error')
502 category='error')
504
503
505 return redirect(url('edit_repo', repo_name=repo_name))
504 return redirect(url('edit_repo', repo_name=repo_name))
506
505
507 @HasRepoPermissionAllDecorator('repository.admin')
506 @HasRepoPermissionAllDecorator('repository.admin')
508 def repo_as_fork(self, repo_name):
507 def repo_as_fork(self, repo_name):
509 """
508 """
510 Mark given repository as a fork of another
509 Mark given repository as a fork of another
511
510
512 :param repo_name:
511 :param repo_name:
513 """
512 """
514 try:
513 try:
515 fork_id = request.POST.get('id_fork_of')
514 fork_id = request.POST.get('id_fork_of')
516 repo = ScmModel().mark_as_fork(repo_name, fork_id,
515 repo = ScmModel().mark_as_fork(repo_name, fork_id,
517 self.rhodecode_user.username)
516 self.rhodecode_user.username)
518 fork = repo.fork.repo_name if repo.fork else _('Nothing')
517 fork = repo.fork.repo_name if repo.fork else _('Nothing')
519 Session().commit()
518 Session().commit()
520 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
519 h.flash(_('Marked repo %s as fork of %s') % (repo_name, fork),
521 category='success')
520 category='success')
522 except Exception, e:
521 except Exception, e:
523 log.error(traceback.format_exc())
522 log.error(traceback.format_exc())
524 h.flash(_('An error occurred during this operation'),
523 h.flash(_('An error occurred during this operation'),
525 category='error')
524 category='error')
526
525
527 return redirect(url('edit_repo', repo_name=repo_name))
526 return redirect(url('edit_repo', repo_name=repo_name))
528
527
529 @HasPermissionAllDecorator('hg.admin')
528 @HasPermissionAllDecorator('hg.admin')
530 def show(self, repo_name, format='html'):
529 def show(self, repo_name, format='html'):
531 """GET /repos/repo_name: Show a specific item"""
530 """GET /repos/repo_name: Show a specific item"""
532 # url('repo', repo_name=ID)
531 # url('repo', repo_name=ID)
533
532
534 @HasRepoPermissionAllDecorator('repository.admin')
533 @HasRepoPermissionAllDecorator('repository.admin')
535 def edit(self, repo_name, format='html'):
534 def edit(self, repo_name, format='html'):
536 """GET /repos/repo_name/edit: Form to edit an existing item"""
535 """GET /repos/repo_name/edit: Form to edit an existing item"""
537 # url('edit_repo', repo_name=ID)
536 # url('edit_repo', repo_name=ID)
538 defaults = self.__load_data(repo_name)
537 defaults = self.__load_data(repo_name)
539
538
540 return htmlfill.render(
539 return htmlfill.render(
541 render('admin/repos/repo_edit.html'),
540 render('admin/repos/repo_edit.html'),
542 defaults=defaults,
541 defaults=defaults,
543 encoding="UTF-8",
542 encoding="UTF-8",
544 force_defaults=False
543 force_defaults=False
545 )
544 )
546
545
547 @HasPermissionAllDecorator('hg.admin')
546 @HasPermissionAllDecorator('hg.admin')
548 def create_repo_field(self, repo_name):
547 def create_repo_field(self, repo_name):
549 try:
548 try:
550 form_result = RepoFieldForm()().to_python(dict(request.POST))
549 form_result = RepoFieldForm()().to_python(dict(request.POST))
551 new_field = RepositoryField()
550 new_field = RepositoryField()
552 new_field.repository = Repository.get_by_repo_name(repo_name)
551 new_field.repository = Repository.get_by_repo_name(repo_name)
553 new_field.field_key = form_result['new_field_key']
552 new_field.field_key = form_result['new_field_key']
554 new_field.field_type = form_result['new_field_type'] # python type
553 new_field.field_type = form_result['new_field_type'] # python type
555 new_field.field_value = form_result['new_field_value'] # set initial blank value
554 new_field.field_value = form_result['new_field_value'] # set initial blank value
556 new_field.field_desc = form_result['new_field_desc']
555 new_field.field_desc = form_result['new_field_desc']
557 new_field.field_label = form_result['new_field_label']
556 new_field.field_label = form_result['new_field_label']
558 Session().add(new_field)
557 Session().add(new_field)
559 Session().commit()
558 Session().commit()
560
559
561 except Exception, e:
560 except Exception, e:
562 log.error(traceback.format_exc())
561 log.error(traceback.format_exc())
563 msg = _('An error occurred during creation of field')
562 msg = _('An error occurred during creation of field')
564 if isinstance(e, formencode.Invalid):
563 if isinstance(e, formencode.Invalid):
565 msg += ". " + e.msg
564 msg += ". " + e.msg
566 h.flash(msg, category='error')
565 h.flash(msg, category='error')
567 return redirect(url('edit_repo', repo_name=repo_name))
566 return redirect(url('edit_repo', repo_name=repo_name))
568
567
569 @HasPermissionAllDecorator('hg.admin')
568 @HasPermissionAllDecorator('hg.admin')
570 def delete_repo_field(self, repo_name, field_id):
569 def delete_repo_field(self, repo_name, field_id):
571 field = RepositoryField.get_or_404(field_id)
570 field = RepositoryField.get_or_404(field_id)
572 try:
571 try:
573 Session().delete(field)
572 Session().delete(field)
574 Session().commit()
573 Session().commit()
575 except Exception, e:
574 except Exception, e:
576 log.error(traceback.format_exc())
575 log.error(traceback.format_exc())
577 msg = _('An error occurred during removal of field')
576 msg = _('An error occurred during removal of field')
578 h.flash(msg, category='error')
577 h.flash(msg, category='error')
579 return redirect(url('edit_repo', repo_name=repo_name))
578 return redirect(url('edit_repo', repo_name=repo_name))
@@ -1,1113 +1,1113 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from sqlalchemy.orm.exc import ObjectDeletedError
37 from sqlalchemy.orm.exc import ObjectDeletedError
38
38
39 from rhodecode import __platform__, is_windows, is_unix
39 from rhodecode import __platform__, is_windows, is_unix
40 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_unicode, aslist
42 from rhodecode.lib.utils2 import str2bool, safe_unicode, aslist
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError,\
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError,\
44 LdapImportError
44 LdapImportError
45 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug,\
45 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug,\
46 get_user_group_slug
46 get_user_group_slug
47 from rhodecode.lib.auth_ldap import AuthLdap
47 from rhodecode.lib.auth_ldap import AuthLdap
48
48
49 from rhodecode.model import meta
49 from rhodecode.model import meta
50 from rhodecode.model.user import UserModel
50 from rhodecode.model.user import UserModel
51 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
51 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
52 from rhodecode.lib.caching_query import FromCache
52 from rhodecode.lib.caching_query import FromCache
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 class PasswordGenerator(object):
57 class PasswordGenerator(object):
58 """
58 """
59 This is a simple class for generating password from different sets of
59 This is a simple class for generating password from different sets of
60 characters
60 characters
61 usage::
61 usage::
62
62
63 passwd_gen = PasswordGenerator()
63 passwd_gen = PasswordGenerator()
64 #print 8-letter password containing only big and small letters
64 #print 8-letter password containing only big and small letters
65 of alphabet
65 of alphabet
66 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
66 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
67 """
67 """
68 ALPHABETS_NUM = r'''1234567890'''
68 ALPHABETS_NUM = r'''1234567890'''
69 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
69 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
70 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
70 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
71 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
71 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
72 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
72 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
73 + ALPHABETS_NUM + ALPHABETS_SPECIAL
73 + ALPHABETS_NUM + ALPHABETS_SPECIAL
74 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
75 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
75 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
76 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
76 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
77 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
77 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
78
78
79 def __init__(self, passwd=''):
79 def __init__(self, passwd=''):
80 self.passwd = passwd
80 self.passwd = passwd
81
81
82 def gen_password(self, length, type_=None):
82 def gen_password(self, length, type_=None):
83 if type_ is None:
83 if type_ is None:
84 type_ = self.ALPHABETS_FULL
84 type_ = self.ALPHABETS_FULL
85 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
85 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
86 return self.passwd
86 return self.passwd
87
87
88
88
89 class RhodeCodeCrypto(object):
89 class RhodeCodeCrypto(object):
90
90
91 @classmethod
91 @classmethod
92 def hash_string(cls, str_):
92 def hash_string(cls, str_):
93 """
93 """
94 Cryptographic function used for password hashing based on pybcrypt
94 Cryptographic function used for password hashing based on pybcrypt
95 or pycrypto in windows
95 or pycrypto in windows
96
96
97 :param password: password to hash
97 :param password: password to hash
98 """
98 """
99 if is_windows:
99 if is_windows:
100 from hashlib import sha256
100 from hashlib import sha256
101 return sha256(str_).hexdigest()
101 return sha256(str_).hexdigest()
102 elif is_unix:
102 elif is_unix:
103 import bcrypt
103 import bcrypt
104 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
104 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
105 else:
105 else:
106 raise Exception('Unknown or unsupported platform %s' \
106 raise Exception('Unknown or unsupported platform %s' \
107 % __platform__)
107 % __platform__)
108
108
109 @classmethod
109 @classmethod
110 def hash_check(cls, password, hashed):
110 def hash_check(cls, password, hashed):
111 """
111 """
112 Checks matching password with it's hashed value, runs different
112 Checks matching password with it's hashed value, runs different
113 implementation based on platform it runs on
113 implementation based on platform it runs on
114
114
115 :param password: password
115 :param password: password
116 :param hashed: password in hashed form
116 :param hashed: password in hashed form
117 """
117 """
118
118
119 if is_windows:
119 if is_windows:
120 from hashlib import sha256
120 from hashlib import sha256
121 return sha256(password).hexdigest() == hashed
121 return sha256(password).hexdigest() == hashed
122 elif is_unix:
122 elif is_unix:
123 import bcrypt
123 import bcrypt
124 return bcrypt.hashpw(password, hashed) == hashed
124 return bcrypt.hashpw(password, hashed) == hashed
125 else:
125 else:
126 raise Exception('Unknown or unsupported platform %s' \
126 raise Exception('Unknown or unsupported platform %s' \
127 % __platform__)
127 % __platform__)
128
128
129
129
130 def get_crypt_password(password):
130 def get_crypt_password(password):
131 return RhodeCodeCrypto.hash_string(password)
131 return RhodeCodeCrypto.hash_string(password)
132
132
133
133
134 def check_password(password, hashed):
134 def check_password(password, hashed):
135 return RhodeCodeCrypto.hash_check(password, hashed)
135 return RhodeCodeCrypto.hash_check(password, hashed)
136
136
137
137
138 def generate_api_key(str_, salt=None):
138 def generate_api_key(str_, salt=None):
139 """
139 """
140 Generates API KEY from given string
140 Generates API KEY from given string
141
141
142 :param str_:
142 :param str_:
143 :param salt:
143 :param salt:
144 """
144 """
145
145
146 if salt is None:
146 if salt is None:
147 salt = _RandomNameSequence().next()
147 salt = _RandomNameSequence().next()
148
148
149 return hashlib.sha1(str_ + salt).hexdigest()
149 return hashlib.sha1(str_ + salt).hexdigest()
150
150
151
151
152 def authfunc(environ, username, password):
152 def authfunc(environ, username, password):
153 """
153 """
154 Dummy authentication wrapper function used in Mercurial and Git for
154 Dummy authentication wrapper function used in Mercurial and Git for
155 access control.
155 access control.
156
156
157 :param environ: needed only for using in Basic auth
157 :param environ: needed only for using in Basic auth
158 """
158 """
159 return authenticate(username, password)
159 return authenticate(username, password)
160
160
161
161
162 def authenticate(username, password):
162 def authenticate(username, password):
163 """
163 """
164 Authentication function used for access control,
164 Authentication function used for access control,
165 firstly checks for db authentication then if ldap is enabled for ldap
165 firstly checks for db authentication then if ldap is enabled for ldap
166 authentication, also creates ldap user if not in database
166 authentication, also creates ldap user if not in database
167
167
168 :param username: username
168 :param username: username
169 :param password: password
169 :param password: password
170 """
170 """
171
171
172 user_model = UserModel()
172 user_model = UserModel()
173 user = User.get_by_username(username)
173 user = User.get_by_username(username)
174
174
175 log.debug('Authenticating user using RhodeCode account')
175 log.debug('Authenticating user using RhodeCode account')
176 if user is not None and not user.ldap_dn:
176 if user is not None and not user.ldap_dn:
177 if user.active:
177 if user.active:
178 if user.username == 'default' and user.active:
178 if user.username == 'default' and user.active:
179 log.info('user %s authenticated correctly as anonymous user' %
179 log.info('user %s authenticated correctly as anonymous user' %
180 username)
180 username)
181 return True
181 return True
182
182
183 elif user.username == username and check_password(password,
183 elif user.username == username and check_password(password,
184 user.password):
184 user.password):
185 log.info('user %s authenticated correctly' % username)
185 log.info('user %s authenticated correctly' % username)
186 return True
186 return True
187 else:
187 else:
188 log.warning('user %s tried auth but is disabled' % username)
188 log.warning('user %s tried auth but is disabled' % username)
189
189
190 else:
190 else:
191 log.debug('Regular authentication failed')
191 log.debug('Regular authentication failed')
192 user_obj = User.get_by_username(username, case_insensitive=True)
192 user_obj = User.get_by_username(username, case_insensitive=True)
193
193
194 if user_obj is not None and not user_obj.ldap_dn:
194 if user_obj is not None and not user_obj.ldap_dn:
195 log.debug('this user already exists as non ldap')
195 log.debug('this user already exists as non ldap')
196 return False
196 return False
197
197
198 ldap_settings = RhodeCodeSetting.get_ldap_settings()
198 ldap_settings = RhodeCodeSetting.get_ldap_settings()
199 #======================================================================
199 #======================================================================
200 # FALLBACK TO LDAP AUTH IF ENABLE
200 # FALLBACK TO LDAP AUTH IF ENABLE
201 #======================================================================
201 #======================================================================
202 if str2bool(ldap_settings.get('ldap_active')):
202 if str2bool(ldap_settings.get('ldap_active')):
203 log.debug("Authenticating user using ldap")
203 log.debug("Authenticating user using ldap")
204 kwargs = {
204 kwargs = {
205 'server': ldap_settings.get('ldap_host', ''),
205 'server': ldap_settings.get('ldap_host', ''),
206 'base_dn': ldap_settings.get('ldap_base_dn', ''),
206 'base_dn': ldap_settings.get('ldap_base_dn', ''),
207 'port': ldap_settings.get('ldap_port'),
207 'port': ldap_settings.get('ldap_port'),
208 'bind_dn': ldap_settings.get('ldap_dn_user'),
208 'bind_dn': ldap_settings.get('ldap_dn_user'),
209 'bind_pass': ldap_settings.get('ldap_dn_pass'),
209 'bind_pass': ldap_settings.get('ldap_dn_pass'),
210 'tls_kind': ldap_settings.get('ldap_tls_kind'),
210 'tls_kind': ldap_settings.get('ldap_tls_kind'),
211 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
211 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
212 'ldap_filter': ldap_settings.get('ldap_filter'),
212 'ldap_filter': ldap_settings.get('ldap_filter'),
213 'search_scope': ldap_settings.get('ldap_search_scope'),
213 'search_scope': ldap_settings.get('ldap_search_scope'),
214 'attr_login': ldap_settings.get('ldap_attr_login'),
214 'attr_login': ldap_settings.get('ldap_attr_login'),
215 'ldap_version': 3,
215 'ldap_version': 3,
216 }
216 }
217 log.debug('Checking for ldap authentication')
217 log.debug('Checking for ldap authentication')
218 try:
218 try:
219 aldap = AuthLdap(**kwargs)
219 aldap = AuthLdap(**kwargs)
220 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
220 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
221 password)
221 password)
222 log.debug('Got ldap DN response %s' % user_dn)
222 log.debug('Got ldap DN response %s' % user_dn)
223
223
224 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
224 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
225 .get(k), [''])[0]
225 .get(k), [''])[0]
226
226
227 user_attrs = {
227 user_attrs = {
228 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
228 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
229 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
229 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
230 'email': get_ldap_attr('ldap_attr_email'),
230 'email': get_ldap_attr('ldap_attr_email'),
231 'active': 'hg.extern_activate.auto' in User.get_default_user()\
231 'active': 'hg.extern_activate.auto' in User.get_default_user()\
232 .AuthUser.permissions['global']
232 .AuthUser.permissions['global']
233 }
233 }
234
234
235 # don't store LDAP password since we don't need it. Override
235 # don't store LDAP password since we don't need it. Override
236 # with some random generated password
236 # with some random generated password
237 _password = PasswordGenerator().gen_password(length=8)
237 _password = PasswordGenerator().gen_password(length=8)
238 # create this user on the fly if it doesn't exist in rhodecode
238 # create this user on the fly if it doesn't exist in rhodecode
239 # database
239 # database
240 if user_model.create_ldap(username, _password, user_dn,
240 if user_model.create_ldap(username, _password, user_dn,
241 user_attrs):
241 user_attrs):
242 log.info('created new ldap user %s' % username)
242 log.info('created new ldap user %s' % username)
243
243
244 Session().commit()
244 Session().commit()
245 return True
245 return True
246 except (LdapUsernameError, LdapPasswordError, LdapImportError):
246 except (LdapUsernameError, LdapPasswordError, LdapImportError):
247 pass
247 pass
248 except (Exception,):
248 except (Exception,):
249 log.error(traceback.format_exc())
249 log.error(traceback.format_exc())
250 pass
250 pass
251 return False
251 return False
252
252
253
253
254 def login_container_auth(username):
254 def login_container_auth(username):
255 user = User.get_by_username(username)
255 user = User.get_by_username(username)
256 if user is None:
256 if user is None:
257 user_attrs = {
257 user_attrs = {
258 'name': username,
258 'name': username,
259 'lastname': None,
259 'lastname': None,
260 'email': None,
260 'email': None,
261 'active': 'hg.extern_activate.auto' in User.get_default_user()\
261 'active': 'hg.extern_activate.auto' in User.get_default_user()\
262 .AuthUser.permissions['global']
262 .AuthUser.permissions['global']
263 }
263 }
264 user = UserModel().create_for_container_auth(username, user_attrs)
264 user = UserModel().create_for_container_auth(username, user_attrs)
265 if not user:
265 if not user:
266 return None
266 return None
267 log.info('User %s was created by container authentication' % username)
267 log.info('User %s was created by container authentication' % username)
268
268
269 if not user.active:
269 if not user.active:
270 return None
270 return None
271
271
272 user.update_lastlogin()
272 user.update_lastlogin()
273 Session().commit()
273 Session().commit()
274
274
275 log.debug('User %s is now logged in by container authentication',
275 log.debug('User %s is now logged in by container authentication',
276 user.username)
276 user.username)
277 return user
277 return user
278
278
279
279
280 def get_container_username(environ, config, clean_username=False):
280 def get_container_username(environ, config, clean_username=False):
281 """
281 """
282 Get's the container_auth username (or email). It tries to get username
282 Get's the container_auth username (or email). It tries to get username
283 from REMOTE_USER if container_auth_enabled is enabled, if that fails
283 from REMOTE_USER if container_auth_enabled is enabled, if that fails
284 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
284 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
285 is enabled. clean_username extracts the username from this data if it's
285 is enabled. clean_username extracts the username from this data if it's
286 having @ in it.
286 having @ in it.
287
287
288 :param environ:
288 :param environ:
289 :param config:
289 :param config:
290 :param clean_username:
290 :param clean_username:
291 """
291 """
292 username = None
292 username = None
293
293
294 if str2bool(config.get('container_auth_enabled', False)):
294 if str2bool(config.get('container_auth_enabled', False)):
295 from paste.httpheaders import REMOTE_USER
295 from paste.httpheaders import REMOTE_USER
296 username = REMOTE_USER(environ)
296 username = REMOTE_USER(environ)
297 log.debug('extracted REMOTE_USER:%s' % (username))
297 log.debug('extracted REMOTE_USER:%s' % (username))
298
298
299 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
299 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
300 username = environ.get('HTTP_X_FORWARDED_USER')
300 username = environ.get('HTTP_X_FORWARDED_USER')
301 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
301 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
302
302
303 if username and clean_username:
303 if username and clean_username:
304 # Removing realm and domain from username
304 # Removing realm and domain from username
305 username = username.partition('@')[0]
305 username = username.partition('@')[0]
306 username = username.rpartition('\\')[2]
306 username = username.rpartition('\\')[2]
307 log.debug('Received username %s from container' % username)
307 log.debug('Received username %s from container' % username)
308
308
309 return username
309 return username
310
310
311
311
312 class CookieStoreWrapper(object):
312 class CookieStoreWrapper(object):
313
313
314 def __init__(self, cookie_store):
314 def __init__(self, cookie_store):
315 self.cookie_store = cookie_store
315 self.cookie_store = cookie_store
316
316
317 def __repr__(self):
317 def __repr__(self):
318 return 'CookieStore<%s>' % (self.cookie_store)
318 return 'CookieStore<%s>' % (self.cookie_store)
319
319
320 def get(self, key, other=None):
320 def get(self, key, other=None):
321 if isinstance(self.cookie_store, dict):
321 if isinstance(self.cookie_store, dict):
322 return self.cookie_store.get(key, other)
322 return self.cookie_store.get(key, other)
323 elif isinstance(self.cookie_store, AuthUser):
323 elif isinstance(self.cookie_store, AuthUser):
324 return self.cookie_store.__dict__.get(key, other)
324 return self.cookie_store.__dict__.get(key, other)
325
325
326
326
327 class AuthUser(object):
327 class AuthUser(object):
328 """
328 """
329 A simple object that handles all attributes of user in RhodeCode
329 A simple object that handles all attributes of user in RhodeCode
330
330
331 It does lookup based on API key,given user, or user present in session
331 It does lookup based on API key,given user, or user present in session
332 Then it fills all required information for such user. It also checks if
332 Then it fills all required information for such user. It also checks if
333 anonymous access is enabled and if so, it returns default user as logged
333 anonymous access is enabled and if so, it returns default user as logged
334 in
334 in
335 """
335 """
336
336
337 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
337 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
338
338
339 self.user_id = user_id
339 self.user_id = user_id
340 self.api_key = None
340 self.api_key = None
341 self.username = username
341 self.username = username
342 self.ip_addr = ip_addr
342 self.ip_addr = ip_addr
343
343
344 self.name = ''
344 self.name = ''
345 self.lastname = ''
345 self.lastname = ''
346 self.email = ''
346 self.email = ''
347 self.is_authenticated = False
347 self.is_authenticated = False
348 self.admin = False
348 self.admin = False
349 self.inherit_default_permissions = False
349 self.inherit_default_permissions = False
350 self.permissions = {}
350 self.permissions = {}
351 self._api_key = api_key
351 self._api_key = api_key
352 self.propagate_data()
352 self.propagate_data()
353 self._instance = None
353 self._instance = None
354
354
355 def propagate_data(self):
355 def propagate_data(self):
356 user_model = UserModel()
356 user_model = UserModel()
357 self.anonymous_user = User.get_by_username('default', cache=True)
357 self.anonymous_user = User.get_by_username('default', cache=True)
358 is_user_loaded = False
358 is_user_loaded = False
359
359
360 # try go get user by api key
360 # try go get user by api key
361 if self._api_key and self._api_key != self.anonymous_user.api_key:
361 if self._api_key and self._api_key != self.anonymous_user.api_key:
362 log.debug('Auth User lookup by API KEY %s' % self._api_key)
362 log.debug('Auth User lookup by API KEY %s' % self._api_key)
363 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
363 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
364 # lookup by userid
364 # lookup by userid
365 elif (self.user_id is not None and
365 elif (self.user_id is not None and
366 self.user_id != self.anonymous_user.user_id):
366 self.user_id != self.anonymous_user.user_id):
367 log.debug('Auth User lookup by USER ID %s' % self.user_id)
367 log.debug('Auth User lookup by USER ID %s' % self.user_id)
368 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
368 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
369 # lookup by username
369 # lookup by username
370 elif self.username and \
370 elif self.username and \
371 str2bool(config.get('container_auth_enabled', False)):
371 str2bool(config.get('container_auth_enabled', False)):
372
372
373 log.debug('Auth User lookup by USER NAME %s' % self.username)
373 log.debug('Auth User lookup by USER NAME %s' % self.username)
374 dbuser = login_container_auth(self.username)
374 dbuser = login_container_auth(self.username)
375 if dbuser is not None:
375 if dbuser is not None:
376 log.debug('filling all attributes to object')
376 log.debug('filling all attributes to object')
377 for k, v in dbuser.get_dict().items():
377 for k, v in dbuser.get_dict().items():
378 setattr(self, k, v)
378 setattr(self, k, v)
379 self.set_authenticated()
379 self.set_authenticated()
380 is_user_loaded = True
380 is_user_loaded = True
381 else:
381 else:
382 log.debug('No data in %s that could been used to log in' % self)
382 log.debug('No data in %s that could been used to log in' % self)
383
383
384 if not is_user_loaded:
384 if not is_user_loaded:
385 # if we cannot authenticate user try anonymous
385 # if we cannot authenticate user try anonymous
386 if self.anonymous_user.active:
386 if self.anonymous_user.active:
387 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
387 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
388 # then we set this user is logged in
388 # then we set this user is logged in
389 self.is_authenticated = True
389 self.is_authenticated = True
390 else:
390 else:
391 self.user_id = None
391 self.user_id = None
392 self.username = None
392 self.username = None
393 self.is_authenticated = False
393 self.is_authenticated = False
394
394
395 if not self.username:
395 if not self.username:
396 self.username = 'None'
396 self.username = 'None'
397
397
398 log.debug('Auth User is now %s' % self)
398 log.debug('Auth User is now %s' % self)
399 user_model.fill_perms(self)
399 user_model.fill_perms(self)
400
400
401 @property
401 @property
402 def is_admin(self):
402 def is_admin(self):
403 return self.admin
403 return self.admin
404
404
405 @property
405 @property
406 def repos_admin(self):
406 def repositories_admin(self):
407 """
407 """
408 Returns list of repositories you're an admin of
408 Returns list of repositories you're an admin of
409 """
409 """
410 return [x[0] for x in self.permissions['repositories'].iteritems()
410 return [x[0] for x in self.permissions['repositories'].iteritems()
411 if x[1] == 'repository.admin']
411 if x[1] == 'repository.admin']
412
412
413 @property
413 @property
414 def repository_groups_admin(self):
414 def repository_groups_admin(self):
415 """
415 """
416 Returns list of repository groups you're an admin of
416 Returns list of repository groups you're an admin of
417 """
417 """
418 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
418 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
419 if x[1] == 'group.admin']
419 if x[1] == 'group.admin']
420
420
421 @property
421 @property
422 def user_groups_admin(self):
422 def user_groups_admin(self):
423 """
423 """
424 Returns list of user groups you're an admin of
424 Returns list of user groups you're an admin of
425 """
425 """
426 return [x[0] for x in self.permissions['user_groups'].iteritems()
426 return [x[0] for x in self.permissions['user_groups'].iteritems()
427 if x[1] == 'usergroup.admin']
427 if x[1] == 'usergroup.admin']
428
428
429 @property
429 @property
430 def ip_allowed(self):
430 def ip_allowed(self):
431 """
431 """
432 Checks if ip_addr used in constructor is allowed from defined list of
432 Checks if ip_addr used in constructor is allowed from defined list of
433 allowed ip_addresses for user
433 allowed ip_addresses for user
434
434
435 :returns: boolean, True if ip is in allowed ip range
435 :returns: boolean, True if ip is in allowed ip range
436 """
436 """
437 #check IP
437 #check IP
438 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
438 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
439 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
439 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
440 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
440 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
441 return True
441 return True
442 else:
442 else:
443 log.info('Access for IP:%s forbidden, '
443 log.info('Access for IP:%s forbidden, '
444 'not in %s' % (self.ip_addr, allowed_ips))
444 'not in %s' % (self.ip_addr, allowed_ips))
445 return False
445 return False
446
446
447 def __repr__(self):
447 def __repr__(self):
448 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
448 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
449 self.is_authenticated)
449 self.is_authenticated)
450
450
451 def set_authenticated(self, authenticated=True):
451 def set_authenticated(self, authenticated=True):
452 if self.user_id != self.anonymous_user.user_id:
452 if self.user_id != self.anonymous_user.user_id:
453 self.is_authenticated = authenticated
453 self.is_authenticated = authenticated
454
454
455 def get_cookie_store(self):
455 def get_cookie_store(self):
456 return {'username': self.username,
456 return {'username': self.username,
457 'user_id': self.user_id,
457 'user_id': self.user_id,
458 'is_authenticated': self.is_authenticated}
458 'is_authenticated': self.is_authenticated}
459
459
460 @classmethod
460 @classmethod
461 def from_cookie_store(cls, cookie_store):
461 def from_cookie_store(cls, cookie_store):
462 """
462 """
463 Creates AuthUser from a cookie store
463 Creates AuthUser from a cookie store
464
464
465 :param cls:
465 :param cls:
466 :param cookie_store:
466 :param cookie_store:
467 """
467 """
468 user_id = cookie_store.get('user_id')
468 user_id = cookie_store.get('user_id')
469 username = cookie_store.get('username')
469 username = cookie_store.get('username')
470 api_key = cookie_store.get('api_key')
470 api_key = cookie_store.get('api_key')
471 return AuthUser(user_id, api_key, username)
471 return AuthUser(user_id, api_key, username)
472
472
473 @classmethod
473 @classmethod
474 def get_allowed_ips(cls, user_id, cache=False):
474 def get_allowed_ips(cls, user_id, cache=False):
475 _set = set()
475 _set = set()
476 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
476 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
477 if cache:
477 if cache:
478 user_ips = user_ips.options(FromCache("sql_cache_short",
478 user_ips = user_ips.options(FromCache("sql_cache_short",
479 "get_user_ips_%s" % user_id))
479 "get_user_ips_%s" % user_id))
480 for ip in user_ips:
480 for ip in user_ips:
481 try:
481 try:
482 _set.add(ip.ip_addr)
482 _set.add(ip.ip_addr)
483 except ObjectDeletedError:
483 except ObjectDeletedError:
484 # since we use heavy caching sometimes it happens that we get
484 # since we use heavy caching sometimes it happens that we get
485 # deleted objects here, we just skip them
485 # deleted objects here, we just skip them
486 pass
486 pass
487 return _set or set(['0.0.0.0/0', '::/0'])
487 return _set or set(['0.0.0.0/0', '::/0'])
488
488
489
489
490 def set_available_permissions(config):
490 def set_available_permissions(config):
491 """
491 """
492 This function will propagate pylons globals with all available defined
492 This function will propagate pylons globals with all available defined
493 permission given in db. We don't want to check each time from db for new
493 permission given in db. We don't want to check each time from db for new
494 permissions since adding a new permission also requires application restart
494 permissions since adding a new permission also requires application restart
495 ie. to decorate new views with the newly created permission
495 ie. to decorate new views with the newly created permission
496
496
497 :param config: current pylons config instance
497 :param config: current pylons config instance
498
498
499 """
499 """
500 log.info('getting information about all available permissions')
500 log.info('getting information about all available permissions')
501 try:
501 try:
502 sa = meta.Session
502 sa = meta.Session
503 all_perms = sa.query(Permission).all()
503 all_perms = sa.query(Permission).all()
504 except Exception:
504 except Exception:
505 pass
505 pass
506 finally:
506 finally:
507 meta.Session.remove()
507 meta.Session.remove()
508
508
509 config['available_permissions'] = [x.permission_name for x in all_perms]
509 config['available_permissions'] = [x.permission_name for x in all_perms]
510
510
511
511
512 #==============================================================================
512 #==============================================================================
513 # CHECK DECORATORS
513 # CHECK DECORATORS
514 #==============================================================================
514 #==============================================================================
515 class LoginRequired(object):
515 class LoginRequired(object):
516 """
516 """
517 Must be logged in to execute this function else
517 Must be logged in to execute this function else
518 redirect to login page
518 redirect to login page
519
519
520 :param api_access: if enabled this checks only for valid auth token
520 :param api_access: if enabled this checks only for valid auth token
521 and grants access based on valid token
521 and grants access based on valid token
522 """
522 """
523
523
524 def __init__(self, api_access=False):
524 def __init__(self, api_access=False):
525 self.api_access = api_access
525 self.api_access = api_access
526
526
527 def __call__(self, func):
527 def __call__(self, func):
528 return decorator(self.__wrapper, func)
528 return decorator(self.__wrapper, func)
529
529
530 def __wrapper(self, func, *fargs, **fkwargs):
530 def __wrapper(self, func, *fargs, **fkwargs):
531 cls = fargs[0]
531 cls = fargs[0]
532 user = cls.rhodecode_user
532 user = cls.rhodecode_user
533 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
533 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
534 # defined whitelist of controllers which API access will be enabled
534 # defined whitelist of controllers which API access will be enabled
535 whitelist = aslist(config.get('api_access_controllers_whitelist'),
535 whitelist = aslist(config.get('api_access_controllers_whitelist'),
536 sep=',')
536 sep=',')
537 api_access_whitelist = loc in whitelist
537 api_access_whitelist = loc in whitelist
538 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
538 log.debug('loc:%s is in API whitelist:%s:%s' % (loc, whitelist,
539 api_access_whitelist))
539 api_access_whitelist))
540 #check IP
540 #check IP
541 ip_access_ok = True
541 ip_access_ok = True
542 if not user.ip_allowed:
542 if not user.ip_allowed:
543 from rhodecode.lib import helpers as h
543 from rhodecode.lib import helpers as h
544 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
544 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
545 category='warning')
545 category='warning')
546 ip_access_ok = False
546 ip_access_ok = False
547
547
548 api_access_ok = False
548 api_access_ok = False
549 if self.api_access or api_access_whitelist:
549 if self.api_access or api_access_whitelist:
550 log.debug('Checking API KEY access for %s' % cls)
550 log.debug('Checking API KEY access for %s' % cls)
551 if user.api_key == request.GET.get('api_key'):
551 if user.api_key == request.GET.get('api_key'):
552 api_access_ok = True
552 api_access_ok = True
553 else:
553 else:
554 log.debug("API KEY token not valid")
554 log.debug("API KEY token not valid")
555
555
556 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
556 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
557 if (user.is_authenticated or api_access_ok) and ip_access_ok:
557 if (user.is_authenticated or api_access_ok) and ip_access_ok:
558 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
558 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
559 log.info('user %s is authenticated and granted access to %s '
559 log.info('user %s is authenticated and granted access to %s '
560 'using %s' % (user.username, loc, reason)
560 'using %s' % (user.username, loc, reason)
561 )
561 )
562 return func(*fargs, **fkwargs)
562 return func(*fargs, **fkwargs)
563 else:
563 else:
564 log.warn('user %s NOT authenticated on func: %s' % (
564 log.warn('user %s NOT authenticated on func: %s' % (
565 user, loc)
565 user, loc)
566 )
566 )
567 p = url.current()
567 p = url.current()
568
568
569 log.debug('redirecting to login page with %s' % p)
569 log.debug('redirecting to login page with %s' % p)
570 return redirect(url('login_home', came_from=p))
570 return redirect(url('login_home', came_from=p))
571
571
572
572
573 class NotAnonymous(object):
573 class NotAnonymous(object):
574 """
574 """
575 Must be logged in to execute this function else
575 Must be logged in to execute this function else
576 redirect to login page"""
576 redirect to login page"""
577
577
578 def __call__(self, func):
578 def __call__(self, func):
579 return decorator(self.__wrapper, func)
579 return decorator(self.__wrapper, func)
580
580
581 def __wrapper(self, func, *fargs, **fkwargs):
581 def __wrapper(self, func, *fargs, **fkwargs):
582 cls = fargs[0]
582 cls = fargs[0]
583 self.user = cls.rhodecode_user
583 self.user = cls.rhodecode_user
584
584
585 log.debug('Checking if user is not anonymous @%s' % cls)
585 log.debug('Checking if user is not anonymous @%s' % cls)
586
586
587 anonymous = self.user.username == 'default'
587 anonymous = self.user.username == 'default'
588
588
589 if anonymous:
589 if anonymous:
590 p = url.current()
590 p = url.current()
591
591
592 import rhodecode.lib.helpers as h
592 import rhodecode.lib.helpers as h
593 h.flash(_('You need to be a registered user to '
593 h.flash(_('You need to be a registered user to '
594 'perform this action'),
594 'perform this action'),
595 category='warning')
595 category='warning')
596 return redirect(url('login_home', came_from=p))
596 return redirect(url('login_home', came_from=p))
597 else:
597 else:
598 return func(*fargs, **fkwargs)
598 return func(*fargs, **fkwargs)
599
599
600
600
601 class PermsDecorator(object):
601 class PermsDecorator(object):
602 """Base class for controller decorators"""
602 """Base class for controller decorators"""
603
603
604 def __init__(self, *required_perms):
604 def __init__(self, *required_perms):
605 available_perms = config['available_permissions']
605 available_perms = config['available_permissions']
606 for perm in required_perms:
606 for perm in required_perms:
607 if perm not in available_perms:
607 if perm not in available_perms:
608 raise Exception("'%s' permission is not defined" % perm)
608 raise Exception("'%s' permission is not defined" % perm)
609 self.required_perms = set(required_perms)
609 self.required_perms = set(required_perms)
610 self.user_perms = None
610 self.user_perms = None
611
611
612 def __call__(self, func):
612 def __call__(self, func):
613 return decorator(self.__wrapper, func)
613 return decorator(self.__wrapper, func)
614
614
615 def __wrapper(self, func, *fargs, **fkwargs):
615 def __wrapper(self, func, *fargs, **fkwargs):
616 cls = fargs[0]
616 cls = fargs[0]
617 self.user = cls.rhodecode_user
617 self.user = cls.rhodecode_user
618 self.user_perms = self.user.permissions
618 self.user_perms = self.user.permissions
619 log.debug('checking %s permissions %s for %s %s',
619 log.debug('checking %s permissions %s for %s %s',
620 self.__class__.__name__, self.required_perms, cls, self.user)
620 self.__class__.__name__, self.required_perms, cls, self.user)
621
621
622 if self.check_permissions():
622 if self.check_permissions():
623 log.debug('Permission granted for %s %s' % (cls, self.user))
623 log.debug('Permission granted for %s %s' % (cls, self.user))
624 return func(*fargs, **fkwargs)
624 return func(*fargs, **fkwargs)
625
625
626 else:
626 else:
627 log.debug('Permission denied for %s %s' % (cls, self.user))
627 log.debug('Permission denied for %s %s' % (cls, self.user))
628 anonymous = self.user.username == 'default'
628 anonymous = self.user.username == 'default'
629
629
630 if anonymous:
630 if anonymous:
631 p = url.current()
631 p = url.current()
632
632
633 import rhodecode.lib.helpers as h
633 import rhodecode.lib.helpers as h
634 h.flash(_('You need to be a signed in to '
634 h.flash(_('You need to be a signed in to '
635 'view this page'),
635 'view this page'),
636 category='warning')
636 category='warning')
637 return redirect(url('login_home', came_from=p))
637 return redirect(url('login_home', came_from=p))
638
638
639 else:
639 else:
640 # redirect with forbidden ret code
640 # redirect with forbidden ret code
641 return abort(403)
641 return abort(403)
642
642
643 def check_permissions(self):
643 def check_permissions(self):
644 """Dummy function for overriding"""
644 """Dummy function for overriding"""
645 raise Exception('You have to write this function in child class')
645 raise Exception('You have to write this function in child class')
646
646
647
647
648 class HasPermissionAllDecorator(PermsDecorator):
648 class HasPermissionAllDecorator(PermsDecorator):
649 """
649 """
650 Checks for access permission for all given predicates. All of them
650 Checks for access permission for all given predicates. All of them
651 have to be meet in order to fulfill the request
651 have to be meet in order to fulfill the request
652 """
652 """
653
653
654 def check_permissions(self):
654 def check_permissions(self):
655 if self.required_perms.issubset(self.user_perms.get('global')):
655 if self.required_perms.issubset(self.user_perms.get('global')):
656 return True
656 return True
657 return False
657 return False
658
658
659
659
660 class HasPermissionAnyDecorator(PermsDecorator):
660 class HasPermissionAnyDecorator(PermsDecorator):
661 """
661 """
662 Checks for access permission for any of given predicates. In order to
662 Checks for access permission for any of given predicates. In order to
663 fulfill the request any of predicates must be meet
663 fulfill the request any of predicates must be meet
664 """
664 """
665
665
666 def check_permissions(self):
666 def check_permissions(self):
667 if self.required_perms.intersection(self.user_perms.get('global')):
667 if self.required_perms.intersection(self.user_perms.get('global')):
668 return True
668 return True
669 return False
669 return False
670
670
671
671
672 class HasRepoPermissionAllDecorator(PermsDecorator):
672 class HasRepoPermissionAllDecorator(PermsDecorator):
673 """
673 """
674 Checks for access permission for all given predicates for specific
674 Checks for access permission for all given predicates for specific
675 repository. All of them have to be meet in order to fulfill the request
675 repository. All of them have to be meet in order to fulfill the request
676 """
676 """
677
677
678 def check_permissions(self):
678 def check_permissions(self):
679 repo_name = get_repo_slug(request)
679 repo_name = get_repo_slug(request)
680 try:
680 try:
681 user_perms = set([self.user_perms['repositories'][repo_name]])
681 user_perms = set([self.user_perms['repositories'][repo_name]])
682 except KeyError:
682 except KeyError:
683 return False
683 return False
684 if self.required_perms.issubset(user_perms):
684 if self.required_perms.issubset(user_perms):
685 return True
685 return True
686 return False
686 return False
687
687
688
688
689 class HasRepoPermissionAnyDecorator(PermsDecorator):
689 class HasRepoPermissionAnyDecorator(PermsDecorator):
690 """
690 """
691 Checks for access permission for any of given predicates for specific
691 Checks for access permission for any of given predicates for specific
692 repository. In order to fulfill the request any of predicates must be meet
692 repository. In order to fulfill the request any of predicates must be meet
693 """
693 """
694
694
695 def check_permissions(self):
695 def check_permissions(self):
696 repo_name = get_repo_slug(request)
696 repo_name = get_repo_slug(request)
697 try:
697 try:
698 user_perms = set([self.user_perms['repositories'][repo_name]])
698 user_perms = set([self.user_perms['repositories'][repo_name]])
699 except KeyError:
699 except KeyError:
700 return False
700 return False
701
701
702 if self.required_perms.intersection(user_perms):
702 if self.required_perms.intersection(user_perms):
703 return True
703 return True
704 return False
704 return False
705
705
706
706
707 class HasReposGroupPermissionAllDecorator(PermsDecorator):
707 class HasReposGroupPermissionAllDecorator(PermsDecorator):
708 """
708 """
709 Checks for access permission for all given predicates for specific
709 Checks for access permission for all given predicates for specific
710 repository group. All of them have to be meet in order to fulfill the request
710 repository group. All of them have to be meet in order to fulfill the request
711 """
711 """
712
712
713 def check_permissions(self):
713 def check_permissions(self):
714 group_name = get_repos_group_slug(request)
714 group_name = get_repos_group_slug(request)
715 try:
715 try:
716 user_perms = set([self.user_perms['repositories_groups'][group_name]])
716 user_perms = set([self.user_perms['repositories_groups'][group_name]])
717 except KeyError:
717 except KeyError:
718 return False
718 return False
719
719
720 if self.required_perms.issubset(user_perms):
720 if self.required_perms.issubset(user_perms):
721 return True
721 return True
722 return False
722 return False
723
723
724
724
725 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
725 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
726 """
726 """
727 Checks for access permission for any of given predicates for specific
727 Checks for access permission for any of given predicates for specific
728 repository group. In order to fulfill the request any of predicates must be meet
728 repository group. In order to fulfill the request any of predicates must be meet
729 """
729 """
730
730
731 def check_permissions(self):
731 def check_permissions(self):
732 group_name = get_repos_group_slug(request)
732 group_name = get_repos_group_slug(request)
733 try:
733 try:
734 user_perms = set([self.user_perms['repositories_groups'][group_name]])
734 user_perms = set([self.user_perms['repositories_groups'][group_name]])
735 except KeyError:
735 except KeyError:
736 return False
736 return False
737
737
738 if self.required_perms.intersection(user_perms):
738 if self.required_perms.intersection(user_perms):
739 return True
739 return True
740 return False
740 return False
741
741
742
742
743 class HasUserGroupPermissionAllDecorator(PermsDecorator):
743 class HasUserGroupPermissionAllDecorator(PermsDecorator):
744 """
744 """
745 Checks for access permission for all given predicates for specific
745 Checks for access permission for all given predicates for specific
746 user group. All of them have to be meet in order to fulfill the request
746 user group. All of them have to be meet in order to fulfill the request
747 """
747 """
748
748
749 def check_permissions(self):
749 def check_permissions(self):
750 group_name = get_user_group_slug(request)
750 group_name = get_user_group_slug(request)
751 try:
751 try:
752 user_perms = set([self.user_perms['user_groups'][group_name]])
752 user_perms = set([self.user_perms['user_groups'][group_name]])
753 except KeyError:
753 except KeyError:
754 return False
754 return False
755
755
756 if self.required_perms.issubset(user_perms):
756 if self.required_perms.issubset(user_perms):
757 return True
757 return True
758 return False
758 return False
759
759
760
760
761 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
761 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
762 """
762 """
763 Checks for access permission for any of given predicates for specific
763 Checks for access permission for any of given predicates for specific
764 user group. In order to fulfill the request any of predicates must be meet
764 user group. In order to fulfill the request any of predicates must be meet
765 """
765 """
766
766
767 def check_permissions(self):
767 def check_permissions(self):
768 group_name = get_user_group_slug(request)
768 group_name = get_user_group_slug(request)
769 try:
769 try:
770 user_perms = set([self.user_perms['user_groups'][group_name]])
770 user_perms = set([self.user_perms['user_groups'][group_name]])
771 except KeyError:
771 except KeyError:
772 return False
772 return False
773
773
774 if self.required_perms.intersection(user_perms):
774 if self.required_perms.intersection(user_perms):
775 return True
775 return True
776 return False
776 return False
777
777
778
778
779 #==============================================================================
779 #==============================================================================
780 # CHECK FUNCTIONS
780 # CHECK FUNCTIONS
781 #==============================================================================
781 #==============================================================================
782 class PermsFunction(object):
782 class PermsFunction(object):
783 """Base function for other check functions"""
783 """Base function for other check functions"""
784
784
785 def __init__(self, *perms):
785 def __init__(self, *perms):
786 available_perms = config['available_permissions']
786 available_perms = config['available_permissions']
787
787
788 for perm in perms:
788 for perm in perms:
789 if perm not in available_perms:
789 if perm not in available_perms:
790 raise Exception("'%s' permission is not defined" % perm)
790 raise Exception("'%s' permission is not defined" % perm)
791 self.required_perms = set(perms)
791 self.required_perms = set(perms)
792 self.user_perms = None
792 self.user_perms = None
793 self.repo_name = None
793 self.repo_name = None
794 self.group_name = None
794 self.group_name = None
795
795
796 def __call__(self, check_location=''):
796 def __call__(self, check_location=''):
797 #TODO: put user as attribute here
797 #TODO: put user as attribute here
798 user = request.user
798 user = request.user
799 cls_name = self.__class__.__name__
799 cls_name = self.__class__.__name__
800 check_scope = {
800 check_scope = {
801 'HasPermissionAll': '',
801 'HasPermissionAll': '',
802 'HasPermissionAny': '',
802 'HasPermissionAny': '',
803 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
803 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
804 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
804 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
805 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
805 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
806 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
806 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
807 }.get(cls_name, '?')
807 }.get(cls_name, '?')
808 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
808 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
809 self.required_perms, user, check_scope,
809 self.required_perms, user, check_scope,
810 check_location or 'unspecified location')
810 check_location or 'unspecified location')
811 if not user:
811 if not user:
812 log.debug('Empty request user')
812 log.debug('Empty request user')
813 return False
813 return False
814 self.user_perms = user.permissions
814 self.user_perms = user.permissions
815 if self.check_permissions():
815 if self.check_permissions():
816 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
816 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
817 check_location or 'unspecified location')
817 check_location or 'unspecified location')
818 return True
818 return True
819
819
820 else:
820 else:
821 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
821 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
822 check_location or 'unspecified location')
822 check_location or 'unspecified location')
823 return False
823 return False
824
824
825 def check_permissions(self):
825 def check_permissions(self):
826 """Dummy function for overriding"""
826 """Dummy function for overriding"""
827 raise Exception('You have to write this function in child class')
827 raise Exception('You have to write this function in child class')
828
828
829
829
830 class HasPermissionAll(PermsFunction):
830 class HasPermissionAll(PermsFunction):
831 def check_permissions(self):
831 def check_permissions(self):
832 if self.required_perms.issubset(self.user_perms.get('global')):
832 if self.required_perms.issubset(self.user_perms.get('global')):
833 return True
833 return True
834 return False
834 return False
835
835
836
836
837 class HasPermissionAny(PermsFunction):
837 class HasPermissionAny(PermsFunction):
838 def check_permissions(self):
838 def check_permissions(self):
839 if self.required_perms.intersection(self.user_perms.get('global')):
839 if self.required_perms.intersection(self.user_perms.get('global')):
840 return True
840 return True
841 return False
841 return False
842
842
843
843
844 class HasRepoPermissionAll(PermsFunction):
844 class HasRepoPermissionAll(PermsFunction):
845 def __call__(self, repo_name=None, check_location=''):
845 def __call__(self, repo_name=None, check_location=''):
846 self.repo_name = repo_name
846 self.repo_name = repo_name
847 return super(HasRepoPermissionAll, self).__call__(check_location)
847 return super(HasRepoPermissionAll, self).__call__(check_location)
848
848
849 def check_permissions(self):
849 def check_permissions(self):
850 if not self.repo_name:
850 if not self.repo_name:
851 self.repo_name = get_repo_slug(request)
851 self.repo_name = get_repo_slug(request)
852
852
853 try:
853 try:
854 self._user_perms = set(
854 self._user_perms = set(
855 [self.user_perms['repositories'][self.repo_name]]
855 [self.user_perms['repositories'][self.repo_name]]
856 )
856 )
857 except KeyError:
857 except KeyError:
858 return False
858 return False
859 if self.required_perms.issubset(self._user_perms):
859 if self.required_perms.issubset(self._user_perms):
860 return True
860 return True
861 return False
861 return False
862
862
863
863
864 class HasRepoPermissionAny(PermsFunction):
864 class HasRepoPermissionAny(PermsFunction):
865 def __call__(self, repo_name=None, check_location=''):
865 def __call__(self, repo_name=None, check_location=''):
866 self.repo_name = repo_name
866 self.repo_name = repo_name
867 return super(HasRepoPermissionAny, self).__call__(check_location)
867 return super(HasRepoPermissionAny, self).__call__(check_location)
868
868
869 def check_permissions(self):
869 def check_permissions(self):
870 if not self.repo_name:
870 if not self.repo_name:
871 self.repo_name = get_repo_slug(request)
871 self.repo_name = get_repo_slug(request)
872
872
873 try:
873 try:
874 self._user_perms = set(
874 self._user_perms = set(
875 [self.user_perms['repositories'][self.repo_name]]
875 [self.user_perms['repositories'][self.repo_name]]
876 )
876 )
877 except KeyError:
877 except KeyError:
878 return False
878 return False
879 if self.required_perms.intersection(self._user_perms):
879 if self.required_perms.intersection(self._user_perms):
880 return True
880 return True
881 return False
881 return False
882
882
883
883
884 class HasReposGroupPermissionAny(PermsFunction):
884 class HasReposGroupPermissionAny(PermsFunction):
885 def __call__(self, group_name=None, check_location=''):
885 def __call__(self, group_name=None, check_location=''):
886 self.group_name = group_name
886 self.group_name = group_name
887 return super(HasReposGroupPermissionAny, self).__call__(check_location)
887 return super(HasReposGroupPermissionAny, self).__call__(check_location)
888
888
889 def check_permissions(self):
889 def check_permissions(self):
890 try:
890 try:
891 self._user_perms = set(
891 self._user_perms = set(
892 [self.user_perms['repositories_groups'][self.group_name]]
892 [self.user_perms['repositories_groups'][self.group_name]]
893 )
893 )
894 except KeyError:
894 except KeyError:
895 return False
895 return False
896 if self.required_perms.intersection(self._user_perms):
896 if self.required_perms.intersection(self._user_perms):
897 return True
897 return True
898 return False
898 return False
899
899
900
900
901 class HasReposGroupPermissionAll(PermsFunction):
901 class HasReposGroupPermissionAll(PermsFunction):
902 def __call__(self, group_name=None, check_location=''):
902 def __call__(self, group_name=None, check_location=''):
903 self.group_name = group_name
903 self.group_name = group_name
904 return super(HasReposGroupPermissionAll, self).__call__(check_location)
904 return super(HasReposGroupPermissionAll, self).__call__(check_location)
905
905
906 def check_permissions(self):
906 def check_permissions(self):
907 try:
907 try:
908 self._user_perms = set(
908 self._user_perms = set(
909 [self.user_perms['repositories_groups'][self.group_name]]
909 [self.user_perms['repositories_groups'][self.group_name]]
910 )
910 )
911 except KeyError:
911 except KeyError:
912 return False
912 return False
913 if self.required_perms.issubset(self._user_perms):
913 if self.required_perms.issubset(self._user_perms):
914 return True
914 return True
915 return False
915 return False
916
916
917
917
918 class HasUserGroupPermissionAny(PermsFunction):
918 class HasUserGroupPermissionAny(PermsFunction):
919 def __call__(self, user_group_name=None, check_location=''):
919 def __call__(self, user_group_name=None, check_location=''):
920 self.user_group_name = user_group_name
920 self.user_group_name = user_group_name
921 return super(HasUserGroupPermissionAny, self).__call__(check_location)
921 return super(HasUserGroupPermissionAny, self).__call__(check_location)
922
922
923 def check_permissions(self):
923 def check_permissions(self):
924 try:
924 try:
925 self._user_perms = set(
925 self._user_perms = set(
926 [self.user_perms['user_groups'][self.user_group_name]]
926 [self.user_perms['user_groups'][self.user_group_name]]
927 )
927 )
928 except KeyError:
928 except KeyError:
929 return False
929 return False
930 if self.required_perms.intersection(self._user_perms):
930 if self.required_perms.intersection(self._user_perms):
931 return True
931 return True
932 return False
932 return False
933
933
934
934
935 class HasUserGroupPermissionAll(PermsFunction):
935 class HasUserGroupPermissionAll(PermsFunction):
936 def __call__(self, user_group_name=None, check_location=''):
936 def __call__(self, user_group_name=None, check_location=''):
937 self.user_group_name = user_group_name
937 self.user_group_name = user_group_name
938 return super(HasUserGroupPermissionAll, self).__call__(check_location)
938 return super(HasUserGroupPermissionAll, self).__call__(check_location)
939
939
940 def check_permissions(self):
940 def check_permissions(self):
941 try:
941 try:
942 self._user_perms = set(
942 self._user_perms = set(
943 [self.user_perms['user_groups'][self.user_group_name]]
943 [self.user_perms['user_groups'][self.user_group_name]]
944 )
944 )
945 except KeyError:
945 except KeyError:
946 return False
946 return False
947 if self.required_perms.issubset(self._user_perms):
947 if self.required_perms.issubset(self._user_perms):
948 return True
948 return True
949 return False
949 return False
950
950
951 #==============================================================================
951 #==============================================================================
952 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
952 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
953 #==============================================================================
953 #==============================================================================
954 class HasPermissionAnyMiddleware(object):
954 class HasPermissionAnyMiddleware(object):
955 def __init__(self, *perms):
955 def __init__(self, *perms):
956 self.required_perms = set(perms)
956 self.required_perms = set(perms)
957
957
958 def __call__(self, user, repo_name):
958 def __call__(self, user, repo_name):
959 # repo_name MUST be unicode, since we handle keys in permission
959 # repo_name MUST be unicode, since we handle keys in permission
960 # dict by unicode
960 # dict by unicode
961 repo_name = safe_unicode(repo_name)
961 repo_name = safe_unicode(repo_name)
962 usr = AuthUser(user.user_id)
962 usr = AuthUser(user.user_id)
963 try:
963 try:
964 self.user_perms = set([usr.permissions['repositories'][repo_name]])
964 self.user_perms = set([usr.permissions['repositories'][repo_name]])
965 except Exception:
965 except Exception:
966 log.error('Exception while accessing permissions %s' %
966 log.error('Exception while accessing permissions %s' %
967 traceback.format_exc())
967 traceback.format_exc())
968 self.user_perms = set()
968 self.user_perms = set()
969 self.username = user.username
969 self.username = user.username
970 self.repo_name = repo_name
970 self.repo_name = repo_name
971 return self.check_permissions()
971 return self.check_permissions()
972
972
973 def check_permissions(self):
973 def check_permissions(self):
974 log.debug('checking VCS protocol '
974 log.debug('checking VCS protocol '
975 'permissions %s for user:%s repository:%s', self.user_perms,
975 'permissions %s for user:%s repository:%s', self.user_perms,
976 self.username, self.repo_name)
976 self.username, self.repo_name)
977 if self.required_perms.intersection(self.user_perms):
977 if self.required_perms.intersection(self.user_perms):
978 log.debug('permission granted for user:%s on repo:%s' % (
978 log.debug('permission granted for user:%s on repo:%s' % (
979 self.username, self.repo_name
979 self.username, self.repo_name
980 )
980 )
981 )
981 )
982 return True
982 return True
983 log.debug('permission denied for user:%s on repo:%s' % (
983 log.debug('permission denied for user:%s on repo:%s' % (
984 self.username, self.repo_name
984 self.username, self.repo_name
985 )
985 )
986 )
986 )
987 return False
987 return False
988
988
989
989
990 #==============================================================================
990 #==============================================================================
991 # SPECIAL VERSION TO HANDLE API AUTH
991 # SPECIAL VERSION TO HANDLE API AUTH
992 #==============================================================================
992 #==============================================================================
993 class _BaseApiPerm(object):
993 class _BaseApiPerm(object):
994 def __init__(self, *perms):
994 def __init__(self, *perms):
995 self.required_perms = set(perms)
995 self.required_perms = set(perms)
996
996
997 def __call__(self, check_location='unspecified', user=None, repo_name=None):
997 def __call__(self, check_location='unspecified', user=None, repo_name=None):
998 cls_name = self.__class__.__name__
998 cls_name = self.__class__.__name__
999 check_scope = 'user:%s, repo:%s' % (user, repo_name)
999 check_scope = 'user:%s, repo:%s' % (user, repo_name)
1000 log.debug('checking cls:%s %s %s @ %s', cls_name,
1000 log.debug('checking cls:%s %s %s @ %s', cls_name,
1001 self.required_perms, check_scope, check_location)
1001 self.required_perms, check_scope, check_location)
1002 if not user:
1002 if not user:
1003 log.debug('Empty User passed into arguments')
1003 log.debug('Empty User passed into arguments')
1004 return False
1004 return False
1005
1005
1006 ## process user
1006 ## process user
1007 if not isinstance(user, AuthUser):
1007 if not isinstance(user, AuthUser):
1008 user = AuthUser(user.user_id)
1008 user = AuthUser(user.user_id)
1009
1009
1010 if self.check_permissions(user.permissions, repo_name):
1010 if self.check_permissions(user.permissions, repo_name):
1011 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
1011 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
1012 user, check_location)
1012 user, check_location)
1013 return True
1013 return True
1014
1014
1015 else:
1015 else:
1016 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
1016 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
1017 user, check_location)
1017 user, check_location)
1018 return False
1018 return False
1019
1019
1020 def check_permissions(self, perm_defs, repo_name):
1020 def check_permissions(self, perm_defs, repo_name):
1021 """
1021 """
1022 implement in child class should return True if permissions are ok,
1022 implement in child class should return True if permissions are ok,
1023 False otherwise
1023 False otherwise
1024
1024
1025 :param perm_defs: dict with permission definitions
1025 :param perm_defs: dict with permission definitions
1026 :param repo_name: repo name
1026 :param repo_name: repo name
1027 """
1027 """
1028 raise NotImplementedError()
1028 raise NotImplementedError()
1029
1029
1030
1030
1031 class HasPermissionAllApi(_BaseApiPerm):
1031 class HasPermissionAllApi(_BaseApiPerm):
1032 def __call__(self, user, check_location=''):
1032 def __call__(self, user, check_location=''):
1033 return super(HasPermissionAllApi, self)\
1033 return super(HasPermissionAllApi, self)\
1034 .__call__(check_location=check_location, user=user)
1034 .__call__(check_location=check_location, user=user)
1035
1035
1036 def check_permissions(self, perm_defs, repo):
1036 def check_permissions(self, perm_defs, repo):
1037 if self.required_perms.issubset(perm_defs.get('global')):
1037 if self.required_perms.issubset(perm_defs.get('global')):
1038 return True
1038 return True
1039 return False
1039 return False
1040
1040
1041
1041
1042 class HasPermissionAnyApi(_BaseApiPerm):
1042 class HasPermissionAnyApi(_BaseApiPerm):
1043 def __call__(self, user, check_location=''):
1043 def __call__(self, user, check_location=''):
1044 return super(HasPermissionAnyApi, self)\
1044 return super(HasPermissionAnyApi, self)\
1045 .__call__(check_location=check_location, user=user)
1045 .__call__(check_location=check_location, user=user)
1046
1046
1047 def check_permissions(self, perm_defs, repo):
1047 def check_permissions(self, perm_defs, repo):
1048 if self.required_perms.intersection(perm_defs.get('global')):
1048 if self.required_perms.intersection(perm_defs.get('global')):
1049 return True
1049 return True
1050 return False
1050 return False
1051
1051
1052
1052
1053 class HasRepoPermissionAllApi(_BaseApiPerm):
1053 class HasRepoPermissionAllApi(_BaseApiPerm):
1054 def __call__(self, user, repo_name, check_location=''):
1054 def __call__(self, user, repo_name, check_location=''):
1055 return super(HasRepoPermissionAllApi, self)\
1055 return super(HasRepoPermissionAllApi, self)\
1056 .__call__(check_location=check_location, user=user,
1056 .__call__(check_location=check_location, user=user,
1057 repo_name=repo_name)
1057 repo_name=repo_name)
1058
1058
1059 def check_permissions(self, perm_defs, repo_name):
1059 def check_permissions(self, perm_defs, repo_name):
1060
1060
1061 try:
1061 try:
1062 self._user_perms = set(
1062 self._user_perms = set(
1063 [perm_defs['repositories'][repo_name]]
1063 [perm_defs['repositories'][repo_name]]
1064 )
1064 )
1065 except KeyError:
1065 except KeyError:
1066 log.warning(traceback.format_exc())
1066 log.warning(traceback.format_exc())
1067 return False
1067 return False
1068 if self.required_perms.issubset(self._user_perms):
1068 if self.required_perms.issubset(self._user_perms):
1069 return True
1069 return True
1070 return False
1070 return False
1071
1071
1072
1072
1073 class HasRepoPermissionAnyApi(_BaseApiPerm):
1073 class HasRepoPermissionAnyApi(_BaseApiPerm):
1074 def __call__(self, user, repo_name, check_location=''):
1074 def __call__(self, user, repo_name, check_location=''):
1075 return super(HasRepoPermissionAnyApi, self)\
1075 return super(HasRepoPermissionAnyApi, self)\
1076 .__call__(check_location=check_location, user=user,
1076 .__call__(check_location=check_location, user=user,
1077 repo_name=repo_name)
1077 repo_name=repo_name)
1078
1078
1079 def check_permissions(self, perm_defs, repo_name):
1079 def check_permissions(self, perm_defs, repo_name):
1080
1080
1081 try:
1081 try:
1082 _user_perms = set(
1082 _user_perms = set(
1083 [perm_defs['repositories'][repo_name]]
1083 [perm_defs['repositories'][repo_name]]
1084 )
1084 )
1085 except KeyError:
1085 except KeyError:
1086 log.warning(traceback.format_exc())
1086 log.warning(traceback.format_exc())
1087 return False
1087 return False
1088 if self.required_perms.intersection(_user_perms):
1088 if self.required_perms.intersection(_user_perms):
1089 return True
1089 return True
1090 return False
1090 return False
1091
1091
1092
1092
1093 def check_ip_access(source_ip, allowed_ips=None):
1093 def check_ip_access(source_ip, allowed_ips=None):
1094 """
1094 """
1095 Checks if source_ip is a subnet of any of allowed_ips.
1095 Checks if source_ip is a subnet of any of allowed_ips.
1096
1096
1097 :param source_ip:
1097 :param source_ip:
1098 :param allowed_ips: list of allowed ips together with mask
1098 :param allowed_ips: list of allowed ips together with mask
1099 """
1099 """
1100 from rhodecode.lib import ipaddr
1100 from rhodecode.lib import ipaddr
1101 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1101 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1102 if isinstance(allowed_ips, (tuple, list, set)):
1102 if isinstance(allowed_ips, (tuple, list, set)):
1103 for ip in allowed_ips:
1103 for ip in allowed_ips:
1104 try:
1104 try:
1105 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1105 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1106 return True
1106 return True
1107 # for any case we cannot determine the IP, don't crash just
1107 # for any case we cannot determine the IP, don't crash just
1108 # skip it and log as error, we want to say forbidden still when
1108 # skip it and log as error, we want to say forbidden still when
1109 # sending bad IP
1109 # sending bad IP
1110 except Exception:
1110 except Exception:
1111 log.error(traceback.format_exc())
1111 log.error(traceback.format_exc())
1112 continue
1112 continue
1113 return False
1113 return False
@@ -1,354 +1,358 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="root.html"/>
2 <%inherit file="root.html"/>
3
3
4 <!-- HEADER -->
4 <!-- HEADER -->
5 <div id="header-dd"></div>
5 <div id="header-dd"></div>
6 <div id="header">
6 <div id="header">
7 <div id="header-inner" class="title">
7 <div id="header-inner" class="title">
8 <div id="logo">
8 <div id="logo">
9 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
9 <h1><a href="${h.url('home')}">${c.rhodecode_name}</a></h1>
10 </div>
10 </div>
11 <!-- MENU -->
11 <!-- MENU -->
12 ${self.page_nav()}
12 ${self.page_nav()}
13 <!-- END MENU -->
13 <!-- END MENU -->
14 ${self.body()}
14 ${self.body()}
15 </div>
15 </div>
16 </div>
16 </div>
17 <!-- END HEADER -->
17 <!-- END HEADER -->
18
18
19 <!-- CONTENT -->
19 <!-- CONTENT -->
20 <div id="content">
20 <div id="content">
21 <div class="flash_msg">
21 <div class="flash_msg">
22 <% messages = h.flash.pop_messages() %>
22 <% messages = h.flash.pop_messages() %>
23 % if messages:
23 % if messages:
24 <ul id="flash-messages">
24 <ul id="flash-messages">
25 % for message in messages:
25 % for message in messages:
26 <li class="${message.category}_msg">${message}</li>
26 <li class="${message.category}_msg">${message}</li>
27 % endfor
27 % endfor
28 </ul>
28 </ul>
29 % endif
29 % endif
30 </div>
30 </div>
31 <div id="main">
31 <div id="main">
32 ${next.main()}
32 ${next.main()}
33 </div>
33 </div>
34 </div>
34 </div>
35 <!-- END CONTENT -->
35 <!-- END CONTENT -->
36
36
37 <!-- FOOTER -->
37 <!-- FOOTER -->
38 <div id="footer">
38 <div id="footer">
39 <div id="footer-inner" class="title">
39 <div id="footer-inner" class="title">
40 <div>
40 <div>
41 <p class="footer-link">
41 <p class="footer-link">
42 ${_('Server instance: %s') % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}
42 ${_('Server instance: %s') % c.rhodecode_instanceid if c.rhodecode_instanceid else ''}
43 </p>
43 </p>
44 <p class="footer-link-right">
44 <p class="footer-link-right">
45 <a href="${h.url('rhodecode_official')}">RhodeCode ${c.rhodecode_version}</a>
45 <a href="${h.url('rhodecode_official')}">RhodeCode ${c.rhodecode_version}</a>
46 &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski and others
46 &copy; 2010-${h.datetime.today().year} by Marcin Kuzminski and others
47 &ndash; <a href="${h.url('bugtracker')}">${_('Report a bug')}</a>
47 &ndash; <a href="${h.url('bugtracker')}">${_('Report a bug')}</a>
48 </p>
48 </p>
49 </div>
49 </div>
50 </div>
50 </div>
51 </div>
51 </div>
52
52
53 <!-- END FOOTER -->
53 <!-- END FOOTER -->
54
54
55 ### MAKO DEFS ###
55 ### MAKO DEFS ###
56 <%def name="breadcrumbs()">
56 <%def name="breadcrumbs()">
57 <div class="breadcrumbs">
57 <div class="breadcrumbs">
58 ${self.breadcrumbs_links()}
58 ${self.breadcrumbs_links()}
59 </div>
59 </div>
60 </%def>
60 </%def>
61
61
62 <%def name="context_bar(current)">
62 <%def name="context_bar(current)">
63 ${repo_context_bar(current)}
63 ${repo_context_bar(current)}
64 </%def>
64 </%def>
65
65
66 <%def name="admin_menu()">
66 <%def name="admin_menu()">
67 <ul class="admin_menu">
67 <ul class="admin_menu">
68 <li>${h.link_to(_('Admin journal'),h.url('admin_home'),class_='journal ')}</li>
68 <li>${h.link_to(_('Admin journal'),h.url('admin_home'),class_='journal ')}</li>
69 <li>${h.link_to(_('Repositories'),h.url('repos'),class_='repos')}</li>
69 <li>${h.link_to(_('Repositories'),h.url('repos'),class_='repos')}</li>
70 <li>${h.link_to(_('Repository groups'),h.url('repos_groups'),class_='repos_groups')}</li>
70 <li>${h.link_to(_('Repository groups'),h.url('repos_groups'),class_='repos_groups')}</li>
71 <li>${h.link_to(_('Users'),h.url('users'),class_='users')}</li>
71 <li>${h.link_to(_('Users'),h.url('users'),class_='users')}</li>
72 <li>${h.link_to(_('User groups'),h.url('users_groups'),class_='groups')}</li>
72 <li>${h.link_to(_('User groups'),h.url('users_groups'),class_='groups')}</li>
73 <li>${h.link_to(_('Permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
73 <li>${h.link_to(_('Permissions'),h.url('edit_permission',id='default'),class_='permissions')}</li>
74 <li>${h.link_to(_('LDAP'),h.url('ldap_home'),class_='ldap')}</li>
74 <li>${h.link_to(_('LDAP'),h.url('ldap_home'),class_='ldap')}</li>
75 <li>${h.link_to(_('Defaults'),h.url('defaults'),class_='defaults')}</li>
75 <li>${h.link_to(_('Defaults'),h.url('defaults'),class_='defaults')}</li>
76 <li class="last">${h.link_to(_('Settings'),h.url('admin_settings'),class_='settings')}</li>
76 <li class="last">${h.link_to(_('Settings'),h.url('admin_settings'),class_='settings')}</li>
77 </ul>
77 </ul>
78 </%def>
78 </%def>
79
79
80 <%def name="admin_menu_simple(repository_groups=None, user_groups=None)">
80 <%def name="admin_menu_simple(repositories=None, repository_groups=None, user_groups=None)">
81 <ul>
81 <ul>
82 %if repositories:
83 <li>${h.link_to(_('Repositories'),h.url('repos'),class_='repos')}</li>
84 %endif
82 %if repository_groups:
85 %if repository_groups:
83 <li>${h.link_to(_('Repository groups'),h.url('repos_groups'),class_='repos_groups')}</li>
86 <li>${h.link_to(_('Repository groups'),h.url('repos_groups'),class_='repos_groups')}</li>
84 %endif:
87 %endif
85 %if user_groups:
88 %if user_groups:
86 <li>${h.link_to(_('User groups'),h.url('users_groups'),class_='groups')}</li>
89 <li>${h.link_to(_('User groups'),h.url('users_groups'),class_='groups')}</li>
87 %endif
90 %endif
88 </ul>
91 </ul>
89 </%def>
92 </%def>
90
93
91 <%def name="repo_context_bar(current=None)">
94 <%def name="repo_context_bar(current=None)">
92 <%
95 <%
93 def follow_class():
96 def follow_class():
94 if c.repository_following:
97 if c.repository_following:
95 return h.literal('following')
98 return h.literal('following')
96 else:
99 else:
97 return h.literal('follow')
100 return h.literal('follow')
98 %>
101 %>
99 <%
102 <%
100 def is_current(selected):
103 def is_current(selected):
101 if selected == current:
104 if selected == current:
102 return h.literal('class="current"')
105 return h.literal('class="current"')
103 %>
106 %>
104
107
105 <!--- CONTEXT BAR -->
108 <!--- CONTEXT BAR -->
106 <div id="context-bar" class="box">
109 <div id="context-bar" class="box">
107 <div id="breadcrumbs">
110 <div id="breadcrumbs">
108 ${h.link_to(_(u'Repositories'),h.url('home'))}
111 ${h.link_to(_(u'Repositories'),h.url('home'))}
109 &raquo;
112 &raquo;
110 ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)}
113 ${h.repo_link(c.rhodecode_db_repo.groups_and_repo)}
111 </div>
114 </div>
112 <ul id="context-pages" class="horizontal-list">
115 <ul id="context-pages" class="horizontal-list">
113 <li ${is_current('summary')}><a href="${h.url('summary_home', repo_name=c.repo_name)}" class="summary">${_('Summary')}</a></li>
116 <li ${is_current('summary')}><a href="${h.url('summary_home', repo_name=c.repo_name)}" class="summary">${_('Summary')}</a></li>
114 <li ${is_current('changelog')}><a href="${h.url('changelog_home', repo_name=c.repo_name)}" class="changelogs">${_('Changelog')}</a></li>
117 <li ${is_current('changelog')}><a href="${h.url('changelog_home', repo_name=c.repo_name)}" class="changelogs">${_('Changelog')}</a></li>
115 <li ${is_current('files')}><a href="${h.url('files_home', repo_name=c.repo_name)}" class="files"></span>${_('Files')}</a></li>
118 <li ${is_current('files')}><a href="${h.url('files_home', repo_name=c.repo_name)}" class="files"></span>${_('Files')}</a></li>
116 <li ${is_current('switch-to')}>
119 <li ${is_current('switch-to')}>
117 <a href="#" id="branch_tag_switcher_2" class="dropdown switch-to"></span>${_('Switch To')}</a>
120 <a href="#" id="branch_tag_switcher_2" class="dropdown switch-to"></span>${_('Switch To')}</a>
118 <ul id="switch_to_list_2" class="switch_to submenu">
121 <ul id="switch_to_list_2" class="switch_to submenu">
119 <li><a href="#">${_('loading...')}</a></li>
122 <li><a href="#">${_('loading...')}</a></li>
120 </ul>
123 </ul>
121 </li>
124 </li>
122 <li ${is_current('options')}>
125 <li ${is_current('options')}>
123 <a href="#" class="dropdown options"></span>${_('Options')}</a>
126 <a href="#" class="dropdown options"></span>${_('Options')}</a>
124 <ul>
127 <ul>
125 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
128 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
126 <li>${h.link_to(_('Settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
129 <li>${h.link_to(_('Settings'),h.url('edit_repo',repo_name=c.repo_name),class_='settings')}</li>
127 %endif
130 %endif
128 %if c.rhodecode_db_repo.fork:
131 %if c.rhodecode_db_repo.fork:
129 <li>${h.link_to(_('Compare fork'),h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default', merge=1),class_='compare_request')}</li>
132 <li>${h.link_to(_('Compare fork'),h.url('compare_url',repo_name=c.rhodecode_db_repo.fork.repo_name,org_ref_type='branch',org_ref='default',other_repo=c.repo_name,other_ref_type='branch',other_ref=request.GET.get('branch') or 'default', merge=1),class_='compare_request')}</li>
130 %endif
133 %endif
131 <li>${h.link_to(_('Search'),h.url('search_repo',repo_name=c.repo_name),class_='search')}</li>
134 <li>${h.link_to(_('Search'),h.url('search_repo',repo_name=c.repo_name),class_='search')}</li>
132
135
133 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
136 %if h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking:
134 %if c.rhodecode_db_repo.locked[0]:
137 %if c.rhodecode_db_repo.locked[0]:
135 <li>${h.link_to(_('Unlock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_del')}</li>
138 <li>${h.link_to(_('Unlock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_del')}</li>
136 %else:
139 %else:
137 <li>${h.link_to(_('Lock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_add')}</li>
140 <li>${h.link_to(_('Lock'), h.url('toggle_locking',repo_name=c.repo_name),class_='locking_add')}</li>
138 %endif
141 %endif
139 %endif
142 %endif
140 ## TODO: this check feels wrong, it would be better to have a check for permissions
143 ## TODO: this check feels wrong, it would be better to have a check for permissions
141 ## also it feels like a job for the controller
144 ## also it feels like a job for the controller
142 %if c.rhodecode_user.username != 'default':
145 %if c.rhodecode_user.username != 'default':
143 <li>
146 <li>
144 <a class="${follow_class()}" onclick="javascript:toggleFollowingRepo(this,${c.rhodecode_db_repo.repo_id},'${str(h.get_token())}');">
147 <a class="${follow_class()}" onclick="javascript:toggleFollowingRepo(this,${c.rhodecode_db_repo.repo_id},'${str(h.get_token())}');">
145 <span class="show-follow">${_('Follow')}</span>
148 <span class="show-follow">${_('Follow')}</span>
146 <span class="show-following">${_('Unfollow')}</span>
149 <span class="show-following">${_('Unfollow')}</span>
147 </a>
150 </a>
148 </li>
151 </li>
149 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}" class="fork">${_('Fork')}</a></li>
152 <li><a href="${h.url('repo_fork_home',repo_name=c.repo_name)}" class="fork">${_('Fork')}</a></li>
150 %if h.is_hg(c.rhodecode_repo):
153 %if h.is_hg(c.rhodecode_repo):
151 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="pull-request">${_('Create Pull Request')}</a></li>
154 <li><a href="${h.url('pullrequest_home',repo_name=c.repo_name)}" class="pull-request">${_('Create Pull Request')}</a></li>
152 %endif
155 %endif
153 %endif
156 %endif
154 </ul>
157 </ul>
155 </li>
158 </li>
156 <li ${is_current('showpullrequest')}>
159 <li ${is_current('showpullrequest')}>
157 <a href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests')}" class="pull-request">${_('Pull Requests')}
160 <a href="${h.url('pullrequest_show_all',repo_name=c.repo_name)}" title="${_('Show Pull Requests')}" class="pull-request">${_('Pull Requests')}
158 %if c.repository_pull_requests:
161 %if c.repository_pull_requests:
159 <span>${c.repository_pull_requests}</span>
162 <span>${c.repository_pull_requests}</span>
160 %endif
163 %endif
161 </a>
164 </a>
162 </li>
165 </li>
163 </ul>
166 </ul>
164 </div>
167 </div>
165 <script type="text/javascript">
168 <script type="text/javascript">
166 YUE.on('branch_tag_switcher_2','mouseover',function(){
169 YUE.on('branch_tag_switcher_2','mouseover',function(){
167 var loaded = YUD.hasClass('branch_tag_switcher_2','loaded');
170 var loaded = YUD.hasClass('branch_tag_switcher_2','loaded');
168 if(!loaded){
171 if(!loaded){
169 YUD.addClass('branch_tag_switcher_2','loaded');
172 YUD.addClass('branch_tag_switcher_2','loaded');
170 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list_2',
173 ypjax("${h.url('branch_tag_switcher',repo_name=c.repo_name)}",'switch_to_list_2',
171 function(o){},
174 function(o){},
172 function(o){YUD.removeClass('branch_tag_switcher_2','loaded');}
175 function(o){YUD.removeClass('branch_tag_switcher_2','loaded');}
173 ,null);
176 ,null);
174 }
177 }
175 return false;
178 return false;
176 });
179 });
177 </script>
180 </script>
178 <!--- END CONTEXT BAR -->
181 <!--- END CONTEXT BAR -->
179 </%def>
182 </%def>
180
183
181 <%def name="usermenu()">
184 <%def name="usermenu()">
182 ## USER MENU
185 ## USER MENU
183 <li>
186 <li>
184 <a class="menu_link childs" id="quick_login_link">
187 <a class="menu_link childs" id="quick_login_link">
185 <span class="icon">
188 <span class="icon">
186 <img src="${h.gravatar_url(c.rhodecode_user.email,20)}" alt="avatar">
189 <img src="${h.gravatar_url(c.rhodecode_user.email,20)}" alt="avatar">
187 </span>
190 </span>
188 %if c.rhodecode_user.username != 'default':
191 %if c.rhodecode_user.username != 'default':
189 <span class="menu_link_user">${c.rhodecode_user.username}</span>
192 <span class="menu_link_user">${c.rhodecode_user.username}</span>
190 %if c.unread_notifications != 0:
193 %if c.unread_notifications != 0:
191 <span class="menu_link_notifications">${c.unread_notifications}</span>
194 <span class="menu_link_notifications">${c.unread_notifications}</span>
192 %endif
195 %endif
193 %else:
196 %else:
194 <span>${_('Not logged in')}</span>
197 <span>${_('Not logged in')}</span>
195 %endif
198 %endif
196 </a>
199 </a>
197
200
198 <div class="user-menu">
201 <div class="user-menu">
199 <div id="quick_login">
202 <div id="quick_login">
200 %if c.rhodecode_user.username == 'default':
203 %if c.rhodecode_user.username == 'default':
201 <h4>${_('Login to your account')}</h4>
204 <h4>${_('Login to your account')}</h4>
202 ${h.form(h.url('login_home',came_from=h.url.current()))}
205 ${h.form(h.url('login_home',came_from=h.url.current()))}
203 <div class="form">
206 <div class="form">
204 <div class="fields">
207 <div class="fields">
205 <div class="field">
208 <div class="field">
206 <div class="label">
209 <div class="label">
207 <label for="username">${_('Username')}:</label>
210 <label for="username">${_('Username')}:</label>
208 </div>
211 </div>
209 <div class="input">
212 <div class="input">
210 ${h.text('username',class_='focus')}
213 ${h.text('username',class_='focus')}
211 </div>
214 </div>
212
215
213 </div>
216 </div>
214 <div class="field">
217 <div class="field">
215 <div class="label">
218 <div class="label">
216 <label for="password">${_('Password')}:</label>
219 <label for="password">${_('Password')}:</label>
217 </div>
220 </div>
218 <div class="input">
221 <div class="input">
219 ${h.password('password',class_='focus')}
222 ${h.password('password',class_='focus')}
220 </div>
223 </div>
221
224
222 </div>
225 </div>
223 <div class="buttons">
226 <div class="buttons">
224 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
227 <div class="password_forgoten">${h.link_to(_('Forgot password ?'),h.url('reset_password'))}</div>
225 <div class="register">
228 <div class="register">
226 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
229 %if h.HasPermissionAny('hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')():
227 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
230 ${h.link_to(_("Don't have an account ?"),h.url('register'))}
228 %endif
231 %endif
229 </div>
232 </div>
230 <div class="submit">
233 <div class="submit">
231 ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")}
234 ${h.submit('sign_in',_('Log In'),class_="ui-btn xsmall")}
232 </div>
235 </div>
233 </div>
236 </div>
234 </div>
237 </div>
235 </div>
238 </div>
236 ${h.end_form()}
239 ${h.end_form()}
237 %else:
240 %else:
238 <div class="links_left">
241 <div class="links_left">
239 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
242 <div class="big_gravatar"><img alt="gravatar" src="${h.gravatar_url(c.rhodecode_user.email,48)}" /></div>
240 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
243 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
241 <div class="email">${c.rhodecode_user.email}</div>
244 <div class="email">${c.rhodecode_user.email}</div>
242 </div>
245 </div>
243 <div class="links_right">
246 <div class="links_right">
244 <ol class="links">
247 <ol class="links">
245 <li><a href="${h.url('notifications')}">${_('Notifications')}: ${c.unread_notifications}</a></li>
248 <li><a href="${h.url('notifications')}">${_('Notifications')}: ${c.unread_notifications}</a></li>
246 <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li>
249 <li>${h.link_to(_(u'My account'),h.url('admin_settings_my_account'))}</li>
247 <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
250 <li class="logout">${h.link_to(_(u'Log Out'),h.url('logout_home'))}</li>
248 </ol>
251 </ol>
249 </div>
252 </div>
250 %endif
253 %endif
251 </div>
254 </div>
252 </div>
255 </div>
253
256
254 </li>
257 </li>
255 </%def>
258 </%def>
256
259
257 <%def name="menu(current=None)">
260 <%def name="menu(current=None)">
258 <%
261 <%
259 def is_current(selected):
262 def is_current(selected):
260 if selected == current:
263 if selected == current:
261 return h.literal('class="current"')
264 return h.literal('class="current"')
262 %>
265 %>
263 <ul id="quick" class="horizontal-list">
266 <ul id="quick" class="horizontal-list">
264 <!-- repo switcher -->
267 <!-- repo switcher -->
265 <li ${is_current('repositories')}>
268 <li ${is_current('repositories')}>
266 <a class="menu_link repo_switcher childs" id="repo_switcher" title="${_('Switch repository')}" href="${h.url('home')}">
269 <a class="menu_link repo_switcher childs" id="repo_switcher" title="${_('Switch repository')}" href="${h.url('home')}">
267 ${_('Repositories')}
270 ${_('Repositories')}
268 </a>
271 </a>
269 <ul id="repo_switcher_list" class="repo_switcher">
272 <ul id="repo_switcher_list" class="repo_switcher">
270 <li>
273 <li>
271 <a href="#">${_('loading...')}</a>
274 <a href="#">${_('loading...')}</a>
272 </li>
275 </li>
273 </ul>
276 </ul>
274 </li>
277 </li>
275 ##ROOT MENU
278 ##ROOT MENU
276 %if c.rhodecode_user.username != 'default':
279 %if c.rhodecode_user.username != 'default':
277 <li ${is_current('journal')}>
280 <li ${is_current('journal')}>
278 <a class="menu_link journal" title="${_('Show recent activity')}" href="${h.url('journal')}">
281 <a class="menu_link journal" title="${_('Show recent activity')}" href="${h.url('journal')}">
279 ${_('Journal')}
282 ${_('Journal')}
280 </a>
283 </a>
281 </li>
284 </li>
282 %else:
285 %else:
283 <li ${is_current('journal')}>
286 <li ${is_current('journal')}>
284 <a class="menu_link journal" title="${_('Public journal')}" href="${h.url('public_journal')}">
287 <a class="menu_link journal" title="${_('Public journal')}" href="${h.url('public_journal')}">
285 ${_('Public journal')}
288 ${_('Public journal')}
286 </a>
289 </a>
287 </li>
290 </li>
288 %endif
291 %endif
289 <li ${is_current('gists')}>
292 <li ${is_current('gists')}>
290 <a class="menu_link gists childs" title="${_('Show public gists')}" href="${h.url('gists')}">
293 <a class="menu_link gists childs" title="${_('Show public gists')}" href="${h.url('gists')}">
291 ${_('Gists')}
294 ${_('Gists')}
292 </a>
295 </a>
293 <ul class="admin_menu">
296 <ul class="admin_menu">
294 <li>${h.link_to(_('Create new gist'),h.url('new_gist'),class_='gists-new ')}</li>
297 <li>${h.link_to(_('Create new gist'),h.url('new_gist'),class_='gists-new ')}</li>
295 <li>${h.link_to(_('All public gists'),h.url('gists'),class_='gists ')}</li>
298 <li>${h.link_to(_('All public gists'),h.url('gists'),class_='gists ')}</li>
296 %if c.rhodecode_user.username != 'default':
299 %if c.rhodecode_user.username != 'default':
297 <li>${h.link_to(_('My public gists'),h.url('gists', public=1),class_='gists')}</li>
300 <li>${h.link_to(_('My public gists'),h.url('gists', public=1),class_='gists')}</li>
298 <li>${h.link_to(_('My private gists'),h.url('gists', private=1),class_='gists-private ')}</li>
301 <li>${h.link_to(_('My private gists'),h.url('gists', private=1),class_='gists-private ')}</li>
299 %endif
302 %endif
300 </ul>
303 </ul>
301 </li>
304 </li>
302 <li ${is_current('search')}>
305 <li ${is_current('search')}>
303 <a class="menu_link search" title="${_('Search in repositories')}" href="${h.url('search')}">
306 <a class="menu_link search" title="${_('Search in repositories')}" href="${h.url('search')}">
304 ${_('Search')}
307 ${_('Search')}
305 </a>
308 </a>
306 </li>
309 </li>
307 % if h.HasPermissionAll('hg.admin')('access admin main page'):
310 % if h.HasPermissionAll('hg.admin')('access admin main page'):
308 <li ${is_current('admin')}>
311 <li ${is_current('admin')}>
309 <a class="menu_link admin childs" title="${_('Admin')}" href="${h.url('admin_home')}">
312 <a class="menu_link admin childs" title="${_('Admin')}" href="${h.url('admin_home')}">
310 ${_('Admin')}
313 ${_('Admin')}
311 </a>
314 </a>
312 ${admin_menu()}
315 ${admin_menu()}
313 </li>
316 </li>
314 % elif c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
317 % elif c.rhodecode_user.repositories_admin or c.rhodecode_user.repository_groups_admin or c.rhodecode_user.user_groups_admin:
315 <li ${is_current('admin')}>
318 <li ${is_current('admin')}>
316 <a class="menu_link admin childs" title="${_('Admin')}">
319 <a class="menu_link admin childs" title="${_('Admin')}">
317 ${_('Admin')}
320 ${_('Admin')}
318 </a>
321 </a>
319 ${admin_menu_simple(c.rhodecode_user.repository_groups_admin,
322 ${admin_menu_simple(c.rhodecode_user.repositories_admin,
323 c.rhodecode_user.repository_groups_admin,
320 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
324 c.rhodecode_user.user_groups_admin or h.HasPermissionAny('hg.usergroup.create.true')())}
321 </li>
325 </li>
322 % endif
326 % endif
323 ${usermenu()}
327 ${usermenu()}
324 <script type="text/javascript">
328 <script type="text/javascript">
325 YUE.on('repo_switcher','mouseover',function(){
329 YUE.on('repo_switcher','mouseover',function(){
326 var target = 'q_filter_rs';
330 var target = 'q_filter_rs';
327 var qfilter_activate = function(){
331 var qfilter_activate = function(){
328 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
332 var nodes = YUQ('ul#repo_switcher_list li a.repo_name');
329 var func = function(node){
333 var func = function(node){
330 return node.parentNode;
334 return node.parentNode;
331 }
335 }
332 q_filter(target,nodes,func);
336 q_filter(target,nodes,func);
333 }
337 }
334
338
335 var loaded = YUD.hasClass('repo_switcher','loaded');
339 var loaded = YUD.hasClass('repo_switcher','loaded');
336 if(!loaded){
340 if(!loaded){
337 YUD.addClass('repo_switcher','loaded');
341 YUD.addClass('repo_switcher','loaded');
338 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
342 ypjax("${h.url('repo_switcher')}",'repo_switcher_list',
339 function(o){qfilter_activate();YUD.get(target).focus()},
343 function(o){qfilter_activate();YUD.get(target).focus()},
340 function(o){YUD.removeClass('repo_switcher','loaded');}
344 function(o){YUD.removeClass('repo_switcher','loaded');}
341 ,null);
345 ,null);
342 }else{
346 }else{
343 YUD.get(target).focus();
347 YUD.get(target).focus();
344 }
348 }
345 return false;
349 return false;
346 });
350 });
347
351
348 YUE.on('header-dd', 'click',function(e){
352 YUE.on('header-dd', 'click',function(e){
349 YUD.addClass('header-inner', 'hover');
353 YUD.addClass('header-inner', 'hover');
350 YUD.addClass('content', 'hover');
354 YUD.addClass('content', 'hover');
351 });
355 });
352
356
353 </script>
357 </script>
354 </%def>
358 </%def>
General Comments 0
You need to be logged in to leave comments. Login now