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