##// END OF EJS Templates
permissions: fixed a case when a duplicate permission was making archive operation still display settings.
marcink -
r4414:92157ec6 default
parent child Browse files
Show More
@@ -1,2502 +1,2511 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 """
21 """
22 authentication and permission libraries
22 authentication and permission libraries
23 """
23 """
24
24
25 import os
25 import os
26
26
27 import colander
27 import colander
28 import time
28 import time
29 import collections
29 import collections
30 import fnmatch
30 import fnmatch
31 import hashlib
31 import hashlib
32 import itertools
32 import itertools
33 import logging
33 import logging
34 import random
34 import random
35 import traceback
35 import traceback
36 from functools import wraps
36 from functools import wraps
37
37
38 import ipaddress
38 import ipaddress
39
39
40 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
40 from pyramid.httpexceptions import HTTPForbidden, HTTPFound, HTTPNotFound
41 from sqlalchemy.orm.exc import ObjectDeletedError
41 from sqlalchemy.orm.exc import ObjectDeletedError
42 from sqlalchemy.orm import joinedload
42 from sqlalchemy.orm import joinedload
43 from zope.cachedescriptors.property import Lazy as LazyProperty
43 from zope.cachedescriptors.property import Lazy as LazyProperty
44
44
45 import rhodecode
45 import rhodecode
46 from rhodecode.model import meta
46 from rhodecode.model import meta
47 from rhodecode.model.meta import Session
47 from rhodecode.model.meta import Session
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49 from rhodecode.model.db import (
49 from rhodecode.model.db import (
50 false, User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
50 false, User, Repository, Permission, UserToPerm, UserGroupToPerm, UserGroupMember,
51 UserIpMap, UserApiKeys, RepoGroup, UserGroup, UserNotice)
51 UserIpMap, UserApiKeys, RepoGroup, UserGroup, UserNotice)
52 from rhodecode.lib import rc_cache
52 from rhodecode.lib import rc_cache
53 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
53 from rhodecode.lib.utils2 import safe_unicode, aslist, safe_str, md5, safe_int, sha1
54 from rhodecode.lib.utils import (
54 from rhodecode.lib.utils import (
55 get_repo_slug, get_repo_group_slug, get_user_group_slug)
55 get_repo_slug, get_repo_group_slug, get_user_group_slug)
56 from rhodecode.lib.caching_query import FromCache
56 from rhodecode.lib.caching_query import FromCache
57
57
58 if rhodecode.is_unix:
58 if rhodecode.is_unix:
59 import bcrypt
59 import bcrypt
60
60
61 log = logging.getLogger(__name__)
61 log = logging.getLogger(__name__)
62
62
63 csrf_token_key = "csrf_token"
63 csrf_token_key = "csrf_token"
64
64
65
65
66 class PasswordGenerator(object):
66 class PasswordGenerator(object):
67 """
67 """
68 This is a simple class for generating password from different sets of
68 This is a simple class for generating password from different sets of
69 characters
69 characters
70 usage::
70 usage::
71 passwd_gen = PasswordGenerator()
71 passwd_gen = PasswordGenerator()
72 #print 8-letter password containing only big and small letters
72 #print 8-letter password containing only big and small letters
73 of alphabet
73 of alphabet
74 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
74 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
75 """
75 """
76 ALPHABETS_NUM = r'''1234567890'''
76 ALPHABETS_NUM = r'''1234567890'''
77 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
77 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
78 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
78 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
79 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
79 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
80 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
80 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
81 + ALPHABETS_NUM + ALPHABETS_SPECIAL
81 + ALPHABETS_NUM + ALPHABETS_SPECIAL
82 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
82 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
83 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
83 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
84 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
84 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
85 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
85 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
86
86
87 def __init__(self, passwd=''):
87 def __init__(self, passwd=''):
88 self.passwd = passwd
88 self.passwd = passwd
89
89
90 def gen_password(self, length, type_=None):
90 def gen_password(self, length, type_=None):
91 if type_ is None:
91 if type_ is None:
92 type_ = self.ALPHABETS_FULL
92 type_ = self.ALPHABETS_FULL
93 self.passwd = ''.join([random.choice(type_) for _ in range(length)])
93 self.passwd = ''.join([random.choice(type_) for _ in range(length)])
94 return self.passwd
94 return self.passwd
95
95
96
96
97 class _RhodeCodeCryptoBase(object):
97 class _RhodeCodeCryptoBase(object):
98 ENC_PREF = None
98 ENC_PREF = None
99
99
100 def hash_create(self, str_):
100 def hash_create(self, str_):
101 """
101 """
102 hash the string using
102 hash the string using
103
103
104 :param str_: password to hash
104 :param str_: password to hash
105 """
105 """
106 raise NotImplementedError
106 raise NotImplementedError
107
107
108 def hash_check_with_upgrade(self, password, hashed):
108 def hash_check_with_upgrade(self, password, hashed):
109 """
109 """
110 Returns tuple in which first element is boolean that states that
110 Returns tuple in which first element is boolean that states that
111 given password matches it's hashed version, and the second is new hash
111 given password matches it's hashed version, and the second is new hash
112 of the password, in case this password should be migrated to new
112 of the password, in case this password should be migrated to new
113 cipher.
113 cipher.
114 """
114 """
115 checked_hash = self.hash_check(password, hashed)
115 checked_hash = self.hash_check(password, hashed)
116 return checked_hash, None
116 return checked_hash, None
117
117
118 def hash_check(self, password, hashed):
118 def hash_check(self, password, hashed):
119 """
119 """
120 Checks matching password with it's hashed value.
120 Checks matching password with it's hashed value.
121
121
122 :param password: password
122 :param password: password
123 :param hashed: password in hashed form
123 :param hashed: password in hashed form
124 """
124 """
125 raise NotImplementedError
125 raise NotImplementedError
126
126
127 def _assert_bytes(self, value):
127 def _assert_bytes(self, value):
128 """
128 """
129 Passing in an `unicode` object can lead to hard to detect issues
129 Passing in an `unicode` object can lead to hard to detect issues
130 if passwords contain non-ascii characters. Doing a type check
130 if passwords contain non-ascii characters. Doing a type check
131 during runtime, so that such mistakes are detected early on.
131 during runtime, so that such mistakes are detected early on.
132 """
132 """
133 if not isinstance(value, str):
133 if not isinstance(value, str):
134 raise TypeError(
134 raise TypeError(
135 "Bytestring required as input, got %r." % (value, ))
135 "Bytestring required as input, got %r." % (value, ))
136
136
137
137
138 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
138 class _RhodeCodeCryptoBCrypt(_RhodeCodeCryptoBase):
139 ENC_PREF = ('$2a$10', '$2b$10')
139 ENC_PREF = ('$2a$10', '$2b$10')
140
140
141 def hash_create(self, str_):
141 def hash_create(self, str_):
142 self._assert_bytes(str_)
142 self._assert_bytes(str_)
143 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
143 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
144
144
145 def hash_check_with_upgrade(self, password, hashed):
145 def hash_check_with_upgrade(self, password, hashed):
146 """
146 """
147 Returns tuple in which first element is boolean that states that
147 Returns tuple in which first element is boolean that states that
148 given password matches it's hashed version, and the second is new hash
148 given password matches it's hashed version, and the second is new hash
149 of the password, in case this password should be migrated to new
149 of the password, in case this password should be migrated to new
150 cipher.
150 cipher.
151
151
152 This implements special upgrade logic which works like that:
152 This implements special upgrade logic which works like that:
153 - check if the given password == bcrypted hash, if yes then we
153 - check if the given password == bcrypted hash, if yes then we
154 properly used password and it was already in bcrypt. Proceed
154 properly used password and it was already in bcrypt. Proceed
155 without any changes
155 without any changes
156 - if bcrypt hash check is not working try with sha256. If hash compare
156 - if bcrypt hash check is not working try with sha256. If hash compare
157 is ok, it means we using correct but old hashed password. indicate
157 is ok, it means we using correct but old hashed password. indicate
158 hash change and proceed
158 hash change and proceed
159 """
159 """
160
160
161 new_hash = None
161 new_hash = None
162
162
163 # regular pw check
163 # regular pw check
164 password_match_bcrypt = self.hash_check(password, hashed)
164 password_match_bcrypt = self.hash_check(password, hashed)
165
165
166 # now we want to know if the password was maybe from sha256
166 # now we want to know if the password was maybe from sha256
167 # basically calling _RhodeCodeCryptoSha256().hash_check()
167 # basically calling _RhodeCodeCryptoSha256().hash_check()
168 if not password_match_bcrypt:
168 if not password_match_bcrypt:
169 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
169 if _RhodeCodeCryptoSha256().hash_check(password, hashed):
170 new_hash = self.hash_create(password) # make new bcrypt hash
170 new_hash = self.hash_create(password) # make new bcrypt hash
171 password_match_bcrypt = True
171 password_match_bcrypt = True
172
172
173 return password_match_bcrypt, new_hash
173 return password_match_bcrypt, new_hash
174
174
175 def hash_check(self, password, hashed):
175 def hash_check(self, password, hashed):
176 """
176 """
177 Checks matching password with it's hashed value.
177 Checks matching password with it's hashed value.
178
178
179 :param password: password
179 :param password: password
180 :param hashed: password in hashed form
180 :param hashed: password in hashed form
181 """
181 """
182 self._assert_bytes(password)
182 self._assert_bytes(password)
183 try:
183 try:
184 return bcrypt.hashpw(password, hashed) == hashed
184 return bcrypt.hashpw(password, hashed) == hashed
185 except ValueError as e:
185 except ValueError as e:
186 # we're having a invalid salt here probably, we should not crash
186 # we're having a invalid salt here probably, we should not crash
187 # just return with False as it would be a wrong password.
187 # just return with False as it would be a wrong password.
188 log.debug('Failed to check password hash using bcrypt %s',
188 log.debug('Failed to check password hash using bcrypt %s',
189 safe_str(e))
189 safe_str(e))
190
190
191 return False
191 return False
192
192
193
193
194 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
194 class _RhodeCodeCryptoSha256(_RhodeCodeCryptoBase):
195 ENC_PREF = '_'
195 ENC_PREF = '_'
196
196
197 def hash_create(self, str_):
197 def hash_create(self, str_):
198 self._assert_bytes(str_)
198 self._assert_bytes(str_)
199 return hashlib.sha256(str_).hexdigest()
199 return hashlib.sha256(str_).hexdigest()
200
200
201 def hash_check(self, password, hashed):
201 def hash_check(self, password, hashed):
202 """
202 """
203 Checks matching password with it's hashed value.
203 Checks matching password with it's hashed value.
204
204
205 :param password: password
205 :param password: password
206 :param hashed: password in hashed form
206 :param hashed: password in hashed form
207 """
207 """
208 self._assert_bytes(password)
208 self._assert_bytes(password)
209 return hashlib.sha256(password).hexdigest() == hashed
209 return hashlib.sha256(password).hexdigest() == hashed
210
210
211
211
212 class _RhodeCodeCryptoTest(_RhodeCodeCryptoBase):
212 class _RhodeCodeCryptoTest(_RhodeCodeCryptoBase):
213 ENC_PREF = '_'
213 ENC_PREF = '_'
214
214
215 def hash_create(self, str_):
215 def hash_create(self, str_):
216 self._assert_bytes(str_)
216 self._assert_bytes(str_)
217 return sha1(str_)
217 return sha1(str_)
218
218
219 def hash_check(self, password, hashed):
219 def hash_check(self, password, hashed):
220 """
220 """
221 Checks matching password with it's hashed value.
221 Checks matching password with it's hashed value.
222
222
223 :param password: password
223 :param password: password
224 :param hashed: password in hashed form
224 :param hashed: password in hashed form
225 """
225 """
226 self._assert_bytes(password)
226 self._assert_bytes(password)
227 return sha1(password) == hashed
227 return sha1(password) == hashed
228
228
229
229
230 def crypto_backend():
230 def crypto_backend():
231 """
231 """
232 Return the matching crypto backend.
232 Return the matching crypto backend.
233
233
234 Selection is based on if we run tests or not, we pick sha1-test backend to run
234 Selection is based on if we run tests or not, we pick sha1-test backend to run
235 tests faster since BCRYPT is expensive to calculate
235 tests faster since BCRYPT is expensive to calculate
236 """
236 """
237 if rhodecode.is_test:
237 if rhodecode.is_test:
238 RhodeCodeCrypto = _RhodeCodeCryptoTest()
238 RhodeCodeCrypto = _RhodeCodeCryptoTest()
239 else:
239 else:
240 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
240 RhodeCodeCrypto = _RhodeCodeCryptoBCrypt()
241
241
242 return RhodeCodeCrypto
242 return RhodeCodeCrypto
243
243
244
244
245 def get_crypt_password(password):
245 def get_crypt_password(password):
246 """
246 """
247 Create the hash of `password` with the active crypto backend.
247 Create the hash of `password` with the active crypto backend.
248
248
249 :param password: The cleartext password.
249 :param password: The cleartext password.
250 :type password: unicode
250 :type password: unicode
251 """
251 """
252 password = safe_str(password)
252 password = safe_str(password)
253 return crypto_backend().hash_create(password)
253 return crypto_backend().hash_create(password)
254
254
255
255
256 def check_password(password, hashed):
256 def check_password(password, hashed):
257 """
257 """
258 Check if the value in `password` matches the hash in `hashed`.
258 Check if the value in `password` matches the hash in `hashed`.
259
259
260 :param password: The cleartext password.
260 :param password: The cleartext password.
261 :type password: unicode
261 :type password: unicode
262
262
263 :param hashed: The expected hashed version of the password.
263 :param hashed: The expected hashed version of the password.
264 :type hashed: The hash has to be passed in in text representation.
264 :type hashed: The hash has to be passed in in text representation.
265 """
265 """
266 password = safe_str(password)
266 password = safe_str(password)
267 return crypto_backend().hash_check(password, hashed)
267 return crypto_backend().hash_check(password, hashed)
268
268
269
269
270 def generate_auth_token(data, salt=None):
270 def generate_auth_token(data, salt=None):
271 """
271 """
272 Generates API KEY from given string
272 Generates API KEY from given string
273 """
273 """
274
274
275 if salt is None:
275 if salt is None:
276 salt = os.urandom(16)
276 salt = os.urandom(16)
277 return hashlib.sha1(safe_str(data) + salt).hexdigest()
277 return hashlib.sha1(safe_str(data) + salt).hexdigest()
278
278
279
279
280 def get_came_from(request):
280 def get_came_from(request):
281 """
281 """
282 get query_string+path from request sanitized after removing auth_token
282 get query_string+path from request sanitized after removing auth_token
283 """
283 """
284 _req = request
284 _req = request
285
285
286 path = _req.path
286 path = _req.path
287 if 'auth_token' in _req.GET:
287 if 'auth_token' in _req.GET:
288 # sanitize the request and remove auth_token for redirection
288 # sanitize the request and remove auth_token for redirection
289 _req.GET.pop('auth_token')
289 _req.GET.pop('auth_token')
290 qs = _req.query_string
290 qs = _req.query_string
291 if qs:
291 if qs:
292 path += '?' + qs
292 path += '?' + qs
293
293
294 return path
294 return path
295
295
296
296
297 class CookieStoreWrapper(object):
297 class CookieStoreWrapper(object):
298
298
299 def __init__(self, cookie_store):
299 def __init__(self, cookie_store):
300 self.cookie_store = cookie_store
300 self.cookie_store = cookie_store
301
301
302 def __repr__(self):
302 def __repr__(self):
303 return 'CookieStore<%s>' % (self.cookie_store)
303 return 'CookieStore<%s>' % (self.cookie_store)
304
304
305 def get(self, key, other=None):
305 def get(self, key, other=None):
306 if isinstance(self.cookie_store, dict):
306 if isinstance(self.cookie_store, dict):
307 return self.cookie_store.get(key, other)
307 return self.cookie_store.get(key, other)
308 elif isinstance(self.cookie_store, AuthUser):
308 elif isinstance(self.cookie_store, AuthUser):
309 return self.cookie_store.__dict__.get(key, other)
309 return self.cookie_store.__dict__.get(key, other)
310
310
311
311
312 def _cached_perms_data(user_id, scope, user_is_admin,
312 def _cached_perms_data(user_id, scope, user_is_admin,
313 user_inherit_default_permissions, explicit, algo,
313 user_inherit_default_permissions, explicit, algo,
314 calculate_super_admin):
314 calculate_super_admin):
315
315
316 permissions = PermissionCalculator(
316 permissions = PermissionCalculator(
317 user_id, scope, user_is_admin, user_inherit_default_permissions,
317 user_id, scope, user_is_admin, user_inherit_default_permissions,
318 explicit, algo, calculate_super_admin)
318 explicit, algo, calculate_super_admin)
319 return permissions.calculate()
319 return permissions.calculate()
320
320
321
321
322 class PermOrigin(object):
322 class PermOrigin(object):
323 SUPER_ADMIN = 'superadmin'
323 SUPER_ADMIN = 'superadmin'
324 ARCHIVED = 'archived'
324 ARCHIVED = 'archived'
325
325
326 REPO_USER = 'user:%s'
326 REPO_USER = 'user:%s'
327 REPO_USERGROUP = 'usergroup:%s'
327 REPO_USERGROUP = 'usergroup:%s'
328 REPO_OWNER = 'repo.owner'
328 REPO_OWNER = 'repo.owner'
329 REPO_DEFAULT = 'repo.default'
329 REPO_DEFAULT = 'repo.default'
330 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
330 REPO_DEFAULT_NO_INHERIT = 'repo.default.no.inherit'
331 REPO_PRIVATE = 'repo.private'
331 REPO_PRIVATE = 'repo.private'
332
332
333 REPOGROUP_USER = 'user:%s'
333 REPOGROUP_USER = 'user:%s'
334 REPOGROUP_USERGROUP = 'usergroup:%s'
334 REPOGROUP_USERGROUP = 'usergroup:%s'
335 REPOGROUP_OWNER = 'group.owner'
335 REPOGROUP_OWNER = 'group.owner'
336 REPOGROUP_DEFAULT = 'group.default'
336 REPOGROUP_DEFAULT = 'group.default'
337 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
337 REPOGROUP_DEFAULT_NO_INHERIT = 'group.default.no.inherit'
338
338
339 USERGROUP_USER = 'user:%s'
339 USERGROUP_USER = 'user:%s'
340 USERGROUP_USERGROUP = 'usergroup:%s'
340 USERGROUP_USERGROUP = 'usergroup:%s'
341 USERGROUP_OWNER = 'usergroup.owner'
341 USERGROUP_OWNER = 'usergroup.owner'
342 USERGROUP_DEFAULT = 'usergroup.default'
342 USERGROUP_DEFAULT = 'usergroup.default'
343 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
343 USERGROUP_DEFAULT_NO_INHERIT = 'usergroup.default.no.inherit'
344
344
345
345
346 class PermOriginDict(dict):
346 class PermOriginDict(dict):
347 """
347 """
348 A special dict used for tracking permissions along with their origins.
348 A special dict used for tracking permissions along with their origins.
349
349
350 `__setitem__` has been overridden to expect a tuple(perm, origin)
350 `__setitem__` has been overridden to expect a tuple(perm, origin)
351 `__getitem__` will return only the perm
351 `__getitem__` will return only the perm
352 `.perm_origin_stack` will return the stack of (perm, origin) set per key
352 `.perm_origin_stack` will return the stack of (perm, origin) set per key
353
353
354 >>> perms = PermOriginDict()
354 >>> perms = PermOriginDict()
355 >>> perms['resource'] = 'read', 'default', 1
355 >>> perms['resource'] = 'read', 'default', 1
356 >>> perms['resource']
356 >>> perms['resource']
357 'read'
357 'read'
358 >>> perms['resource'] = 'write', 'admin', 2
358 >>> perms['resource'] = 'write', 'admin', 2
359 >>> perms['resource']
359 >>> perms['resource']
360 'write'
360 'write'
361 >>> perms.perm_origin_stack
361 >>> perms.perm_origin_stack
362 {'resource': [('read', 'default', 1), ('write', 'admin', 2)]}
362 {'resource': [('read', 'default', 1), ('write', 'admin', 2)]}
363 """
363 """
364
364
365 def __init__(self, *args, **kw):
365 def __init__(self, *args, **kw):
366 dict.__init__(self, *args, **kw)
366 dict.__init__(self, *args, **kw)
367 self.perm_origin_stack = collections.OrderedDict()
367 self.perm_origin_stack = collections.OrderedDict()
368
368
369 def __setitem__(self, key, (perm, origin, obj_id)):
369 def __setitem__(self, key, (perm, origin, obj_id)):
370 self.perm_origin_stack.setdefault(key, []).append(
370 self.perm_origin_stack.setdefault(key, []).append((perm, origin, obj_id))
371 (perm, origin, obj_id))
372 dict.__setitem__(self, key, perm)
371 dict.__setitem__(self, key, perm)
373
372
374
373
375 class BranchPermOriginDict(PermOriginDict):
374 class BranchPermOriginDict(PermOriginDict):
376 """
375 """
377 Dedicated branch permissions dict, with tracking of patterns and origins.
376 Dedicated branch permissions dict, with tracking of patterns and origins.
378
377
379 >>> perms = BranchPermOriginDict()
378 >>> perms = BranchPermOriginDict()
380 >>> perms['resource'] = '*pattern', 'read', 'default'
379 >>> perms['resource'] = '*pattern', 'read', 'default'
381 >>> perms['resource']
380 >>> perms['resource']
382 {'*pattern': 'read'}
381 {'*pattern': 'read'}
383 >>> perms['resource'] = '*pattern', 'write', 'admin'
382 >>> perms['resource'] = '*pattern', 'write', 'admin'
384 >>> perms['resource']
383 >>> perms['resource']
385 {'*pattern': 'write'}
384 {'*pattern': 'write'}
386 >>> perms.perm_origin_stack
385 >>> perms.perm_origin_stack
387 {'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
386 {'resource': {'*pattern': [('read', 'default'), ('write', 'admin')]}}
388 """
387 """
389 def __setitem__(self, key, (pattern, perm, origin)):
388 def __setitem__(self, key, (pattern, perm, origin)):
390
389
391 self.perm_origin_stack.setdefault(key, {}) \
390 self.perm_origin_stack.setdefault(key, {}) \
392 .setdefault(pattern, []).append((perm, origin))
391 .setdefault(pattern, []).append((perm, origin))
393
392
394 if key in self:
393 if key in self:
395 self[key].__setitem__(pattern, perm)
394 self[key].__setitem__(pattern, perm)
396 else:
395 else:
397 patterns = collections.OrderedDict()
396 patterns = collections.OrderedDict()
398 patterns[pattern] = perm
397 patterns[pattern] = perm
399 dict.__setitem__(self, key, patterns)
398 dict.__setitem__(self, key, patterns)
400
399
401
400
402 class PermissionCalculator(object):
401 class PermissionCalculator(object):
403
402
404 def __init__(
403 def __init__(
405 self, user_id, scope, user_is_admin,
404 self, user_id, scope, user_is_admin,
406 user_inherit_default_permissions, explicit, algo,
405 user_inherit_default_permissions, explicit, algo,
407 calculate_super_admin_as_user=False):
406 calculate_super_admin_as_user=False):
408
407
409 self.user_id = user_id
408 self.user_id = user_id
410 self.user_is_admin = user_is_admin
409 self.user_is_admin = user_is_admin
411 self.inherit_default_permissions = user_inherit_default_permissions
410 self.inherit_default_permissions = user_inherit_default_permissions
412 self.explicit = explicit
411 self.explicit = explicit
413 self.algo = algo
412 self.algo = algo
414 self.calculate_super_admin_as_user = calculate_super_admin_as_user
413 self.calculate_super_admin_as_user = calculate_super_admin_as_user
415
414
416 scope = scope or {}
415 scope = scope or {}
417 self.scope_repo_id = scope.get('repo_id')
416 self.scope_repo_id = scope.get('repo_id')
418 self.scope_repo_group_id = scope.get('repo_group_id')
417 self.scope_repo_group_id = scope.get('repo_group_id')
419 self.scope_user_group_id = scope.get('user_group_id')
418 self.scope_user_group_id = scope.get('user_group_id')
420
419
421 self.default_user_id = User.get_default_user(cache=True).user_id
420 self.default_user_id = User.get_default_user(cache=True).user_id
422
421
423 self.permissions_repositories = PermOriginDict()
422 self.permissions_repositories = PermOriginDict()
424 self.permissions_repository_groups = PermOriginDict()
423 self.permissions_repository_groups = PermOriginDict()
425 self.permissions_user_groups = PermOriginDict()
424 self.permissions_user_groups = PermOriginDict()
426 self.permissions_repository_branches = BranchPermOriginDict()
425 self.permissions_repository_branches = BranchPermOriginDict()
427 self.permissions_global = set()
426 self.permissions_global = set()
428
427
429 self.default_repo_perms = Permission.get_default_repo_perms(
428 self.default_repo_perms = Permission.get_default_repo_perms(
430 self.default_user_id, self.scope_repo_id)
429 self.default_user_id, self.scope_repo_id)
431 self.default_repo_groups_perms = Permission.get_default_group_perms(
430 self.default_repo_groups_perms = Permission.get_default_group_perms(
432 self.default_user_id, self.scope_repo_group_id)
431 self.default_user_id, self.scope_repo_group_id)
433 self.default_user_group_perms = \
432 self.default_user_group_perms = \
434 Permission.get_default_user_group_perms(
433 Permission.get_default_user_group_perms(
435 self.default_user_id, self.scope_user_group_id)
434 self.default_user_id, self.scope_user_group_id)
436
435
437 # default branch perms
436 # default branch perms
438 self.default_branch_repo_perms = \
437 self.default_branch_repo_perms = \
439 Permission.get_default_repo_branch_perms(
438 Permission.get_default_repo_branch_perms(
440 self.default_user_id, self.scope_repo_id)
439 self.default_user_id, self.scope_repo_id)
441
440
442 def calculate(self):
441 def calculate(self):
443 if self.user_is_admin and not self.calculate_super_admin_as_user:
442 if self.user_is_admin and not self.calculate_super_admin_as_user:
444 return self._calculate_admin_permissions()
443 return self._calculate_super_admin_permissions()
445
444
446 self._calculate_global_default_permissions()
445 self._calculate_global_default_permissions()
447 self._calculate_global_permissions()
446 self._calculate_global_permissions()
448 self._calculate_default_permissions()
447 self._calculate_default_permissions()
449 self._calculate_repository_permissions()
448 self._calculate_repository_permissions()
450 self._calculate_repository_branch_permissions()
449 self._calculate_repository_branch_permissions()
451 self._calculate_repository_group_permissions()
450 self._calculate_repository_group_permissions()
452 self._calculate_user_group_permissions()
451 self._calculate_user_group_permissions()
453 return self._permission_structure()
452 return self._permission_structure()
454
453
455 def _calculate_admin_permissions(self):
454 def _calculate_super_admin_permissions(self):
456 """
455 """
457 admin user have all default rights for repositories
456 super-admin user have all default rights for repositories
458 and groups set to admin
457 and groups set to admin
459 """
458 """
460 self.permissions_global.add('hg.admin')
459 self.permissions_global.add('hg.admin')
461 self.permissions_global.add('hg.create.write_on_repogroup.true')
460 self.permissions_global.add('hg.create.write_on_repogroup.true')
462
461
463 # repositories
462 # repositories
464 for perm in self.default_repo_perms:
463 for perm in self.default_repo_perms:
465 r_k = perm.UserRepoToPerm.repository.repo_name
464 r_k = perm.UserRepoToPerm.repository.repo_name
466 obj_id = perm.UserRepoToPerm.repository.repo_id
465 obj_id = perm.UserRepoToPerm.repository.repo_id
467 archived = perm.UserRepoToPerm.repository.archived
466 archived = perm.UserRepoToPerm.repository.archived
468 p = 'repository.admin'
467 p = 'repository.admin'
469 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN, obj_id
468 self.permissions_repositories[r_k] = p, PermOrigin.SUPER_ADMIN, obj_id
470 # special case for archived repositories, which we block still even for
469 # special case for archived repositories, which we block still even for
471 # super admins
470 # super admins
472 if archived:
471 if archived:
473 p = 'repository.read'
472 p = 'repository.read'
474 self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED, obj_id
473 self.permissions_repositories[r_k] = p, PermOrigin.ARCHIVED, obj_id
475
474
476 # repository groups
475 # repository groups
477 for perm in self.default_repo_groups_perms:
476 for perm in self.default_repo_groups_perms:
478 rg_k = perm.UserRepoGroupToPerm.group.group_name
477 rg_k = perm.UserRepoGroupToPerm.group.group_name
479 obj_id = perm.UserRepoGroupToPerm.group.group_id
478 obj_id = perm.UserRepoGroupToPerm.group.group_id
480 p = 'group.admin'
479 p = 'group.admin'
481 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN, obj_id
480 self.permissions_repository_groups[rg_k] = p, PermOrigin.SUPER_ADMIN, obj_id
482
481
483 # user groups
482 # user groups
484 for perm in self.default_user_group_perms:
483 for perm in self.default_user_group_perms:
485 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
484 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
486 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
485 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
487 p = 'usergroup.admin'
486 p = 'usergroup.admin'
488 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN, obj_id
487 self.permissions_user_groups[u_k] = p, PermOrigin.SUPER_ADMIN, obj_id
489
488
490 # branch permissions
489 # branch permissions
491 # since super-admin also can have custom rule permissions
490 # since super-admin also can have custom rule permissions
492 # we *always* need to calculate those inherited from default, and also explicit
491 # we *always* need to calculate those inherited from default, and also explicit
493 self._calculate_default_permissions_repository_branches(
492 self._calculate_default_permissions_repository_branches(
494 user_inherit_object_permissions=False)
493 user_inherit_object_permissions=False)
495 self._calculate_repository_branch_permissions()
494 self._calculate_repository_branch_permissions()
496
495
497 return self._permission_structure()
496 return self._permission_structure()
498
497
499 def _calculate_global_default_permissions(self):
498 def _calculate_global_default_permissions(self):
500 """
499 """
501 global permissions taken from the default user
500 global permissions taken from the default user
502 """
501 """
503 default_global_perms = UserToPerm.query()\
502 default_global_perms = UserToPerm.query()\
504 .filter(UserToPerm.user_id == self.default_user_id)\
503 .filter(UserToPerm.user_id == self.default_user_id)\
505 .options(joinedload(UserToPerm.permission))
504 .options(joinedload(UserToPerm.permission))
506
505
507 for perm in default_global_perms:
506 for perm in default_global_perms:
508 self.permissions_global.add(perm.permission.permission_name)
507 self.permissions_global.add(perm.permission.permission_name)
509
508
510 if self.user_is_admin:
509 if self.user_is_admin:
511 self.permissions_global.add('hg.admin')
510 self.permissions_global.add('hg.admin')
512 self.permissions_global.add('hg.create.write_on_repogroup.true')
511 self.permissions_global.add('hg.create.write_on_repogroup.true')
513
512
514 def _calculate_global_permissions(self):
513 def _calculate_global_permissions(self):
515 """
514 """
516 Set global system permissions with user permissions or permissions
515 Set global system permissions with user permissions or permissions
517 taken from the user groups of the current user.
516 taken from the user groups of the current user.
518
517
519 The permissions include repo creating, repo group creating, forking
518 The permissions include repo creating, repo group creating, forking
520 etc.
519 etc.
521 """
520 """
522
521
523 # now we read the defined permissions and overwrite what we have set
522 # now we read the defined permissions and overwrite what we have set
524 # before those can be configured from groups or users explicitly.
523 # before those can be configured from groups or users explicitly.
525
524
526 # In case we want to extend this list we should make sure
525 # In case we want to extend this list we should make sure
527 # this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
526 # this is in sync with User.DEFAULT_USER_PERMISSIONS definitions
528 _configurable = frozenset([
527 _configurable = frozenset([
529 'hg.fork.none', 'hg.fork.repository',
528 'hg.fork.none', 'hg.fork.repository',
530 'hg.create.none', 'hg.create.repository',
529 'hg.create.none', 'hg.create.repository',
531 'hg.usergroup.create.false', 'hg.usergroup.create.true',
530 'hg.usergroup.create.false', 'hg.usergroup.create.true',
532 'hg.repogroup.create.false', 'hg.repogroup.create.true',
531 'hg.repogroup.create.false', 'hg.repogroup.create.true',
533 'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
532 'hg.create.write_on_repogroup.false', 'hg.create.write_on_repogroup.true',
534 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
533 'hg.inherit_default_perms.false', 'hg.inherit_default_perms.true'
535 ])
534 ])
536
535
537 # USER GROUPS comes first user group global permissions
536 # USER GROUPS comes first user group global permissions
538 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
537 user_perms_from_users_groups = Session().query(UserGroupToPerm)\
539 .options(joinedload(UserGroupToPerm.permission))\
538 .options(joinedload(UserGroupToPerm.permission))\
540 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
539 .join((UserGroupMember, UserGroupToPerm.users_group_id ==
541 UserGroupMember.users_group_id))\
540 UserGroupMember.users_group_id))\
542 .filter(UserGroupMember.user_id == self.user_id)\
541 .filter(UserGroupMember.user_id == self.user_id)\
543 .order_by(UserGroupToPerm.users_group_id)\
542 .order_by(UserGroupToPerm.users_group_id)\
544 .all()
543 .all()
545
544
546 # need to group here by groups since user can be in more than
545 # need to group here by groups since user can be in more than
547 # one group, so we get all groups
546 # one group, so we get all groups
548 _explicit_grouped_perms = [
547 _explicit_grouped_perms = [
549 [x, list(y)] for x, y in
548 [x, list(y)] for x, y in
550 itertools.groupby(user_perms_from_users_groups,
549 itertools.groupby(user_perms_from_users_groups,
551 lambda _x: _x.users_group)]
550 lambda _x: _x.users_group)]
552
551
553 for gr, perms in _explicit_grouped_perms:
552 for gr, perms in _explicit_grouped_perms:
554 # since user can be in multiple groups iterate over them and
553 # since user can be in multiple groups iterate over them and
555 # select the lowest permissions first (more explicit)
554 # select the lowest permissions first (more explicit)
556 # TODO(marcink): do this^^
555 # TODO(marcink): do this^^
557
556
558 # group doesn't inherit default permissions so we actually set them
557 # group doesn't inherit default permissions so we actually set them
559 if not gr.inherit_default_permissions:
558 if not gr.inherit_default_permissions:
560 # NEED TO IGNORE all previously set configurable permissions
559 # NEED TO IGNORE all previously set configurable permissions
561 # and replace them with explicitly set from this user
560 # and replace them with explicitly set from this user
562 # group permissions
561 # group permissions
563 self.permissions_global = self.permissions_global.difference(
562 self.permissions_global = self.permissions_global.difference(
564 _configurable)
563 _configurable)
565 for perm in perms:
564 for perm in perms:
566 self.permissions_global.add(perm.permission.permission_name)
565 self.permissions_global.add(perm.permission.permission_name)
567
566
568 # user explicit global permissions
567 # user explicit global permissions
569 user_perms = Session().query(UserToPerm)\
568 user_perms = Session().query(UserToPerm)\
570 .options(joinedload(UserToPerm.permission))\
569 .options(joinedload(UserToPerm.permission))\
571 .filter(UserToPerm.user_id == self.user_id).all()
570 .filter(UserToPerm.user_id == self.user_id).all()
572
571
573 if not self.inherit_default_permissions:
572 if not self.inherit_default_permissions:
574 # NEED TO IGNORE all configurable permissions and
573 # NEED TO IGNORE all configurable permissions and
575 # replace them with explicitly set from this user permissions
574 # replace them with explicitly set from this user permissions
576 self.permissions_global = self.permissions_global.difference(
575 self.permissions_global = self.permissions_global.difference(
577 _configurable)
576 _configurable)
578 for perm in user_perms:
577 for perm in user_perms:
579 self.permissions_global.add(perm.permission.permission_name)
578 self.permissions_global.add(perm.permission.permission_name)
580
579
581 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
580 def _calculate_default_permissions_repositories(self, user_inherit_object_permissions):
582 for perm in self.default_repo_perms:
581 for perm in self.default_repo_perms:
583 r_k = perm.UserRepoToPerm.repository.repo_name
582 r_k = perm.UserRepoToPerm.repository.repo_name
584 obj_id = perm.UserRepoToPerm.repository.repo_id
583 obj_id = perm.UserRepoToPerm.repository.repo_id
585 archived = perm.UserRepoToPerm.repository.archived
584 archived = perm.UserRepoToPerm.repository.archived
586 p = perm.Permission.permission_name
585 p = perm.Permission.permission_name
587 o = PermOrigin.REPO_DEFAULT
586 o = PermOrigin.REPO_DEFAULT
588 self.permissions_repositories[r_k] = p, o, obj_id
587 self.permissions_repositories[r_k] = p, o, obj_id
589
588
590 # if we decide this user isn't inheriting permissions from
589 # if we decide this user isn't inheriting permissions from
591 # default user we set him to .none so only explicit
590 # default user we set him to .none so only explicit
592 # permissions work
591 # permissions work
593 if not user_inherit_object_permissions:
592 if not user_inherit_object_permissions:
594 p = 'repository.none'
593 p = 'repository.none'
595 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
594 o = PermOrigin.REPO_DEFAULT_NO_INHERIT
596 self.permissions_repositories[r_k] = p, o, obj_id
595 self.permissions_repositories[r_k] = p, o, obj_id
597
596
598 if perm.Repository.private and not (
597 if perm.Repository.private and not (
599 perm.Repository.user_id == self.user_id):
598 perm.Repository.user_id == self.user_id):
600 # disable defaults for private repos,
599 # disable defaults for private repos,
601 p = 'repository.none'
600 p = 'repository.none'
602 o = PermOrigin.REPO_PRIVATE
601 o = PermOrigin.REPO_PRIVATE
603 self.permissions_repositories[r_k] = p, o, obj_id
602 self.permissions_repositories[r_k] = p, o, obj_id
604
603
605 elif perm.Repository.user_id == self.user_id:
604 elif perm.Repository.user_id == self.user_id:
606 # set admin if owner
605 # set admin if owner
607 p = 'repository.admin'
606 p = 'repository.admin'
608 o = PermOrigin.REPO_OWNER
607 o = PermOrigin.REPO_OWNER
609 self.permissions_repositories[r_k] = p, o, obj_id
608 self.permissions_repositories[r_k] = p, o, obj_id
610
609
611 if self.user_is_admin:
610 if self.user_is_admin:
612 p = 'repository.admin'
611 p = 'repository.admin'
613 o = PermOrigin.SUPER_ADMIN
612 o = PermOrigin.SUPER_ADMIN
614 self.permissions_repositories[r_k] = p, o, obj_id
613 self.permissions_repositories[r_k] = p, o, obj_id
615
614
616 # finally in case of archived repositories, we downgrade higher
615 # finally in case of archived repositories, we downgrade higher
617 # permissions to read
616 # permissions to read
618 if archived:
617 if archived:
619 current_perm = self.permissions_repositories[r_k]
618 current_perm = self.permissions_repositories[r_k]
620 if current_perm in ['repository.write', 'repository.admin']:
619 if current_perm in ['repository.write', 'repository.admin']:
621 p = 'repository.read'
620 p = 'repository.read'
622 o = PermOrigin.ARCHIVED
621 o = PermOrigin.ARCHIVED
623 self.permissions_repositories[r_k] = p, o, obj_id
622 self.permissions_repositories[r_k] = p, o, obj_id
624
623
625 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
624 def _calculate_default_permissions_repository_branches(self, user_inherit_object_permissions):
626 for perm in self.default_branch_repo_perms:
625 for perm in self.default_branch_repo_perms:
627
626
628 r_k = perm.UserRepoToPerm.repository.repo_name
627 r_k = perm.UserRepoToPerm.repository.repo_name
629 p = perm.Permission.permission_name
628 p = perm.Permission.permission_name
630 pattern = perm.UserToRepoBranchPermission.branch_pattern
629 pattern = perm.UserToRepoBranchPermission.branch_pattern
631 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
630 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
632
631
633 if not self.explicit:
632 if not self.explicit:
634 cur_perm = self.permissions_repository_branches.get(r_k)
633 cur_perm = self.permissions_repository_branches.get(r_k)
635 if cur_perm:
634 if cur_perm:
636 cur_perm = cur_perm[pattern]
635 cur_perm = cur_perm[pattern]
637 cur_perm = cur_perm or 'branch.none'
636 cur_perm = cur_perm or 'branch.none'
638
637
639 p = self._choose_permission(p, cur_perm)
638 p = self._choose_permission(p, cur_perm)
640
639
641 # NOTE(marcink): register all pattern/perm instances in this
640 # NOTE(marcink): register all pattern/perm instances in this
642 # special dict that aggregates entries
641 # special dict that aggregates entries
643 self.permissions_repository_branches[r_k] = pattern, p, o
642 self.permissions_repository_branches[r_k] = pattern, p, o
644
643
645 def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions):
644 def _calculate_default_permissions_repository_groups(self, user_inherit_object_permissions):
646 for perm in self.default_repo_groups_perms:
645 for perm in self.default_repo_groups_perms:
647 rg_k = perm.UserRepoGroupToPerm.group.group_name
646 rg_k = perm.UserRepoGroupToPerm.group.group_name
648 obj_id = perm.UserRepoGroupToPerm.group.group_id
647 obj_id = perm.UserRepoGroupToPerm.group.group_id
649 p = perm.Permission.permission_name
648 p = perm.Permission.permission_name
650 o = PermOrigin.REPOGROUP_DEFAULT
649 o = PermOrigin.REPOGROUP_DEFAULT
651 self.permissions_repository_groups[rg_k] = p, o, obj_id
650 self.permissions_repository_groups[rg_k] = p, o, obj_id
652
651
653 # if we decide this user isn't inheriting permissions from default
652 # if we decide this user isn't inheriting permissions from default
654 # user we set him to .none so only explicit permissions work
653 # user we set him to .none so only explicit permissions work
655 if not user_inherit_object_permissions:
654 if not user_inherit_object_permissions:
656 p = 'group.none'
655 p = 'group.none'
657 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
656 o = PermOrigin.REPOGROUP_DEFAULT_NO_INHERIT
658 self.permissions_repository_groups[rg_k] = p, o, obj_id
657 self.permissions_repository_groups[rg_k] = p, o, obj_id
659
658
660 if perm.RepoGroup.user_id == self.user_id:
659 if perm.RepoGroup.user_id == self.user_id:
661 # set admin if owner
660 # set admin if owner
662 p = 'group.admin'
661 p = 'group.admin'
663 o = PermOrigin.REPOGROUP_OWNER
662 o = PermOrigin.REPOGROUP_OWNER
664 self.permissions_repository_groups[rg_k] = p, o, obj_id
663 self.permissions_repository_groups[rg_k] = p, o, obj_id
665
664
666 if self.user_is_admin:
665 if self.user_is_admin:
667 p = 'group.admin'
666 p = 'group.admin'
668 o = PermOrigin.SUPER_ADMIN
667 o = PermOrigin.SUPER_ADMIN
669 self.permissions_repository_groups[rg_k] = p, o, obj_id
668 self.permissions_repository_groups[rg_k] = p, o, obj_id
670
669
671 def _calculate_default_permissions_user_groups(self, user_inherit_object_permissions):
670 def _calculate_default_permissions_user_groups(self, user_inherit_object_permissions):
672 for perm in self.default_user_group_perms:
671 for perm in self.default_user_group_perms:
673 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
672 u_k = perm.UserUserGroupToPerm.user_group.users_group_name
674 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
673 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
675 p = perm.Permission.permission_name
674 p = perm.Permission.permission_name
676 o = PermOrigin.USERGROUP_DEFAULT
675 o = PermOrigin.USERGROUP_DEFAULT
677 self.permissions_user_groups[u_k] = p, o, obj_id
676 self.permissions_user_groups[u_k] = p, o, obj_id
678
677
679 # if we decide this user isn't inheriting permissions from default
678 # if we decide this user isn't inheriting permissions from default
680 # user we set him to .none so only explicit permissions work
679 # user we set him to .none so only explicit permissions work
681 if not user_inherit_object_permissions:
680 if not user_inherit_object_permissions:
682 p = 'usergroup.none'
681 p = 'usergroup.none'
683 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
682 o = PermOrigin.USERGROUP_DEFAULT_NO_INHERIT
684 self.permissions_user_groups[u_k] = p, o, obj_id
683 self.permissions_user_groups[u_k] = p, o, obj_id
685
684
686 if perm.UserGroup.user_id == self.user_id:
685 if perm.UserGroup.user_id == self.user_id:
687 # set admin if owner
686 # set admin if owner
688 p = 'usergroup.admin'
687 p = 'usergroup.admin'
689 o = PermOrigin.USERGROUP_OWNER
688 o = PermOrigin.USERGROUP_OWNER
690 self.permissions_user_groups[u_k] = p, o, obj_id
689 self.permissions_user_groups[u_k] = p, o, obj_id
691
690
692 if self.user_is_admin:
691 if self.user_is_admin:
693 p = 'usergroup.admin'
692 p = 'usergroup.admin'
694 o = PermOrigin.SUPER_ADMIN
693 o = PermOrigin.SUPER_ADMIN
695 self.permissions_user_groups[u_k] = p, o, obj_id
694 self.permissions_user_groups[u_k] = p, o, obj_id
696
695
697 def _calculate_default_permissions(self):
696 def _calculate_default_permissions(self):
698 """
697 """
699 Set default user permissions for repositories, repository branches,
698 Set default user permissions for repositories, repository branches,
700 repository groups, user groups taken from the default user.
699 repository groups, user groups taken from the default user.
701
700
702 Calculate inheritance of object permissions based on what we have now
701 Calculate inheritance of object permissions based on what we have now
703 in GLOBAL permissions. We check if .false is in GLOBAL since this is
702 in GLOBAL permissions. We check if .false is in GLOBAL since this is
704 explicitly set. Inherit is the opposite of .false being there.
703 explicitly set. Inherit is the opposite of .false being there.
705
704
706 .. note::
705 .. note::
707
706
708 the syntax is little bit odd but what we need to check here is
707 the syntax is little bit odd but what we need to check here is
709 the opposite of .false permission being in the list so even for
708 the opposite of .false permission being in the list so even for
710 inconsistent state when both .true/.false is there
709 inconsistent state when both .true/.false is there
711 .false is more important
710 .false is more important
712
711
713 """
712 """
714 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
713 user_inherit_object_permissions = not ('hg.inherit_default_perms.false'
715 in self.permissions_global)
714 in self.permissions_global)
716
715
717 # default permissions inherited from `default` user permissions
716 # default permissions inherited from `default` user permissions
718 self._calculate_default_permissions_repositories(
717 self._calculate_default_permissions_repositories(
719 user_inherit_object_permissions)
718 user_inherit_object_permissions)
720
719
721 self._calculate_default_permissions_repository_branches(
720 self._calculate_default_permissions_repository_branches(
722 user_inherit_object_permissions)
721 user_inherit_object_permissions)
723
722
724 self._calculate_default_permissions_repository_groups(
723 self._calculate_default_permissions_repository_groups(
725 user_inherit_object_permissions)
724 user_inherit_object_permissions)
726
725
727 self._calculate_default_permissions_user_groups(
726 self._calculate_default_permissions_user_groups(
728 user_inherit_object_permissions)
727 user_inherit_object_permissions)
729
728
730 def _calculate_repository_permissions(self):
729 def _calculate_repository_permissions(self):
731 """
730 """
732 Repository access permissions for the current user.
731 Repository access permissions for the current user.
733
732
734 Check if the user is part of user groups for this repository and
733 Check if the user is part of user groups for this repository and
735 fill in the permission from it. `_choose_permission` decides of which
734 fill in the permission from it. `_choose_permission` decides of which
736 permission should be selected based on selected method.
735 permission should be selected based on selected method.
737 """
736 """
738
737
739 # user group for repositories permissions
738 # user group for repositories permissions
740 user_repo_perms_from_user_group = Permission\
739 user_repo_perms_from_user_group = Permission\
741 .get_default_repo_perms_from_user_group(
740 .get_default_repo_perms_from_user_group(
742 self.user_id, self.scope_repo_id)
741 self.user_id, self.scope_repo_id)
743
742
744 multiple_counter = collections.defaultdict(int)
743 multiple_counter = collections.defaultdict(int)
745 for perm in user_repo_perms_from_user_group:
744 for perm in user_repo_perms_from_user_group:
746 r_k = perm.UserGroupRepoToPerm.repository.repo_name
745 r_k = perm.UserGroupRepoToPerm.repository.repo_name
747 obj_id = perm.UserGroupRepoToPerm.repository.repo_id
746 obj_id = perm.UserGroupRepoToPerm.repository.repo_id
748 multiple_counter[r_k] += 1
747 multiple_counter[r_k] += 1
749 p = perm.Permission.permission_name
748 p = perm.Permission.permission_name
750 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
749 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
751 .users_group.users_group_name
750 .users_group.users_group_name
752
751
753 if multiple_counter[r_k] > 1:
752 if multiple_counter[r_k] > 1:
754 cur_perm = self.permissions_repositories[r_k]
753 cur_perm = self.permissions_repositories[r_k]
755 p = self._choose_permission(p, cur_perm)
754 p = self._choose_permission(p, cur_perm)
756
755
757 self.permissions_repositories[r_k] = p, o, obj_id
756 self.permissions_repositories[r_k] = p, o, obj_id
758
757
759 if perm.Repository.user_id == self.user_id:
758 if perm.Repository.user_id == self.user_id:
760 # set admin if owner
759 # set admin if owner
761 p = 'repository.admin'
760 p = 'repository.admin'
762 o = PermOrigin.REPO_OWNER
761 o = PermOrigin.REPO_OWNER
763 self.permissions_repositories[r_k] = p, o, obj_id
762 self.permissions_repositories[r_k] = p, o, obj_id
764
763
765 if self.user_is_admin:
764 if self.user_is_admin:
766 p = 'repository.admin'
765 p = 'repository.admin'
767 o = PermOrigin.SUPER_ADMIN
766 o = PermOrigin.SUPER_ADMIN
768 self.permissions_repositories[r_k] = p, o, obj_id
767 self.permissions_repositories[r_k] = p, o, obj_id
769
768
770 # user explicit permissions for repositories, overrides any specified
769 # user explicit permissions for repositories, overrides any specified
771 # by the group permission
770 # by the group permission
772 user_repo_perms = Permission.get_default_repo_perms(
771 user_repo_perms = Permission.get_default_repo_perms(
773 self.user_id, self.scope_repo_id)
772 self.user_id, self.scope_repo_id)
774 for perm in user_repo_perms:
773 for perm in user_repo_perms:
775 r_k = perm.UserRepoToPerm.repository.repo_name
774 r_k = perm.UserRepoToPerm.repository.repo_name
776 obj_id = perm.UserRepoToPerm.repository.repo_id
775 obj_id = perm.UserRepoToPerm.repository.repo_id
776 archived = perm.UserRepoToPerm.repository.archived
777 p = perm.Permission.permission_name
777 p = perm.Permission.permission_name
778 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
778 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
779
779
780 if not self.explicit:
780 if not self.explicit:
781 cur_perm = self.permissions_repositories.get(
781 cur_perm = self.permissions_repositories.get(
782 r_k, 'repository.none')
782 r_k, 'repository.none')
783 p = self._choose_permission(p, cur_perm)
783 p = self._choose_permission(p, cur_perm)
784
784
785 self.permissions_repositories[r_k] = p, o, obj_id
785 self.permissions_repositories[r_k] = p, o, obj_id
786
786
787 if perm.Repository.user_id == self.user_id:
787 if perm.Repository.user_id == self.user_id:
788 # set admin if owner
788 # set admin if owner
789 p = 'repository.admin'
789 p = 'repository.admin'
790 o = PermOrigin.REPO_OWNER
790 o = PermOrigin.REPO_OWNER
791 self.permissions_repositories[r_k] = p, o, obj_id
791 self.permissions_repositories[r_k] = p, o, obj_id
792
792
793 if self.user_is_admin:
793 if self.user_is_admin:
794 p = 'repository.admin'
794 p = 'repository.admin'
795 o = PermOrigin.SUPER_ADMIN
795 o = PermOrigin.SUPER_ADMIN
796 self.permissions_repositories[r_k] = p, o, obj_id
796 self.permissions_repositories[r_k] = p, o, obj_id
797
797
798 # finally in case of archived repositories, we downgrade higher
799 # permissions to read
800 if archived:
801 current_perm = self.permissions_repositories[r_k]
802 if current_perm in ['repository.write', 'repository.admin']:
803 p = 'repository.read'
804 o = PermOrigin.ARCHIVED
805 self.permissions_repositories[r_k] = p, o, obj_id
806
798 def _calculate_repository_branch_permissions(self):
807 def _calculate_repository_branch_permissions(self):
799 # user group for repositories permissions
808 # user group for repositories permissions
800 user_repo_branch_perms_from_user_group = Permission\
809 user_repo_branch_perms_from_user_group = Permission\
801 .get_default_repo_branch_perms_from_user_group(
810 .get_default_repo_branch_perms_from_user_group(
802 self.user_id, self.scope_repo_id)
811 self.user_id, self.scope_repo_id)
803
812
804 multiple_counter = collections.defaultdict(int)
813 multiple_counter = collections.defaultdict(int)
805 for perm in user_repo_branch_perms_from_user_group:
814 for perm in user_repo_branch_perms_from_user_group:
806 r_k = perm.UserGroupRepoToPerm.repository.repo_name
815 r_k = perm.UserGroupRepoToPerm.repository.repo_name
807 p = perm.Permission.permission_name
816 p = perm.Permission.permission_name
808 pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
817 pattern = perm.UserGroupToRepoBranchPermission.branch_pattern
809 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
818 o = PermOrigin.REPO_USERGROUP % perm.UserGroupRepoToPerm\
810 .users_group.users_group_name
819 .users_group.users_group_name
811
820
812 multiple_counter[r_k] += 1
821 multiple_counter[r_k] += 1
813 if multiple_counter[r_k] > 1:
822 if multiple_counter[r_k] > 1:
814 cur_perm = self.permissions_repository_branches[r_k][pattern]
823 cur_perm = self.permissions_repository_branches[r_k][pattern]
815 p = self._choose_permission(p, cur_perm)
824 p = self._choose_permission(p, cur_perm)
816
825
817 self.permissions_repository_branches[r_k] = pattern, p, o
826 self.permissions_repository_branches[r_k] = pattern, p, o
818
827
819 # user explicit branch permissions for repositories, overrides
828 # user explicit branch permissions for repositories, overrides
820 # any specified by the group permission
829 # any specified by the group permission
821 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
830 user_repo_branch_perms = Permission.get_default_repo_branch_perms(
822 self.user_id, self.scope_repo_id)
831 self.user_id, self.scope_repo_id)
823
832
824 for perm in user_repo_branch_perms:
833 for perm in user_repo_branch_perms:
825
834
826 r_k = perm.UserRepoToPerm.repository.repo_name
835 r_k = perm.UserRepoToPerm.repository.repo_name
827 p = perm.Permission.permission_name
836 p = perm.Permission.permission_name
828 pattern = perm.UserToRepoBranchPermission.branch_pattern
837 pattern = perm.UserToRepoBranchPermission.branch_pattern
829 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
838 o = PermOrigin.REPO_USER % perm.UserRepoToPerm.user.username
830
839
831 if not self.explicit:
840 if not self.explicit:
832 cur_perm = self.permissions_repository_branches.get(r_k)
841 cur_perm = self.permissions_repository_branches.get(r_k)
833 if cur_perm:
842 if cur_perm:
834 cur_perm = cur_perm[pattern]
843 cur_perm = cur_perm[pattern]
835 cur_perm = cur_perm or 'branch.none'
844 cur_perm = cur_perm or 'branch.none'
836 p = self._choose_permission(p, cur_perm)
845 p = self._choose_permission(p, cur_perm)
837
846
838 # NOTE(marcink): register all pattern/perm instances in this
847 # NOTE(marcink): register all pattern/perm instances in this
839 # special dict that aggregates entries
848 # special dict that aggregates entries
840 self.permissions_repository_branches[r_k] = pattern, p, o
849 self.permissions_repository_branches[r_k] = pattern, p, o
841
850
842 def _calculate_repository_group_permissions(self):
851 def _calculate_repository_group_permissions(self):
843 """
852 """
844 Repository group permissions for the current user.
853 Repository group permissions for the current user.
845
854
846 Check if the user is part of user groups for repository groups and
855 Check if the user is part of user groups for repository groups and
847 fill in the permissions from it. `_choose_permission` decides of which
856 fill in the permissions from it. `_choose_permission` decides of which
848 permission should be selected based on selected method.
857 permission should be selected based on selected method.
849 """
858 """
850 # user group for repo groups permissions
859 # user group for repo groups permissions
851 user_repo_group_perms_from_user_group = Permission\
860 user_repo_group_perms_from_user_group = Permission\
852 .get_default_group_perms_from_user_group(
861 .get_default_group_perms_from_user_group(
853 self.user_id, self.scope_repo_group_id)
862 self.user_id, self.scope_repo_group_id)
854
863
855 multiple_counter = collections.defaultdict(int)
864 multiple_counter = collections.defaultdict(int)
856 for perm in user_repo_group_perms_from_user_group:
865 for perm in user_repo_group_perms_from_user_group:
857 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
866 rg_k = perm.UserGroupRepoGroupToPerm.group.group_name
858 obj_id = perm.UserGroupRepoGroupToPerm.group.group_id
867 obj_id = perm.UserGroupRepoGroupToPerm.group.group_id
859 multiple_counter[rg_k] += 1
868 multiple_counter[rg_k] += 1
860 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
869 o = PermOrigin.REPOGROUP_USERGROUP % perm.UserGroupRepoGroupToPerm\
861 .users_group.users_group_name
870 .users_group.users_group_name
862 p = perm.Permission.permission_name
871 p = perm.Permission.permission_name
863
872
864 if multiple_counter[rg_k] > 1:
873 if multiple_counter[rg_k] > 1:
865 cur_perm = self.permissions_repository_groups[rg_k]
874 cur_perm = self.permissions_repository_groups[rg_k]
866 p = self._choose_permission(p, cur_perm)
875 p = self._choose_permission(p, cur_perm)
867 self.permissions_repository_groups[rg_k] = p, o, obj_id
876 self.permissions_repository_groups[rg_k] = p, o, obj_id
868
877
869 if perm.RepoGroup.user_id == self.user_id:
878 if perm.RepoGroup.user_id == self.user_id:
870 # set admin if owner, even for member of other user group
879 # set admin if owner, even for member of other user group
871 p = 'group.admin'
880 p = 'group.admin'
872 o = PermOrigin.REPOGROUP_OWNER
881 o = PermOrigin.REPOGROUP_OWNER
873 self.permissions_repository_groups[rg_k] = p, o, obj_id
882 self.permissions_repository_groups[rg_k] = p, o, obj_id
874
883
875 if self.user_is_admin:
884 if self.user_is_admin:
876 p = 'group.admin'
885 p = 'group.admin'
877 o = PermOrigin.SUPER_ADMIN
886 o = PermOrigin.SUPER_ADMIN
878 self.permissions_repository_groups[rg_k] = p, o, obj_id
887 self.permissions_repository_groups[rg_k] = p, o, obj_id
879
888
880 # user explicit permissions for repository groups
889 # user explicit permissions for repository groups
881 user_repo_groups_perms = Permission.get_default_group_perms(
890 user_repo_groups_perms = Permission.get_default_group_perms(
882 self.user_id, self.scope_repo_group_id)
891 self.user_id, self.scope_repo_group_id)
883 for perm in user_repo_groups_perms:
892 for perm in user_repo_groups_perms:
884 rg_k = perm.UserRepoGroupToPerm.group.group_name
893 rg_k = perm.UserRepoGroupToPerm.group.group_name
885 obj_id = perm.UserRepoGroupToPerm.group.group_id
894 obj_id = perm.UserRepoGroupToPerm.group.group_id
886 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
895 o = PermOrigin.REPOGROUP_USER % perm.UserRepoGroupToPerm\
887 .user.username
896 .user.username
888 p = perm.Permission.permission_name
897 p = perm.Permission.permission_name
889
898
890 if not self.explicit:
899 if not self.explicit:
891 cur_perm = self.permissions_repository_groups.get(rg_k, 'group.none')
900 cur_perm = self.permissions_repository_groups.get(rg_k, 'group.none')
892 p = self._choose_permission(p, cur_perm)
901 p = self._choose_permission(p, cur_perm)
893
902
894 self.permissions_repository_groups[rg_k] = p, o, obj_id
903 self.permissions_repository_groups[rg_k] = p, o, obj_id
895
904
896 if perm.RepoGroup.user_id == self.user_id:
905 if perm.RepoGroup.user_id == self.user_id:
897 # set admin if owner
906 # set admin if owner
898 p = 'group.admin'
907 p = 'group.admin'
899 o = PermOrigin.REPOGROUP_OWNER
908 o = PermOrigin.REPOGROUP_OWNER
900 self.permissions_repository_groups[rg_k] = p, o, obj_id
909 self.permissions_repository_groups[rg_k] = p, o, obj_id
901
910
902 if self.user_is_admin:
911 if self.user_is_admin:
903 p = 'group.admin'
912 p = 'group.admin'
904 o = PermOrigin.SUPER_ADMIN
913 o = PermOrigin.SUPER_ADMIN
905 self.permissions_repository_groups[rg_k] = p, o, obj_id
914 self.permissions_repository_groups[rg_k] = p, o, obj_id
906
915
907 def _calculate_user_group_permissions(self):
916 def _calculate_user_group_permissions(self):
908 """
917 """
909 User group permissions for the current user.
918 User group permissions for the current user.
910 """
919 """
911 # user group for user group permissions
920 # user group for user group permissions
912 user_group_from_user_group = Permission\
921 user_group_from_user_group = Permission\
913 .get_default_user_group_perms_from_user_group(
922 .get_default_user_group_perms_from_user_group(
914 self.user_id, self.scope_user_group_id)
923 self.user_id, self.scope_user_group_id)
915
924
916 multiple_counter = collections.defaultdict(int)
925 multiple_counter = collections.defaultdict(int)
917 for perm in user_group_from_user_group:
926 for perm in user_group_from_user_group:
918 ug_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
927 ug_k = perm.UserGroupUserGroupToPerm.target_user_group.users_group_name
919 obj_id = perm.UserGroupUserGroupToPerm.target_user_group.users_group_id
928 obj_id = perm.UserGroupUserGroupToPerm.target_user_group.users_group_id
920 multiple_counter[ug_k] += 1
929 multiple_counter[ug_k] += 1
921 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
930 o = PermOrigin.USERGROUP_USERGROUP % perm.UserGroupUserGroupToPerm\
922 .user_group.users_group_name
931 .user_group.users_group_name
923 p = perm.Permission.permission_name
932 p = perm.Permission.permission_name
924
933
925 if multiple_counter[ug_k] > 1:
934 if multiple_counter[ug_k] > 1:
926 cur_perm = self.permissions_user_groups[ug_k]
935 cur_perm = self.permissions_user_groups[ug_k]
927 p = self._choose_permission(p, cur_perm)
936 p = self._choose_permission(p, cur_perm)
928
937
929 self.permissions_user_groups[ug_k] = p, o, obj_id
938 self.permissions_user_groups[ug_k] = p, o, obj_id
930
939
931 if perm.UserGroup.user_id == self.user_id:
940 if perm.UserGroup.user_id == self.user_id:
932 # set admin if owner, even for member of other user group
941 # set admin if owner, even for member of other user group
933 p = 'usergroup.admin'
942 p = 'usergroup.admin'
934 o = PermOrigin.USERGROUP_OWNER
943 o = PermOrigin.USERGROUP_OWNER
935 self.permissions_user_groups[ug_k] = p, o, obj_id
944 self.permissions_user_groups[ug_k] = p, o, obj_id
936
945
937 if self.user_is_admin:
946 if self.user_is_admin:
938 p = 'usergroup.admin'
947 p = 'usergroup.admin'
939 o = PermOrigin.SUPER_ADMIN
948 o = PermOrigin.SUPER_ADMIN
940 self.permissions_user_groups[ug_k] = p, o, obj_id
949 self.permissions_user_groups[ug_k] = p, o, obj_id
941
950
942 # user explicit permission for user groups
951 # user explicit permission for user groups
943 user_user_groups_perms = Permission.get_default_user_group_perms(
952 user_user_groups_perms = Permission.get_default_user_group_perms(
944 self.user_id, self.scope_user_group_id)
953 self.user_id, self.scope_user_group_id)
945 for perm in user_user_groups_perms:
954 for perm in user_user_groups_perms:
946 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
955 ug_k = perm.UserUserGroupToPerm.user_group.users_group_name
947 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
956 obj_id = perm.UserUserGroupToPerm.user_group.users_group_id
948 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
957 o = PermOrigin.USERGROUP_USER % perm.UserUserGroupToPerm\
949 .user.username
958 .user.username
950 p = perm.Permission.permission_name
959 p = perm.Permission.permission_name
951
960
952 if not self.explicit:
961 if not self.explicit:
953 cur_perm = self.permissions_user_groups.get(ug_k, 'usergroup.none')
962 cur_perm = self.permissions_user_groups.get(ug_k, 'usergroup.none')
954 p = self._choose_permission(p, cur_perm)
963 p = self._choose_permission(p, cur_perm)
955
964
956 self.permissions_user_groups[ug_k] = p, o, obj_id
965 self.permissions_user_groups[ug_k] = p, o, obj_id
957
966
958 if perm.UserGroup.user_id == self.user_id:
967 if perm.UserGroup.user_id == self.user_id:
959 # set admin if owner
968 # set admin if owner
960 p = 'usergroup.admin'
969 p = 'usergroup.admin'
961 o = PermOrigin.USERGROUP_OWNER
970 o = PermOrigin.USERGROUP_OWNER
962 self.permissions_user_groups[ug_k] = p, o, obj_id
971 self.permissions_user_groups[ug_k] = p, o, obj_id
963
972
964 if self.user_is_admin:
973 if self.user_is_admin:
965 p = 'usergroup.admin'
974 p = 'usergroup.admin'
966 o = PermOrigin.SUPER_ADMIN
975 o = PermOrigin.SUPER_ADMIN
967 self.permissions_user_groups[ug_k] = p, o, obj_id
976 self.permissions_user_groups[ug_k] = p, o, obj_id
968
977
969 def _choose_permission(self, new_perm, cur_perm):
978 def _choose_permission(self, new_perm, cur_perm):
970 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
979 new_perm_val = Permission.PERM_WEIGHTS[new_perm]
971 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
980 cur_perm_val = Permission.PERM_WEIGHTS[cur_perm]
972 if self.algo == 'higherwin':
981 if self.algo == 'higherwin':
973 if new_perm_val > cur_perm_val:
982 if new_perm_val > cur_perm_val:
974 return new_perm
983 return new_perm
975 return cur_perm
984 return cur_perm
976 elif self.algo == 'lowerwin':
985 elif self.algo == 'lowerwin':
977 if new_perm_val < cur_perm_val:
986 if new_perm_val < cur_perm_val:
978 return new_perm
987 return new_perm
979 return cur_perm
988 return cur_perm
980
989
981 def _permission_structure(self):
990 def _permission_structure(self):
982 return {
991 return {
983 'global': self.permissions_global,
992 'global': self.permissions_global,
984 'repositories': self.permissions_repositories,
993 'repositories': self.permissions_repositories,
985 'repository_branches': self.permissions_repository_branches,
994 'repository_branches': self.permissions_repository_branches,
986 'repositories_groups': self.permissions_repository_groups,
995 'repositories_groups': self.permissions_repository_groups,
987 'user_groups': self.permissions_user_groups,
996 'user_groups': self.permissions_user_groups,
988 }
997 }
989
998
990
999
991 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
1000 def allowed_auth_token_access(view_name, auth_token, whitelist=None):
992 """
1001 """
993 Check if given controller_name is in whitelist of auth token access
1002 Check if given controller_name is in whitelist of auth token access
994 """
1003 """
995 if not whitelist:
1004 if not whitelist:
996 from rhodecode import CONFIG
1005 from rhodecode import CONFIG
997 whitelist = aslist(
1006 whitelist = aslist(
998 CONFIG.get('api_access_controllers_whitelist'), sep=',')
1007 CONFIG.get('api_access_controllers_whitelist'), sep=',')
999 # backward compat translation
1008 # backward compat translation
1000 compat = {
1009 compat = {
1001 # old controller, new VIEW
1010 # old controller, new VIEW
1002 'ChangesetController:*': 'RepoCommitsView:*',
1011 'ChangesetController:*': 'RepoCommitsView:*',
1003 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
1012 'ChangesetController:changeset_patch': 'RepoCommitsView:repo_commit_patch',
1004 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
1013 'ChangesetController:changeset_raw': 'RepoCommitsView:repo_commit_raw',
1005 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
1014 'FilesController:raw': 'RepoCommitsView:repo_commit_raw',
1006 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
1015 'FilesController:archivefile': 'RepoFilesView:repo_archivefile',
1007 'GistsController:*': 'GistView:*',
1016 'GistsController:*': 'GistView:*',
1008 }
1017 }
1009
1018
1010 log.debug(
1019 log.debug(
1011 'Allowed views for AUTH TOKEN access: %s', whitelist)
1020 'Allowed views for AUTH TOKEN access: %s', whitelist)
1012 auth_token_access_valid = False
1021 auth_token_access_valid = False
1013
1022
1014 for entry in whitelist:
1023 for entry in whitelist:
1015 token_match = True
1024 token_match = True
1016 if entry in compat:
1025 if entry in compat:
1017 # translate from old Controllers to Pyramid Views
1026 # translate from old Controllers to Pyramid Views
1018 entry = compat[entry]
1027 entry = compat[entry]
1019
1028
1020 if '@' in entry:
1029 if '@' in entry:
1021 # specific AuthToken
1030 # specific AuthToken
1022 entry, allowed_token = entry.split('@', 1)
1031 entry, allowed_token = entry.split('@', 1)
1023 token_match = auth_token == allowed_token
1032 token_match = auth_token == allowed_token
1024
1033
1025 if fnmatch.fnmatch(view_name, entry) and token_match:
1034 if fnmatch.fnmatch(view_name, entry) and token_match:
1026 auth_token_access_valid = True
1035 auth_token_access_valid = True
1027 break
1036 break
1028
1037
1029 if auth_token_access_valid:
1038 if auth_token_access_valid:
1030 log.debug('view: `%s` matches entry in whitelist: %s',
1039 log.debug('view: `%s` matches entry in whitelist: %s',
1031 view_name, whitelist)
1040 view_name, whitelist)
1032
1041
1033 else:
1042 else:
1034 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
1043 msg = ('view: `%s` does *NOT* match any entry in whitelist: %s'
1035 % (view_name, whitelist))
1044 % (view_name, whitelist))
1036 if auth_token:
1045 if auth_token:
1037 # if we use auth token key and don't have access it's a warning
1046 # if we use auth token key and don't have access it's a warning
1038 log.warning(msg)
1047 log.warning(msg)
1039 else:
1048 else:
1040 log.debug(msg)
1049 log.debug(msg)
1041
1050
1042 return auth_token_access_valid
1051 return auth_token_access_valid
1043
1052
1044
1053
1045 class AuthUser(object):
1054 class AuthUser(object):
1046 """
1055 """
1047 A simple object that handles all attributes of user in RhodeCode
1056 A simple object that handles all attributes of user in RhodeCode
1048
1057
1049 It does lookup based on API key,given user, or user present in session
1058 It does lookup based on API key,given user, or user present in session
1050 Then it fills all required information for such user. It also checks if
1059 Then it fills all required information for such user. It also checks if
1051 anonymous access is enabled and if so, it returns default user as logged in
1060 anonymous access is enabled and if so, it returns default user as logged in
1052 """
1061 """
1053 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
1062 GLOBAL_PERMS = [x[0] for x in Permission.PERMS]
1054 repo_read_perms = ['repository.read', 'repository.admin', 'repository.write']
1063 repo_read_perms = ['repository.read', 'repository.admin', 'repository.write']
1055 repo_group_read_perms = ['group.read', 'group.write', 'group.admin']
1064 repo_group_read_perms = ['group.read', 'group.write', 'group.admin']
1056 user_group_read_perms = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
1065 user_group_read_perms = ['usergroup.read', 'usergroup.write', 'usergroup.admin']
1057
1066
1058 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
1067 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
1059
1068
1060 self.user_id = user_id
1069 self.user_id = user_id
1061 self._api_key = api_key
1070 self._api_key = api_key
1062
1071
1063 self.api_key = None
1072 self.api_key = None
1064 self.username = username
1073 self.username = username
1065 self.ip_addr = ip_addr
1074 self.ip_addr = ip_addr
1066 self.name = ''
1075 self.name = ''
1067 self.lastname = ''
1076 self.lastname = ''
1068 self.first_name = ''
1077 self.first_name = ''
1069 self.last_name = ''
1078 self.last_name = ''
1070 self.email = ''
1079 self.email = ''
1071 self.is_authenticated = False
1080 self.is_authenticated = False
1072 self.admin = False
1081 self.admin = False
1073 self.inherit_default_permissions = False
1082 self.inherit_default_permissions = False
1074 self.password = ''
1083 self.password = ''
1075
1084
1076 self.anonymous_user = None # propagated on propagate_data
1085 self.anonymous_user = None # propagated on propagate_data
1077 self.propagate_data()
1086 self.propagate_data()
1078 self._instance = None
1087 self._instance = None
1079 self._permissions_scoped_cache = {} # used to bind scoped calculation
1088 self._permissions_scoped_cache = {} # used to bind scoped calculation
1080
1089
1081 @LazyProperty
1090 @LazyProperty
1082 def permissions(self):
1091 def permissions(self):
1083 return self.get_perms(user=self, cache=None)
1092 return self.get_perms(user=self, cache=None)
1084
1093
1085 @LazyProperty
1094 @LazyProperty
1086 def permissions_safe(self):
1095 def permissions_safe(self):
1087 """
1096 """
1088 Filtered permissions excluding not allowed repositories
1097 Filtered permissions excluding not allowed repositories
1089 """
1098 """
1090 perms = self.get_perms(user=self, cache=None)
1099 perms = self.get_perms(user=self, cache=None)
1091
1100
1092 perms['repositories'] = {
1101 perms['repositories'] = {
1093 k: v for k, v in perms['repositories'].items()
1102 k: v for k, v in perms['repositories'].items()
1094 if v != 'repository.none'}
1103 if v != 'repository.none'}
1095 perms['repositories_groups'] = {
1104 perms['repositories_groups'] = {
1096 k: v for k, v in perms['repositories_groups'].items()
1105 k: v for k, v in perms['repositories_groups'].items()
1097 if v != 'group.none'}
1106 if v != 'group.none'}
1098 perms['user_groups'] = {
1107 perms['user_groups'] = {
1099 k: v for k, v in perms['user_groups'].items()
1108 k: v for k, v in perms['user_groups'].items()
1100 if v != 'usergroup.none'}
1109 if v != 'usergroup.none'}
1101 perms['repository_branches'] = {
1110 perms['repository_branches'] = {
1102 k: v for k, v in perms['repository_branches'].iteritems()
1111 k: v for k, v in perms['repository_branches'].iteritems()
1103 if v != 'branch.none'}
1112 if v != 'branch.none'}
1104 return perms
1113 return perms
1105
1114
1106 @LazyProperty
1115 @LazyProperty
1107 def permissions_full_details(self):
1116 def permissions_full_details(self):
1108 return self.get_perms(
1117 return self.get_perms(
1109 user=self, cache=None, calculate_super_admin=True)
1118 user=self, cache=None, calculate_super_admin=True)
1110
1119
1111 def permissions_with_scope(self, scope):
1120 def permissions_with_scope(self, scope):
1112 """
1121 """
1113 Call the get_perms function with scoped data. The scope in that function
1122 Call the get_perms function with scoped data. The scope in that function
1114 narrows the SQL calls to the given ID of objects resulting in fetching
1123 narrows the SQL calls to the given ID of objects resulting in fetching
1115 Just particular permission we want to obtain. If scope is an empty dict
1124 Just particular permission we want to obtain. If scope is an empty dict
1116 then it basically narrows the scope to GLOBAL permissions only.
1125 then it basically narrows the scope to GLOBAL permissions only.
1117
1126
1118 :param scope: dict
1127 :param scope: dict
1119 """
1128 """
1120 if 'repo_name' in scope:
1129 if 'repo_name' in scope:
1121 obj = Repository.get_by_repo_name(scope['repo_name'])
1130 obj = Repository.get_by_repo_name(scope['repo_name'])
1122 if obj:
1131 if obj:
1123 scope['repo_id'] = obj.repo_id
1132 scope['repo_id'] = obj.repo_id
1124 _scope = collections.OrderedDict()
1133 _scope = collections.OrderedDict()
1125 _scope['repo_id'] = -1
1134 _scope['repo_id'] = -1
1126 _scope['user_group_id'] = -1
1135 _scope['user_group_id'] = -1
1127 _scope['repo_group_id'] = -1
1136 _scope['repo_group_id'] = -1
1128
1137
1129 for k in sorted(scope.keys()):
1138 for k in sorted(scope.keys()):
1130 _scope[k] = scope[k]
1139 _scope[k] = scope[k]
1131
1140
1132 # store in cache to mimic how the @LazyProperty works,
1141 # store in cache to mimic how the @LazyProperty works,
1133 # the difference here is that we use the unique key calculated
1142 # the difference here is that we use the unique key calculated
1134 # from params and values
1143 # from params and values
1135 return self.get_perms(user=self, cache=None, scope=_scope)
1144 return self.get_perms(user=self, cache=None, scope=_scope)
1136
1145
1137 def get_instance(self):
1146 def get_instance(self):
1138 return User.get(self.user_id)
1147 return User.get(self.user_id)
1139
1148
1140 def propagate_data(self):
1149 def propagate_data(self):
1141 """
1150 """
1142 Fills in user data and propagates values to this instance. Maps fetched
1151 Fills in user data and propagates values to this instance. Maps fetched
1143 user attributes to this class instance attributes
1152 user attributes to this class instance attributes
1144 """
1153 """
1145 log.debug('AuthUser: starting data propagation for new potential user')
1154 log.debug('AuthUser: starting data propagation for new potential user')
1146 user_model = UserModel()
1155 user_model = UserModel()
1147 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1156 anon_user = self.anonymous_user = User.get_default_user(cache=True)
1148 is_user_loaded = False
1157 is_user_loaded = False
1149
1158
1150 # lookup by userid
1159 # lookup by userid
1151 if self.user_id is not None and self.user_id != anon_user.user_id:
1160 if self.user_id is not None and self.user_id != anon_user.user_id:
1152 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1161 log.debug('Trying Auth User lookup by USER ID: `%s`', self.user_id)
1153 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1162 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
1154
1163
1155 # try go get user by api key
1164 # try go get user by api key
1156 elif self._api_key and self._api_key != anon_user.api_key:
1165 elif self._api_key and self._api_key != anon_user.api_key:
1157 log.debug('Trying Auth User lookup by API KEY: `...%s`', self._api_key[-4:])
1166 log.debug('Trying Auth User lookup by API KEY: `...%s`', self._api_key[-4:])
1158 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1167 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
1159
1168
1160 # lookup by username
1169 # lookup by username
1161 elif self.username:
1170 elif self.username:
1162 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1171 log.debug('Trying Auth User lookup by USER NAME: `%s`', self.username)
1163 is_user_loaded = user_model.fill_data(self, username=self.username)
1172 is_user_loaded = user_model.fill_data(self, username=self.username)
1164 else:
1173 else:
1165 log.debug('No data in %s that could been used to log in', self)
1174 log.debug('No data in %s that could been used to log in', self)
1166
1175
1167 if not is_user_loaded:
1176 if not is_user_loaded:
1168 log.debug(
1177 log.debug(
1169 'Failed to load user. Fallback to default user %s', anon_user)
1178 'Failed to load user. Fallback to default user %s', anon_user)
1170 # if we cannot authenticate user try anonymous
1179 # if we cannot authenticate user try anonymous
1171 if anon_user.active:
1180 if anon_user.active:
1172 log.debug('default user is active, using it as a session user')
1181 log.debug('default user is active, using it as a session user')
1173 user_model.fill_data(self, user_id=anon_user.user_id)
1182 user_model.fill_data(self, user_id=anon_user.user_id)
1174 # then we set this user is logged in
1183 # then we set this user is logged in
1175 self.is_authenticated = True
1184 self.is_authenticated = True
1176 else:
1185 else:
1177 log.debug('default user is NOT active')
1186 log.debug('default user is NOT active')
1178 # in case of disabled anonymous user we reset some of the
1187 # in case of disabled anonymous user we reset some of the
1179 # parameters so such user is "corrupted", skipping the fill_data
1188 # parameters so such user is "corrupted", skipping the fill_data
1180 for attr in ['user_id', 'username', 'admin', 'active']:
1189 for attr in ['user_id', 'username', 'admin', 'active']:
1181 setattr(self, attr, None)
1190 setattr(self, attr, None)
1182 self.is_authenticated = False
1191 self.is_authenticated = False
1183
1192
1184 if not self.username:
1193 if not self.username:
1185 self.username = 'None'
1194 self.username = 'None'
1186
1195
1187 log.debug('AuthUser: propagated user is now %s', self)
1196 log.debug('AuthUser: propagated user is now %s', self)
1188
1197
1189 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1198 def get_perms(self, user, scope=None, explicit=True, algo='higherwin',
1190 calculate_super_admin=False, cache=None):
1199 calculate_super_admin=False, cache=None):
1191 """
1200 """
1192 Fills user permission attribute with permissions taken from database
1201 Fills user permission attribute with permissions taken from database
1193 works for permissions given for repositories, and for permissions that
1202 works for permissions given for repositories, and for permissions that
1194 are granted to groups
1203 are granted to groups
1195
1204
1196 :param user: instance of User object from database
1205 :param user: instance of User object from database
1197 :param explicit: In case there are permissions both for user and a group
1206 :param explicit: In case there are permissions both for user and a group
1198 that user is part of, explicit flag will defiine if user will
1207 that user is part of, explicit flag will defiine if user will
1199 explicitly override permissions from group, if it's False it will
1208 explicitly override permissions from group, if it's False it will
1200 make decision based on the algo
1209 make decision based on the algo
1201 :param algo: algorithm to decide what permission should be choose if
1210 :param algo: algorithm to decide what permission should be choose if
1202 it's multiple defined, eg user in two different groups. It also
1211 it's multiple defined, eg user in two different groups. It also
1203 decides if explicit flag is turned off how to specify the permission
1212 decides if explicit flag is turned off how to specify the permission
1204 for case when user is in a group + have defined separate permission
1213 for case when user is in a group + have defined separate permission
1205 :param calculate_super_admin: calculate permissions for super-admin in the
1214 :param calculate_super_admin: calculate permissions for super-admin in the
1206 same way as for regular user without speedups
1215 same way as for regular user without speedups
1207 :param cache: Use caching for calculation, None = let the cache backend decide
1216 :param cache: Use caching for calculation, None = let the cache backend decide
1208 """
1217 """
1209 user_id = user.user_id
1218 user_id = user.user_id
1210 user_is_admin = user.is_admin
1219 user_is_admin = user.is_admin
1211
1220
1212 # inheritance of global permissions like create repo/fork repo etc
1221 # inheritance of global permissions like create repo/fork repo etc
1213 user_inherit_default_permissions = user.inherit_default_permissions
1222 user_inherit_default_permissions = user.inherit_default_permissions
1214
1223
1215 cache_seconds = safe_int(
1224 cache_seconds = safe_int(
1216 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1225 rhodecode.CONFIG.get('rc_cache.cache_perms.expiration_time'))
1217
1226
1218 if cache is None:
1227 if cache is None:
1219 # let the backend cache decide
1228 # let the backend cache decide
1220 cache_on = cache_seconds > 0
1229 cache_on = cache_seconds > 0
1221 else:
1230 else:
1222 cache_on = cache
1231 cache_on = cache
1223
1232
1224 log.debug(
1233 log.debug(
1225 'Computing PERMISSION tree for user %s scope `%s` '
1234 'Computing PERMISSION tree for user %s scope `%s` '
1226 'with caching: %s[TTL: %ss]', user, scope, cache_on, cache_seconds or 0)
1235 'with caching: %s[TTL: %ss]', user, scope, cache_on, cache_seconds or 0)
1227
1236
1228 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
1237 cache_namespace_uid = 'cache_user_auth.{}'.format(user_id)
1229 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1238 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1230
1239
1231 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
1240 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid,
1232 condition=cache_on)
1241 condition=cache_on)
1233 def compute_perm_tree(cache_name, cache_ver,
1242 def compute_perm_tree(cache_name, cache_ver,
1234 user_id, scope, user_is_admin,user_inherit_default_permissions,
1243 user_id, scope, user_is_admin,user_inherit_default_permissions,
1235 explicit, algo, calculate_super_admin):
1244 explicit, algo, calculate_super_admin):
1236 return _cached_perms_data(
1245 return _cached_perms_data(
1237 user_id, scope, user_is_admin, user_inherit_default_permissions,
1246 user_id, scope, user_is_admin, user_inherit_default_permissions,
1238 explicit, algo, calculate_super_admin)
1247 explicit, algo, calculate_super_admin)
1239
1248
1240 start = time.time()
1249 start = time.time()
1241 result = compute_perm_tree(
1250 result = compute_perm_tree(
1242 'permissions', 'v1', user_id, scope, user_is_admin,
1251 'permissions', 'v1', user_id, scope, user_is_admin,
1243 user_inherit_default_permissions, explicit, algo,
1252 user_inherit_default_permissions, explicit, algo,
1244 calculate_super_admin)
1253 calculate_super_admin)
1245
1254
1246 result_repr = []
1255 result_repr = []
1247 for k in result:
1256 for k in result:
1248 result_repr.append((k, len(result[k])))
1257 result_repr.append((k, len(result[k])))
1249 total = time.time() - start
1258 total = time.time() - start
1250 log.debug('PERMISSION tree for user %s computed in %.4fs: %s',
1259 log.debug('PERMISSION tree for user %s computed in %.4fs: %s',
1251 user, total, result_repr)
1260 user, total, result_repr)
1252
1261
1253 return result
1262 return result
1254
1263
1255 @property
1264 @property
1256 def is_default(self):
1265 def is_default(self):
1257 return self.username == User.DEFAULT_USER
1266 return self.username == User.DEFAULT_USER
1258
1267
1259 @property
1268 @property
1260 def is_admin(self):
1269 def is_admin(self):
1261 return self.admin
1270 return self.admin
1262
1271
1263 @property
1272 @property
1264 def is_user_object(self):
1273 def is_user_object(self):
1265 return self.user_id is not None
1274 return self.user_id is not None
1266
1275
1267 @property
1276 @property
1268 def repositories_admin(self):
1277 def repositories_admin(self):
1269 """
1278 """
1270 Returns list of repositories you're an admin of
1279 Returns list of repositories you're an admin of
1271 """
1280 """
1272 return [
1281 return [
1273 x[0] for x in self.permissions['repositories'].items()
1282 x[0] for x in self.permissions['repositories'].items()
1274 if x[1] == 'repository.admin']
1283 if x[1] == 'repository.admin']
1275
1284
1276 @property
1285 @property
1277 def repository_groups_admin(self):
1286 def repository_groups_admin(self):
1278 """
1287 """
1279 Returns list of repository groups you're an admin of
1288 Returns list of repository groups you're an admin of
1280 """
1289 """
1281 return [
1290 return [
1282 x[0] for x in self.permissions['repositories_groups'].items()
1291 x[0] for x in self.permissions['repositories_groups'].items()
1283 if x[1] == 'group.admin']
1292 if x[1] == 'group.admin']
1284
1293
1285 @property
1294 @property
1286 def user_groups_admin(self):
1295 def user_groups_admin(self):
1287 """
1296 """
1288 Returns list of user groups you're an admin of
1297 Returns list of user groups you're an admin of
1289 """
1298 """
1290 return [
1299 return [
1291 x[0] for x in self.permissions['user_groups'].items()
1300 x[0] for x in self.permissions['user_groups'].items()
1292 if x[1] == 'usergroup.admin']
1301 if x[1] == 'usergroup.admin']
1293
1302
1294 def repo_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1303 def repo_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1295 if not perms:
1304 if not perms:
1296 perms = AuthUser.repo_read_perms
1305 perms = AuthUser.repo_read_perms
1297 allowed_ids = []
1306 allowed_ids = []
1298 for k, stack_data in self.permissions['repositories'].perm_origin_stack.items():
1307 for k, stack_data in self.permissions['repositories'].perm_origin_stack.items():
1299 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1308 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1300 if prefix_filter and not k.startswith(prefix_filter):
1309 if prefix_filter and not k.startswith(prefix_filter):
1301 continue
1310 continue
1302 if perm in perms:
1311 if perm in perms:
1303 allowed_ids.append(obj_id)
1312 allowed_ids.append(obj_id)
1304 return allowed_ids
1313 return allowed_ids
1305
1314
1306 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1315 def repo_acl_ids(self, perms=None, name_filter=None, cache=False):
1307 """
1316 """
1308 Returns list of repository ids that user have access to based on given
1317 Returns list of repository ids that user have access to based on given
1309 perms. The cache flag should be only used in cases that are used for
1318 perms. The cache flag should be only used in cases that are used for
1310 display purposes, NOT IN ANY CASE for permission checks.
1319 display purposes, NOT IN ANY CASE for permission checks.
1311 """
1320 """
1312 from rhodecode.model.scm import RepoList
1321 from rhodecode.model.scm import RepoList
1313 if not perms:
1322 if not perms:
1314 perms = AuthUser.repo_read_perms
1323 perms = AuthUser.repo_read_perms
1315
1324
1316 if not isinstance(perms, list):
1325 if not isinstance(perms, list):
1317 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1326 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1318
1327
1319 def _cached_repo_acl(perm_def, _name_filter):
1328 def _cached_repo_acl(perm_def, _name_filter):
1320 qry = Repository.query()
1329 qry = Repository.query()
1321 if _name_filter:
1330 if _name_filter:
1322 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1331 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1323 qry = qry.filter(
1332 qry = qry.filter(
1324 Repository.repo_name.ilike(ilike_expression))
1333 Repository.repo_name.ilike(ilike_expression))
1325
1334
1326 return [x.repo_id for x in
1335 return [x.repo_id for x in
1327 RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1336 RepoList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1328
1337
1329 log.debug('Computing REPO ACL IDS user %s', self)
1338 log.debug('Computing REPO ACL IDS user %s', self)
1330
1339
1331 cache_namespace_uid = 'cache_user_repo_acl_ids.{}'.format(self.user_id)
1340 cache_namespace_uid = 'cache_user_repo_acl_ids.{}'.format(self.user_id)
1332 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1341 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1333
1342
1334 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1343 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1335 def compute_repo_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1344 def compute_repo_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1336 return _cached_repo_acl(perm_def, _name_filter)
1345 return _cached_repo_acl(perm_def, _name_filter)
1337
1346
1338 start = time.time()
1347 start = time.time()
1339 result = compute_repo_acl_ids('v1', self.user_id, perms, name_filter)
1348 result = compute_repo_acl_ids('v1', self.user_id, perms, name_filter)
1340 total = time.time() - start
1349 total = time.time() - start
1341 log.debug('REPO ACL IDS for user %s computed in %.4fs', self, total)
1350 log.debug('REPO ACL IDS for user %s computed in %.4fs', self, total)
1342
1351
1343 return result
1352 return result
1344
1353
1345 def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1354 def repo_group_acl_ids_from_stack(self, perms=None, prefix_filter=None, cache=False):
1346 if not perms:
1355 if not perms:
1347 perms = AuthUser.repo_group_read_perms
1356 perms = AuthUser.repo_group_read_perms
1348 allowed_ids = []
1357 allowed_ids = []
1349 for k, stack_data in self.permissions['repositories_groups'].perm_origin_stack.items():
1358 for k, stack_data in self.permissions['repositories_groups'].perm_origin_stack.items():
1350 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1359 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1351 if prefix_filter and not k.startswith(prefix_filter):
1360 if prefix_filter and not k.startswith(prefix_filter):
1352 continue
1361 continue
1353 if perm in perms:
1362 if perm in perms:
1354 allowed_ids.append(obj_id)
1363 allowed_ids.append(obj_id)
1355 return allowed_ids
1364 return allowed_ids
1356
1365
1357 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1366 def repo_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1358 """
1367 """
1359 Returns list of repository group ids that user have access to based on given
1368 Returns list of repository group ids that user have access to based on given
1360 perms. The cache flag should be only used in cases that are used for
1369 perms. The cache flag should be only used in cases that are used for
1361 display purposes, NOT IN ANY CASE for permission checks.
1370 display purposes, NOT IN ANY CASE for permission checks.
1362 """
1371 """
1363 from rhodecode.model.scm import RepoGroupList
1372 from rhodecode.model.scm import RepoGroupList
1364 if not perms:
1373 if not perms:
1365 perms = AuthUser.repo_group_read_perms
1374 perms = AuthUser.repo_group_read_perms
1366
1375
1367 if not isinstance(perms, list):
1376 if not isinstance(perms, list):
1368 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1377 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1369
1378
1370 def _cached_repo_group_acl(perm_def, _name_filter):
1379 def _cached_repo_group_acl(perm_def, _name_filter):
1371 qry = RepoGroup.query()
1380 qry = RepoGroup.query()
1372 if _name_filter:
1381 if _name_filter:
1373 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1382 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1374 qry = qry.filter(
1383 qry = qry.filter(
1375 RepoGroup.group_name.ilike(ilike_expression))
1384 RepoGroup.group_name.ilike(ilike_expression))
1376
1385
1377 return [x.group_id for x in
1386 return [x.group_id for x in
1378 RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1387 RepoGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1379
1388
1380 log.debug('Computing REPO GROUP ACL IDS user %s', self)
1389 log.debug('Computing REPO GROUP ACL IDS user %s', self)
1381
1390
1382 cache_namespace_uid = 'cache_user_repo_group_acl_ids.{}'.format(self.user_id)
1391 cache_namespace_uid = 'cache_user_repo_group_acl_ids.{}'.format(self.user_id)
1383 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1392 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1384
1393
1385 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1394 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1386 def compute_repo_group_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1395 def compute_repo_group_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1387 return _cached_repo_group_acl(perm_def, _name_filter)
1396 return _cached_repo_group_acl(perm_def, _name_filter)
1388
1397
1389 start = time.time()
1398 start = time.time()
1390 result = compute_repo_group_acl_ids('v1', self.user_id, perms, name_filter)
1399 result = compute_repo_group_acl_ids('v1', self.user_id, perms, name_filter)
1391 total = time.time() - start
1400 total = time.time() - start
1392 log.debug('REPO GROUP ACL IDS for user %s computed in %.4fs', self, total)
1401 log.debug('REPO GROUP ACL IDS for user %s computed in %.4fs', self, total)
1393
1402
1394 return result
1403 return result
1395
1404
1396 def user_group_acl_ids_from_stack(self, perms=None, cache=False):
1405 def user_group_acl_ids_from_stack(self, perms=None, cache=False):
1397 if not perms:
1406 if not perms:
1398 perms = AuthUser.user_group_read_perms
1407 perms = AuthUser.user_group_read_perms
1399 allowed_ids = []
1408 allowed_ids = []
1400 for k, stack_data in self.permissions['user_groups'].perm_origin_stack.items():
1409 for k, stack_data in self.permissions['user_groups'].perm_origin_stack.items():
1401 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1410 perm, origin, obj_id = stack_data[-1] # last item is the current permission
1402 if perm in perms:
1411 if perm in perms:
1403 allowed_ids.append(obj_id)
1412 allowed_ids.append(obj_id)
1404 return allowed_ids
1413 return allowed_ids
1405
1414
1406 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1415 def user_group_acl_ids(self, perms=None, name_filter=None, cache=False):
1407 """
1416 """
1408 Returns list of user group ids that user have access to based on given
1417 Returns list of user group ids that user have access to based on given
1409 perms. The cache flag should be only used in cases that are used for
1418 perms. The cache flag should be only used in cases that are used for
1410 display purposes, NOT IN ANY CASE for permission checks.
1419 display purposes, NOT IN ANY CASE for permission checks.
1411 """
1420 """
1412 from rhodecode.model.scm import UserGroupList
1421 from rhodecode.model.scm import UserGroupList
1413 if not perms:
1422 if not perms:
1414 perms = AuthUser.user_group_read_perms
1423 perms = AuthUser.user_group_read_perms
1415
1424
1416 if not isinstance(perms, list):
1425 if not isinstance(perms, list):
1417 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1426 raise ValueError('perms parameter must be a list got {} instead'.format(perms))
1418
1427
1419 def _cached_user_group_acl(perm_def, _name_filter):
1428 def _cached_user_group_acl(perm_def, _name_filter):
1420 qry = UserGroup.query()
1429 qry = UserGroup.query()
1421 if _name_filter:
1430 if _name_filter:
1422 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1431 ilike_expression = u'%{}%'.format(safe_unicode(_name_filter))
1423 qry = qry.filter(
1432 qry = qry.filter(
1424 UserGroup.users_group_name.ilike(ilike_expression))
1433 UserGroup.users_group_name.ilike(ilike_expression))
1425
1434
1426 return [x.users_group_id for x in
1435 return [x.users_group_id for x in
1427 UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1436 UserGroupList(qry, perm_set=perm_def, extra_kwargs={'user': self})]
1428
1437
1429 log.debug('Computing USER GROUP ACL IDS user %s', self)
1438 log.debug('Computing USER GROUP ACL IDS user %s', self)
1430
1439
1431 cache_namespace_uid = 'cache_user_user_group_acl_ids.{}'.format(self.user_id)
1440 cache_namespace_uid = 'cache_user_user_group_acl_ids.{}'.format(self.user_id)
1432 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1441 region = rc_cache.get_or_create_region('cache_perms', cache_namespace_uid)
1433
1442
1434 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1443 @region.conditional_cache_on_arguments(namespace=cache_namespace_uid, condition=cache)
1435 def compute_user_group_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1444 def compute_user_group_acl_ids(cache_ver, user_id, perm_def, _name_filter):
1436 return _cached_user_group_acl(perm_def, _name_filter)
1445 return _cached_user_group_acl(perm_def, _name_filter)
1437
1446
1438 start = time.time()
1447 start = time.time()
1439 result = compute_user_group_acl_ids('v1', self.user_id, perms, name_filter)
1448 result = compute_user_group_acl_ids('v1', self.user_id, perms, name_filter)
1440 total = time.time() - start
1449 total = time.time() - start
1441 log.debug('USER GROUP ACL IDS for user %s computed in %.4fs', self, total)
1450 log.debug('USER GROUP ACL IDS for user %s computed in %.4fs', self, total)
1442
1451
1443 return result
1452 return result
1444
1453
1445 @property
1454 @property
1446 def ip_allowed(self):
1455 def ip_allowed(self):
1447 """
1456 """
1448 Checks if ip_addr used in constructor is allowed from defined list of
1457 Checks if ip_addr used in constructor is allowed from defined list of
1449 allowed ip_addresses for user
1458 allowed ip_addresses for user
1450
1459
1451 :returns: boolean, True if ip is in allowed ip range
1460 :returns: boolean, True if ip is in allowed ip range
1452 """
1461 """
1453 # check IP
1462 # check IP
1454 inherit = self.inherit_default_permissions
1463 inherit = self.inherit_default_permissions
1455 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1464 return AuthUser.check_ip_allowed(self.user_id, self.ip_addr,
1456 inherit_from_default=inherit)
1465 inherit_from_default=inherit)
1457
1466
1458 @property
1467 @property
1459 def personal_repo_group(self):
1468 def personal_repo_group(self):
1460 return RepoGroup.get_user_personal_repo_group(self.user_id)
1469 return RepoGroup.get_user_personal_repo_group(self.user_id)
1461
1470
1462 @LazyProperty
1471 @LazyProperty
1463 def feed_token(self):
1472 def feed_token(self):
1464 return self.get_instance().feed_token
1473 return self.get_instance().feed_token
1465
1474
1466 @LazyProperty
1475 @LazyProperty
1467 def artifact_token(self):
1476 def artifact_token(self):
1468 return self.get_instance().artifact_token
1477 return self.get_instance().artifact_token
1469
1478
1470 @classmethod
1479 @classmethod
1471 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1480 def check_ip_allowed(cls, user_id, ip_addr, inherit_from_default):
1472 allowed_ips = AuthUser.get_allowed_ips(
1481 allowed_ips = AuthUser.get_allowed_ips(
1473 user_id, cache=True, inherit_from_default=inherit_from_default)
1482 user_id, cache=True, inherit_from_default=inherit_from_default)
1474 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1483 if check_ip_access(source_ip=ip_addr, allowed_ips=allowed_ips):
1475 log.debug('IP:%s for user %s is in range of %s',
1484 log.debug('IP:%s for user %s is in range of %s',
1476 ip_addr, user_id, allowed_ips)
1485 ip_addr, user_id, allowed_ips)
1477 return True
1486 return True
1478 else:
1487 else:
1479 log.info('Access for IP:%s forbidden for user %s, '
1488 log.info('Access for IP:%s forbidden for user %s, '
1480 'not in %s', ip_addr, user_id, allowed_ips)
1489 'not in %s', ip_addr, user_id, allowed_ips)
1481 return False
1490 return False
1482
1491
1483 def get_branch_permissions(self, repo_name, perms=None):
1492 def get_branch_permissions(self, repo_name, perms=None):
1484 perms = perms or self.permissions_with_scope({'repo_name': repo_name})
1493 perms = perms or self.permissions_with_scope({'repo_name': repo_name})
1485 branch_perms = perms.get('repository_branches', {})
1494 branch_perms = perms.get('repository_branches', {})
1486 if not branch_perms:
1495 if not branch_perms:
1487 return {}
1496 return {}
1488 repo_branch_perms = branch_perms.get(repo_name)
1497 repo_branch_perms = branch_perms.get(repo_name)
1489 return repo_branch_perms or {}
1498 return repo_branch_perms or {}
1490
1499
1491 def get_rule_and_branch_permission(self, repo_name, branch_name):
1500 def get_rule_and_branch_permission(self, repo_name, branch_name):
1492 """
1501 """
1493 Check if this AuthUser has defined any permissions for branches. If any of
1502 Check if this AuthUser has defined any permissions for branches. If any of
1494 the rules match in order, we return the matching permissions
1503 the rules match in order, we return the matching permissions
1495 """
1504 """
1496
1505
1497 rule = default_perm = ''
1506 rule = default_perm = ''
1498
1507
1499 repo_branch_perms = self.get_branch_permissions(repo_name=repo_name)
1508 repo_branch_perms = self.get_branch_permissions(repo_name=repo_name)
1500 if not repo_branch_perms:
1509 if not repo_branch_perms:
1501 return rule, default_perm
1510 return rule, default_perm
1502
1511
1503 # now calculate the permissions
1512 # now calculate the permissions
1504 for pattern, branch_perm in repo_branch_perms.items():
1513 for pattern, branch_perm in repo_branch_perms.items():
1505 if fnmatch.fnmatch(branch_name, pattern):
1514 if fnmatch.fnmatch(branch_name, pattern):
1506 rule = '`{}`=>{}'.format(pattern, branch_perm)
1515 rule = '`{}`=>{}'.format(pattern, branch_perm)
1507 return rule, branch_perm
1516 return rule, branch_perm
1508
1517
1509 return rule, default_perm
1518 return rule, default_perm
1510
1519
1511 def get_notice_messages(self):
1520 def get_notice_messages(self):
1512
1521
1513 notice_level = 'notice-error'
1522 notice_level = 'notice-error'
1514 notice_messages = []
1523 notice_messages = []
1515 if self.is_default:
1524 if self.is_default:
1516 return [], notice_level
1525 return [], notice_level
1517
1526
1518 notices = UserNotice.query()\
1527 notices = UserNotice.query()\
1519 .filter(UserNotice.user_id == self.user_id)\
1528 .filter(UserNotice.user_id == self.user_id)\
1520 .filter(UserNotice.notice_read == false())\
1529 .filter(UserNotice.notice_read == false())\
1521 .all()
1530 .all()
1522
1531
1523 try:
1532 try:
1524 for entry in notices:
1533 for entry in notices:
1525
1534
1526 msg = {
1535 msg = {
1527 'msg_id': entry.user_notice_id,
1536 'msg_id': entry.user_notice_id,
1528 'level': entry.notification_level,
1537 'level': entry.notification_level,
1529 'subject': entry.notice_subject,
1538 'subject': entry.notice_subject,
1530 'body': entry.notice_body,
1539 'body': entry.notice_body,
1531 }
1540 }
1532 notice_messages.append(msg)
1541 notice_messages.append(msg)
1533
1542
1534 log.debug('Got user %s %s messages', self, len(notice_messages))
1543 log.debug('Got user %s %s messages', self, len(notice_messages))
1535
1544
1536 levels = [x['level'] for x in notice_messages]
1545 levels = [x['level'] for x in notice_messages]
1537 notice_level = 'notice-error' if 'error' in levels else 'notice-warning'
1546 notice_level = 'notice-error' if 'error' in levels else 'notice-warning'
1538 except Exception:
1547 except Exception:
1539 pass
1548 pass
1540
1549
1541 return notice_messages, notice_level
1550 return notice_messages, notice_level
1542
1551
1543 def __repr__(self):
1552 def __repr__(self):
1544 return self.repr_user(self.user_id, self.username, self.ip_addr, self.is_authenticated)
1553 return self.repr_user(self.user_id, self.username, self.ip_addr, self.is_authenticated)
1545
1554
1546 def set_authenticated(self, authenticated=True):
1555 def set_authenticated(self, authenticated=True):
1547 if self.user_id != self.anonymous_user.user_id:
1556 if self.user_id != self.anonymous_user.user_id:
1548 self.is_authenticated = authenticated
1557 self.is_authenticated = authenticated
1549
1558
1550 def get_cookie_store(self):
1559 def get_cookie_store(self):
1551 return {
1560 return {
1552 'username': self.username,
1561 'username': self.username,
1553 'password': md5(self.password or ''),
1562 'password': md5(self.password or ''),
1554 'user_id': self.user_id,
1563 'user_id': self.user_id,
1555 'is_authenticated': self.is_authenticated
1564 'is_authenticated': self.is_authenticated
1556 }
1565 }
1557
1566
1558 @classmethod
1567 @classmethod
1559 def repr_user(cls, user_id=0, username='ANONYMOUS', ip='0.0.0.0', is_authenticated=False):
1568 def repr_user(cls, user_id=0, username='ANONYMOUS', ip='0.0.0.0', is_authenticated=False):
1560 tmpl = "<AuthUser('id:{}[{}] ip:{} auth:{}')>"
1569 tmpl = "<AuthUser('id:{}[{}] ip:{} auth:{}')>"
1561 return tmpl.format(user_id, username, ip, is_authenticated)
1570 return tmpl.format(user_id, username, ip, is_authenticated)
1562
1571
1563 @classmethod
1572 @classmethod
1564 def from_cookie_store(cls, cookie_store):
1573 def from_cookie_store(cls, cookie_store):
1565 """
1574 """
1566 Creates AuthUser from a cookie store
1575 Creates AuthUser from a cookie store
1567
1576
1568 :param cls:
1577 :param cls:
1569 :param cookie_store:
1578 :param cookie_store:
1570 """
1579 """
1571 user_id = cookie_store.get('user_id')
1580 user_id = cookie_store.get('user_id')
1572 username = cookie_store.get('username')
1581 username = cookie_store.get('username')
1573 api_key = cookie_store.get('api_key')
1582 api_key = cookie_store.get('api_key')
1574 return AuthUser(user_id, api_key, username)
1583 return AuthUser(user_id, api_key, username)
1575
1584
1576 @classmethod
1585 @classmethod
1577 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1586 def get_allowed_ips(cls, user_id, cache=False, inherit_from_default=False):
1578 _set = set()
1587 _set = set()
1579
1588
1580 if inherit_from_default:
1589 if inherit_from_default:
1581 def_user_id = User.get_default_user(cache=True).user_id
1590 def_user_id = User.get_default_user(cache=True).user_id
1582 default_ips = UserIpMap.query().filter(UserIpMap.user_id == def_user_id)
1591 default_ips = UserIpMap.query().filter(UserIpMap.user_id == def_user_id)
1583 if cache:
1592 if cache:
1584 default_ips = default_ips.options(
1593 default_ips = default_ips.options(
1585 FromCache("sql_cache_short", "get_user_ips_default"))
1594 FromCache("sql_cache_short", "get_user_ips_default"))
1586
1595
1587 # populate from default user
1596 # populate from default user
1588 for ip in default_ips:
1597 for ip in default_ips:
1589 try:
1598 try:
1590 _set.add(ip.ip_addr)
1599 _set.add(ip.ip_addr)
1591 except ObjectDeletedError:
1600 except ObjectDeletedError:
1592 # since we use heavy caching sometimes it happens that
1601 # since we use heavy caching sometimes it happens that
1593 # we get deleted objects here, we just skip them
1602 # we get deleted objects here, we just skip them
1594 pass
1603 pass
1595
1604
1596 # NOTE:(marcink) we don't want to load any rules for empty
1605 # NOTE:(marcink) we don't want to load any rules for empty
1597 # user_id which is the case of access of non logged users when anonymous
1606 # user_id which is the case of access of non logged users when anonymous
1598 # access is disabled
1607 # access is disabled
1599 user_ips = []
1608 user_ips = []
1600 if user_id:
1609 if user_id:
1601 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1610 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
1602 if cache:
1611 if cache:
1603 user_ips = user_ips.options(
1612 user_ips = user_ips.options(
1604 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1613 FromCache("sql_cache_short", "get_user_ips_%s" % user_id))
1605
1614
1606 for ip in user_ips:
1615 for ip in user_ips:
1607 try:
1616 try:
1608 _set.add(ip.ip_addr)
1617 _set.add(ip.ip_addr)
1609 except ObjectDeletedError:
1618 except ObjectDeletedError:
1610 # since we use heavy caching sometimes it happens that we get
1619 # since we use heavy caching sometimes it happens that we get
1611 # deleted objects here, we just skip them
1620 # deleted objects here, we just skip them
1612 pass
1621 pass
1613 return _set or {ip for ip in ['0.0.0.0/0', '::/0']}
1622 return _set or {ip for ip in ['0.0.0.0/0', '::/0']}
1614
1623
1615
1624
1616 def set_available_permissions(settings):
1625 def set_available_permissions(settings):
1617 """
1626 """
1618 This function will propagate pyramid settings with all available defined
1627 This function will propagate pyramid settings with all available defined
1619 permission given in db. We don't want to check each time from db for new
1628 permission given in db. We don't want to check each time from db for new
1620 permissions since adding a new permission also requires application restart
1629 permissions since adding a new permission also requires application restart
1621 ie. to decorate new views with the newly created permission
1630 ie. to decorate new views with the newly created permission
1622
1631
1623 :param settings: current pyramid registry.settings
1632 :param settings: current pyramid registry.settings
1624
1633
1625 """
1634 """
1626 log.debug('auth: getting information about all available permissions')
1635 log.debug('auth: getting information about all available permissions')
1627 try:
1636 try:
1628 sa = meta.Session
1637 sa = meta.Session
1629 all_perms = sa.query(Permission).all()
1638 all_perms = sa.query(Permission).all()
1630 settings.setdefault('available_permissions',
1639 settings.setdefault('available_permissions',
1631 [x.permission_name for x in all_perms])
1640 [x.permission_name for x in all_perms])
1632 log.debug('auth: set available permissions')
1641 log.debug('auth: set available permissions')
1633 except Exception:
1642 except Exception:
1634 log.exception('Failed to fetch permissions from the database.')
1643 log.exception('Failed to fetch permissions from the database.')
1635 raise
1644 raise
1636
1645
1637
1646
1638 def get_csrf_token(session, force_new=False, save_if_missing=True):
1647 def get_csrf_token(session, force_new=False, save_if_missing=True):
1639 """
1648 """
1640 Return the current authentication token, creating one if one doesn't
1649 Return the current authentication token, creating one if one doesn't
1641 already exist and the save_if_missing flag is present.
1650 already exist and the save_if_missing flag is present.
1642
1651
1643 :param session: pass in the pyramid session, else we use the global ones
1652 :param session: pass in the pyramid session, else we use the global ones
1644 :param force_new: force to re-generate the token and store it in session
1653 :param force_new: force to re-generate the token and store it in session
1645 :param save_if_missing: save the newly generated token if it's missing in
1654 :param save_if_missing: save the newly generated token if it's missing in
1646 session
1655 session
1647 """
1656 """
1648 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1657 # NOTE(marcink): probably should be replaced with below one from pyramid 1.9
1649 # from pyramid.csrf import get_csrf_token
1658 # from pyramid.csrf import get_csrf_token
1650
1659
1651 if (csrf_token_key not in session and save_if_missing) or force_new:
1660 if (csrf_token_key not in session and save_if_missing) or force_new:
1652 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1661 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
1653 session[csrf_token_key] = token
1662 session[csrf_token_key] = token
1654 if hasattr(session, 'save'):
1663 if hasattr(session, 'save'):
1655 session.save()
1664 session.save()
1656 return session.get(csrf_token_key)
1665 return session.get(csrf_token_key)
1657
1666
1658
1667
1659 def get_request(perm_class_instance):
1668 def get_request(perm_class_instance):
1660 from pyramid.threadlocal import get_current_request
1669 from pyramid.threadlocal import get_current_request
1661 pyramid_request = get_current_request()
1670 pyramid_request = get_current_request()
1662 return pyramid_request
1671 return pyramid_request
1663
1672
1664
1673
1665 # CHECK DECORATORS
1674 # CHECK DECORATORS
1666 class CSRFRequired(object):
1675 class CSRFRequired(object):
1667 """
1676 """
1668 Decorator for authenticating a form
1677 Decorator for authenticating a form
1669
1678
1670 This decorator uses an authorization token stored in the client's
1679 This decorator uses an authorization token stored in the client's
1671 session for prevention of certain Cross-site request forgery (CSRF)
1680 session for prevention of certain Cross-site request forgery (CSRF)
1672 attacks (See
1681 attacks (See
1673 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1682 http://en.wikipedia.org/wiki/Cross-site_request_forgery for more
1674 information).
1683 information).
1675
1684
1676 For use with the ``secure_form`` helper functions.
1685 For use with the ``secure_form`` helper functions.
1677
1686
1678 """
1687 """
1679 def __init__(self, token=csrf_token_key, header='X-CSRF-Token', except_methods=None):
1688 def __init__(self, token=csrf_token_key, header='X-CSRF-Token', except_methods=None):
1680 self.token = token
1689 self.token = token
1681 self.header = header
1690 self.header = header
1682 self.except_methods = except_methods or []
1691 self.except_methods = except_methods or []
1683
1692
1684 def __call__(self, func):
1693 def __call__(self, func):
1685 return get_cython_compat_decorator(self.__wrapper, func)
1694 return get_cython_compat_decorator(self.__wrapper, func)
1686
1695
1687 def _get_csrf(self, _request):
1696 def _get_csrf(self, _request):
1688 return _request.POST.get(self.token, _request.headers.get(self.header))
1697 return _request.POST.get(self.token, _request.headers.get(self.header))
1689
1698
1690 def check_csrf(self, _request, cur_token):
1699 def check_csrf(self, _request, cur_token):
1691 supplied_token = self._get_csrf(_request)
1700 supplied_token = self._get_csrf(_request)
1692 return supplied_token and supplied_token == cur_token
1701 return supplied_token and supplied_token == cur_token
1693
1702
1694 def _get_request(self):
1703 def _get_request(self):
1695 return get_request(self)
1704 return get_request(self)
1696
1705
1697 def __wrapper(self, func, *fargs, **fkwargs):
1706 def __wrapper(self, func, *fargs, **fkwargs):
1698 request = self._get_request()
1707 request = self._get_request()
1699
1708
1700 if request.method in self.except_methods:
1709 if request.method in self.except_methods:
1701 return func(*fargs, **fkwargs)
1710 return func(*fargs, **fkwargs)
1702
1711
1703 cur_token = get_csrf_token(request.session, save_if_missing=False)
1712 cur_token = get_csrf_token(request.session, save_if_missing=False)
1704 if self.check_csrf(request, cur_token):
1713 if self.check_csrf(request, cur_token):
1705 if request.POST.get(self.token):
1714 if request.POST.get(self.token):
1706 del request.POST[self.token]
1715 del request.POST[self.token]
1707 return func(*fargs, **fkwargs)
1716 return func(*fargs, **fkwargs)
1708 else:
1717 else:
1709 reason = 'token-missing'
1718 reason = 'token-missing'
1710 supplied_token = self._get_csrf(request)
1719 supplied_token = self._get_csrf(request)
1711 if supplied_token and cur_token != supplied_token:
1720 if supplied_token and cur_token != supplied_token:
1712 reason = 'token-mismatch [%s:%s]' % (
1721 reason = 'token-mismatch [%s:%s]' % (
1713 cur_token or ''[:6], supplied_token or ''[:6])
1722 cur_token or ''[:6], supplied_token or ''[:6])
1714
1723
1715 csrf_message = \
1724 csrf_message = \
1716 ("Cross-site request forgery detected, request denied. See "
1725 ("Cross-site request forgery detected, request denied. See "
1717 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1726 "http://en.wikipedia.org/wiki/Cross-site_request_forgery for "
1718 "more information.")
1727 "more information.")
1719 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1728 log.warn('Cross-site request forgery detected, request %r DENIED: %s '
1720 'REMOTE_ADDR:%s, HEADERS:%s' % (
1729 'REMOTE_ADDR:%s, HEADERS:%s' % (
1721 request, reason, request.remote_addr, request.headers))
1730 request, reason, request.remote_addr, request.headers))
1722
1731
1723 raise HTTPForbidden(explanation=csrf_message)
1732 raise HTTPForbidden(explanation=csrf_message)
1724
1733
1725
1734
1726 class LoginRequired(object):
1735 class LoginRequired(object):
1727 """
1736 """
1728 Must be logged in to execute this function else
1737 Must be logged in to execute this function else
1729 redirect to login page
1738 redirect to login page
1730
1739
1731 :param api_access: if enabled this checks only for valid auth token
1740 :param api_access: if enabled this checks only for valid auth token
1732 and grants access based on valid token
1741 and grants access based on valid token
1733 """
1742 """
1734 def __init__(self, auth_token_access=None):
1743 def __init__(self, auth_token_access=None):
1735 self.auth_token_access = auth_token_access
1744 self.auth_token_access = auth_token_access
1736 if self.auth_token_access:
1745 if self.auth_token_access:
1737 valid_type = set(auth_token_access).intersection(set(UserApiKeys.ROLES))
1746 valid_type = set(auth_token_access).intersection(set(UserApiKeys.ROLES))
1738 if not valid_type:
1747 if not valid_type:
1739 raise ValueError('auth_token_access must be on of {}, got {}'.format(
1748 raise ValueError('auth_token_access must be on of {}, got {}'.format(
1740 UserApiKeys.ROLES, auth_token_access))
1749 UserApiKeys.ROLES, auth_token_access))
1741
1750
1742 def __call__(self, func):
1751 def __call__(self, func):
1743 return get_cython_compat_decorator(self.__wrapper, func)
1752 return get_cython_compat_decorator(self.__wrapper, func)
1744
1753
1745 def _get_request(self):
1754 def _get_request(self):
1746 return get_request(self)
1755 return get_request(self)
1747
1756
1748 def __wrapper(self, func, *fargs, **fkwargs):
1757 def __wrapper(self, func, *fargs, **fkwargs):
1749 from rhodecode.lib import helpers as h
1758 from rhodecode.lib import helpers as h
1750 cls = fargs[0]
1759 cls = fargs[0]
1751 user = cls._rhodecode_user
1760 user = cls._rhodecode_user
1752 request = self._get_request()
1761 request = self._get_request()
1753 _ = request.translate
1762 _ = request.translate
1754
1763
1755 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1764 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
1756 log.debug('Starting login restriction checks for user: %s', user)
1765 log.debug('Starting login restriction checks for user: %s', user)
1757 # check if our IP is allowed
1766 # check if our IP is allowed
1758 ip_access_valid = True
1767 ip_access_valid = True
1759 if not user.ip_allowed:
1768 if not user.ip_allowed:
1760 h.flash(h.literal(_('IP {} not allowed'.format(user.ip_addr))),
1769 h.flash(h.literal(_('IP {} not allowed'.format(user.ip_addr))),
1761 category='warning')
1770 category='warning')
1762 ip_access_valid = False
1771 ip_access_valid = False
1763
1772
1764 # we used stored token that is extract from GET or URL param (if any)
1773 # we used stored token that is extract from GET or URL param (if any)
1765 _auth_token = request.user_auth_token
1774 _auth_token = request.user_auth_token
1766
1775
1767 # check if we used an AUTH_TOKEN and it's a valid one
1776 # check if we used an AUTH_TOKEN and it's a valid one
1768 # defined white-list of controllers which API access will be enabled
1777 # defined white-list of controllers which API access will be enabled
1769 whitelist = None
1778 whitelist = None
1770 if self.auth_token_access:
1779 if self.auth_token_access:
1771 # since this location is allowed by @LoginRequired decorator it's our
1780 # since this location is allowed by @LoginRequired decorator it's our
1772 # only whitelist
1781 # only whitelist
1773 whitelist = [loc]
1782 whitelist = [loc]
1774 auth_token_access_valid = allowed_auth_token_access(
1783 auth_token_access_valid = allowed_auth_token_access(
1775 loc, whitelist=whitelist, auth_token=_auth_token)
1784 loc, whitelist=whitelist, auth_token=_auth_token)
1776
1785
1777 # explicit controller is enabled or API is in our whitelist
1786 # explicit controller is enabled or API is in our whitelist
1778 if auth_token_access_valid:
1787 if auth_token_access_valid:
1779 log.debug('Checking AUTH TOKEN access for %s', cls)
1788 log.debug('Checking AUTH TOKEN access for %s', cls)
1780 db_user = user.get_instance()
1789 db_user = user.get_instance()
1781
1790
1782 if db_user:
1791 if db_user:
1783 if self.auth_token_access:
1792 if self.auth_token_access:
1784 roles = self.auth_token_access
1793 roles = self.auth_token_access
1785 else:
1794 else:
1786 roles = [UserApiKeys.ROLE_HTTP]
1795 roles = [UserApiKeys.ROLE_HTTP]
1787 log.debug('AUTH TOKEN: checking auth for user %s and roles %s',
1796 log.debug('AUTH TOKEN: checking auth for user %s and roles %s',
1788 db_user, roles)
1797 db_user, roles)
1789 token_match = db_user.authenticate_by_token(
1798 token_match = db_user.authenticate_by_token(
1790 _auth_token, roles=roles)
1799 _auth_token, roles=roles)
1791 else:
1800 else:
1792 log.debug('Unable to fetch db instance for auth user: %s', user)
1801 log.debug('Unable to fetch db instance for auth user: %s', user)
1793 token_match = False
1802 token_match = False
1794
1803
1795 if _auth_token and token_match:
1804 if _auth_token and token_match:
1796 auth_token_access_valid = True
1805 auth_token_access_valid = True
1797 log.debug('AUTH TOKEN ****%s is VALID', _auth_token[-4:])
1806 log.debug('AUTH TOKEN ****%s is VALID', _auth_token[-4:])
1798 else:
1807 else:
1799 auth_token_access_valid = False
1808 auth_token_access_valid = False
1800 if not _auth_token:
1809 if not _auth_token:
1801 log.debug("AUTH TOKEN *NOT* present in request")
1810 log.debug("AUTH TOKEN *NOT* present in request")
1802 else:
1811 else:
1803 log.warning("AUTH TOKEN ****%s *NOT* valid", _auth_token[-4:])
1812 log.warning("AUTH TOKEN ****%s *NOT* valid", _auth_token[-4:])
1804
1813
1805 log.debug('Checking if %s is authenticated @ %s', user.username, loc)
1814 log.debug('Checking if %s is authenticated @ %s', user.username, loc)
1806 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1815 reason = 'RHODECODE_AUTH' if user.is_authenticated \
1807 else 'AUTH_TOKEN_AUTH'
1816 else 'AUTH_TOKEN_AUTH'
1808
1817
1809 if ip_access_valid and (
1818 if ip_access_valid and (
1810 user.is_authenticated or auth_token_access_valid):
1819 user.is_authenticated or auth_token_access_valid):
1811 log.info('user %s authenticating with:%s IS authenticated on func %s',
1820 log.info('user %s authenticating with:%s IS authenticated on func %s',
1812 user, reason, loc)
1821 user, reason, loc)
1813
1822
1814 return func(*fargs, **fkwargs)
1823 return func(*fargs, **fkwargs)
1815 else:
1824 else:
1816 log.warning(
1825 log.warning(
1817 'user %s authenticating with:%s NOT authenticated on '
1826 'user %s authenticating with:%s NOT authenticated on '
1818 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s',
1827 'func: %s: IP_ACCESS:%s AUTH_TOKEN_ACCESS:%s',
1819 user, reason, loc, ip_access_valid, auth_token_access_valid)
1828 user, reason, loc, ip_access_valid, auth_token_access_valid)
1820 # we preserve the get PARAM
1829 # we preserve the get PARAM
1821 came_from = get_came_from(request)
1830 came_from = get_came_from(request)
1822
1831
1823 log.debug('redirecting to login page with %s', came_from)
1832 log.debug('redirecting to login page with %s', came_from)
1824 raise HTTPFound(
1833 raise HTTPFound(
1825 h.route_path('login', _query={'came_from': came_from}))
1834 h.route_path('login', _query={'came_from': came_from}))
1826
1835
1827
1836
1828 class NotAnonymous(object):
1837 class NotAnonymous(object):
1829 """
1838 """
1830 Must be logged in to execute this function else
1839 Must be logged in to execute this function else
1831 redirect to login page
1840 redirect to login page
1832 """
1841 """
1833
1842
1834 def __call__(self, func):
1843 def __call__(self, func):
1835 return get_cython_compat_decorator(self.__wrapper, func)
1844 return get_cython_compat_decorator(self.__wrapper, func)
1836
1845
1837 def _get_request(self):
1846 def _get_request(self):
1838 return get_request(self)
1847 return get_request(self)
1839
1848
1840 def __wrapper(self, func, *fargs, **fkwargs):
1849 def __wrapper(self, func, *fargs, **fkwargs):
1841 import rhodecode.lib.helpers as h
1850 import rhodecode.lib.helpers as h
1842 cls = fargs[0]
1851 cls = fargs[0]
1843 self.user = cls._rhodecode_user
1852 self.user = cls._rhodecode_user
1844 request = self._get_request()
1853 request = self._get_request()
1845 _ = request.translate
1854 _ = request.translate
1846 log.debug('Checking if user is not anonymous @%s', cls)
1855 log.debug('Checking if user is not anonymous @%s', cls)
1847
1856
1848 anonymous = self.user.username == User.DEFAULT_USER
1857 anonymous = self.user.username == User.DEFAULT_USER
1849
1858
1850 if anonymous:
1859 if anonymous:
1851 came_from = get_came_from(request)
1860 came_from = get_came_from(request)
1852 h.flash(_('You need to be a registered user to '
1861 h.flash(_('You need to be a registered user to '
1853 'perform this action'),
1862 'perform this action'),
1854 category='warning')
1863 category='warning')
1855 raise HTTPFound(
1864 raise HTTPFound(
1856 h.route_path('login', _query={'came_from': came_from}))
1865 h.route_path('login', _query={'came_from': came_from}))
1857 else:
1866 else:
1858 return func(*fargs, **fkwargs)
1867 return func(*fargs, **fkwargs)
1859
1868
1860
1869
1861 class PermsDecorator(object):
1870 class PermsDecorator(object):
1862 """
1871 """
1863 Base class for controller decorators, we extract the current user from
1872 Base class for controller decorators, we extract the current user from
1864 the class itself, which has it stored in base controllers
1873 the class itself, which has it stored in base controllers
1865 """
1874 """
1866
1875
1867 def __init__(self, *required_perms):
1876 def __init__(self, *required_perms):
1868 self.required_perms = set(required_perms)
1877 self.required_perms = set(required_perms)
1869
1878
1870 def __call__(self, func):
1879 def __call__(self, func):
1871 return get_cython_compat_decorator(self.__wrapper, func)
1880 return get_cython_compat_decorator(self.__wrapper, func)
1872
1881
1873 def _get_request(self):
1882 def _get_request(self):
1874 return get_request(self)
1883 return get_request(self)
1875
1884
1876 def __wrapper(self, func, *fargs, **fkwargs):
1885 def __wrapper(self, func, *fargs, **fkwargs):
1877 import rhodecode.lib.helpers as h
1886 import rhodecode.lib.helpers as h
1878 cls = fargs[0]
1887 cls = fargs[0]
1879 _user = cls._rhodecode_user
1888 _user = cls._rhodecode_user
1880 request = self._get_request()
1889 request = self._get_request()
1881 _ = request.translate
1890 _ = request.translate
1882
1891
1883 log.debug('checking %s permissions %s for %s %s',
1892 log.debug('checking %s permissions %s for %s %s',
1884 self.__class__.__name__, self.required_perms, cls, _user)
1893 self.__class__.__name__, self.required_perms, cls, _user)
1885
1894
1886 if self.check_permissions(_user):
1895 if self.check_permissions(_user):
1887 log.debug('Permission granted for %s %s', cls, _user)
1896 log.debug('Permission granted for %s %s', cls, _user)
1888 return func(*fargs, **fkwargs)
1897 return func(*fargs, **fkwargs)
1889
1898
1890 else:
1899 else:
1891 log.debug('Permission denied for %s %s', cls, _user)
1900 log.debug('Permission denied for %s %s', cls, _user)
1892 anonymous = _user.username == User.DEFAULT_USER
1901 anonymous = _user.username == User.DEFAULT_USER
1893
1902
1894 if anonymous:
1903 if anonymous:
1895 came_from = get_came_from(self._get_request())
1904 came_from = get_came_from(self._get_request())
1896 h.flash(_('You need to be signed in to view this page'),
1905 h.flash(_('You need to be signed in to view this page'),
1897 category='warning')
1906 category='warning')
1898 raise HTTPFound(
1907 raise HTTPFound(
1899 h.route_path('login', _query={'came_from': came_from}))
1908 h.route_path('login', _query={'came_from': came_from}))
1900
1909
1901 else:
1910 else:
1902 # redirect with 404 to prevent resource discovery
1911 # redirect with 404 to prevent resource discovery
1903 raise HTTPNotFound()
1912 raise HTTPNotFound()
1904
1913
1905 def check_permissions(self, user):
1914 def check_permissions(self, user):
1906 """Dummy function for overriding"""
1915 """Dummy function for overriding"""
1907 raise NotImplementedError(
1916 raise NotImplementedError(
1908 'You have to write this function in child class')
1917 'You have to write this function in child class')
1909
1918
1910
1919
1911 class HasPermissionAllDecorator(PermsDecorator):
1920 class HasPermissionAllDecorator(PermsDecorator):
1912 """
1921 """
1913 Checks for access permission for all given predicates. All of them
1922 Checks for access permission for all given predicates. All of them
1914 have to be meet in order to fulfill the request
1923 have to be meet in order to fulfill the request
1915 """
1924 """
1916
1925
1917 def check_permissions(self, user):
1926 def check_permissions(self, user):
1918 perms = user.permissions_with_scope({})
1927 perms = user.permissions_with_scope({})
1919 if self.required_perms.issubset(perms['global']):
1928 if self.required_perms.issubset(perms['global']):
1920 return True
1929 return True
1921 return False
1930 return False
1922
1931
1923
1932
1924 class HasPermissionAnyDecorator(PermsDecorator):
1933 class HasPermissionAnyDecorator(PermsDecorator):
1925 """
1934 """
1926 Checks for access permission for any of given predicates. In order to
1935 Checks for access permission for any of given predicates. In order to
1927 fulfill the request any of predicates must be meet
1936 fulfill the request any of predicates must be meet
1928 """
1937 """
1929
1938
1930 def check_permissions(self, user):
1939 def check_permissions(self, user):
1931 perms = user.permissions_with_scope({})
1940 perms = user.permissions_with_scope({})
1932 if self.required_perms.intersection(perms['global']):
1941 if self.required_perms.intersection(perms['global']):
1933 return True
1942 return True
1934 return False
1943 return False
1935
1944
1936
1945
1937 class HasRepoPermissionAllDecorator(PermsDecorator):
1946 class HasRepoPermissionAllDecorator(PermsDecorator):
1938 """
1947 """
1939 Checks for access permission for all given predicates for specific
1948 Checks for access permission for all given predicates for specific
1940 repository. All of them have to be meet in order to fulfill the request
1949 repository. All of them have to be meet in order to fulfill the request
1941 """
1950 """
1942 def _get_repo_name(self):
1951 def _get_repo_name(self):
1943 _request = self._get_request()
1952 _request = self._get_request()
1944 return get_repo_slug(_request)
1953 return get_repo_slug(_request)
1945
1954
1946 def check_permissions(self, user):
1955 def check_permissions(self, user):
1947 perms = user.permissions
1956 perms = user.permissions
1948 repo_name = self._get_repo_name()
1957 repo_name = self._get_repo_name()
1949
1958
1950 try:
1959 try:
1951 user_perms = {perms['repositories'][repo_name]}
1960 user_perms = {perms['repositories'][repo_name]}
1952 except KeyError:
1961 except KeyError:
1953 log.debug('cannot locate repo with name: `%s` in permissions defs',
1962 log.debug('cannot locate repo with name: `%s` in permissions defs',
1954 repo_name)
1963 repo_name)
1955 return False
1964 return False
1956
1965
1957 log.debug('checking `%s` permissions for repo `%s`',
1966 log.debug('checking `%s` permissions for repo `%s`',
1958 user_perms, repo_name)
1967 user_perms, repo_name)
1959 if self.required_perms.issubset(user_perms):
1968 if self.required_perms.issubset(user_perms):
1960 return True
1969 return True
1961 return False
1970 return False
1962
1971
1963
1972
1964 class HasRepoPermissionAnyDecorator(PermsDecorator):
1973 class HasRepoPermissionAnyDecorator(PermsDecorator):
1965 """
1974 """
1966 Checks for access permission for any of given predicates for specific
1975 Checks for access permission for any of given predicates for specific
1967 repository. In order to fulfill the request any of predicates must be meet
1976 repository. In order to fulfill the request any of predicates must be meet
1968 """
1977 """
1969 def _get_repo_name(self):
1978 def _get_repo_name(self):
1970 _request = self._get_request()
1979 _request = self._get_request()
1971 return get_repo_slug(_request)
1980 return get_repo_slug(_request)
1972
1981
1973 def check_permissions(self, user):
1982 def check_permissions(self, user):
1974 perms = user.permissions
1983 perms = user.permissions
1975 repo_name = self._get_repo_name()
1984 repo_name = self._get_repo_name()
1976
1985
1977 try:
1986 try:
1978 user_perms = {perms['repositories'][repo_name]}
1987 user_perms = {perms['repositories'][repo_name]}
1979 except KeyError:
1988 except KeyError:
1980 log.debug(
1989 log.debug(
1981 'cannot locate repo with name: `%s` in permissions defs',
1990 'cannot locate repo with name: `%s` in permissions defs',
1982 repo_name)
1991 repo_name)
1983 return False
1992 return False
1984
1993
1985 log.debug('checking `%s` permissions for repo `%s`',
1994 log.debug('checking `%s` permissions for repo `%s`',
1986 user_perms, repo_name)
1995 user_perms, repo_name)
1987 if self.required_perms.intersection(user_perms):
1996 if self.required_perms.intersection(user_perms):
1988 return True
1997 return True
1989 return False
1998 return False
1990
1999
1991
2000
1992 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
2001 class HasRepoGroupPermissionAllDecorator(PermsDecorator):
1993 """
2002 """
1994 Checks for access permission for all given predicates for specific
2003 Checks for access permission for all given predicates for specific
1995 repository group. All of them have to be meet in order to
2004 repository group. All of them have to be meet in order to
1996 fulfill the request
2005 fulfill the request
1997 """
2006 """
1998 def _get_repo_group_name(self):
2007 def _get_repo_group_name(self):
1999 _request = self._get_request()
2008 _request = self._get_request()
2000 return get_repo_group_slug(_request)
2009 return get_repo_group_slug(_request)
2001
2010
2002 def check_permissions(self, user):
2011 def check_permissions(self, user):
2003 perms = user.permissions
2012 perms = user.permissions
2004 group_name = self._get_repo_group_name()
2013 group_name = self._get_repo_group_name()
2005 try:
2014 try:
2006 user_perms = {perms['repositories_groups'][group_name]}
2015 user_perms = {perms['repositories_groups'][group_name]}
2007 except KeyError:
2016 except KeyError:
2008 log.debug(
2017 log.debug(
2009 'cannot locate repo group with name: `%s` in permissions defs',
2018 'cannot locate repo group with name: `%s` in permissions defs',
2010 group_name)
2019 group_name)
2011 return False
2020 return False
2012
2021
2013 log.debug('checking `%s` permissions for repo group `%s`',
2022 log.debug('checking `%s` permissions for repo group `%s`',
2014 user_perms, group_name)
2023 user_perms, group_name)
2015 if self.required_perms.issubset(user_perms):
2024 if self.required_perms.issubset(user_perms):
2016 return True
2025 return True
2017 return False
2026 return False
2018
2027
2019
2028
2020 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
2029 class HasRepoGroupPermissionAnyDecorator(PermsDecorator):
2021 """
2030 """
2022 Checks for access permission for any of given predicates for specific
2031 Checks for access permission for any of given predicates for specific
2023 repository group. In order to fulfill the request any
2032 repository group. In order to fulfill the request any
2024 of predicates must be met
2033 of predicates must be met
2025 """
2034 """
2026 def _get_repo_group_name(self):
2035 def _get_repo_group_name(self):
2027 _request = self._get_request()
2036 _request = self._get_request()
2028 return get_repo_group_slug(_request)
2037 return get_repo_group_slug(_request)
2029
2038
2030 def check_permissions(self, user):
2039 def check_permissions(self, user):
2031 perms = user.permissions
2040 perms = user.permissions
2032 group_name = self._get_repo_group_name()
2041 group_name = self._get_repo_group_name()
2033
2042
2034 try:
2043 try:
2035 user_perms = {perms['repositories_groups'][group_name]}
2044 user_perms = {perms['repositories_groups'][group_name]}
2036 except KeyError:
2045 except KeyError:
2037 log.debug(
2046 log.debug(
2038 'cannot locate repo group with name: `%s` in permissions defs',
2047 'cannot locate repo group with name: `%s` in permissions defs',
2039 group_name)
2048 group_name)
2040 return False
2049 return False
2041
2050
2042 log.debug('checking `%s` permissions for repo group `%s`',
2051 log.debug('checking `%s` permissions for repo group `%s`',
2043 user_perms, group_name)
2052 user_perms, group_name)
2044 if self.required_perms.intersection(user_perms):
2053 if self.required_perms.intersection(user_perms):
2045 return True
2054 return True
2046 return False
2055 return False
2047
2056
2048
2057
2049 class HasUserGroupPermissionAllDecorator(PermsDecorator):
2058 class HasUserGroupPermissionAllDecorator(PermsDecorator):
2050 """
2059 """
2051 Checks for access permission for all given predicates for specific
2060 Checks for access permission for all given predicates for specific
2052 user group. All of them have to be meet in order to fulfill the request
2061 user group. All of them have to be meet in order to fulfill the request
2053 """
2062 """
2054 def _get_user_group_name(self):
2063 def _get_user_group_name(self):
2055 _request = self._get_request()
2064 _request = self._get_request()
2056 return get_user_group_slug(_request)
2065 return get_user_group_slug(_request)
2057
2066
2058 def check_permissions(self, user):
2067 def check_permissions(self, user):
2059 perms = user.permissions
2068 perms = user.permissions
2060 group_name = self._get_user_group_name()
2069 group_name = self._get_user_group_name()
2061 try:
2070 try:
2062 user_perms = {perms['user_groups'][group_name]}
2071 user_perms = {perms['user_groups'][group_name]}
2063 except KeyError:
2072 except KeyError:
2064 return False
2073 return False
2065
2074
2066 if self.required_perms.issubset(user_perms):
2075 if self.required_perms.issubset(user_perms):
2067 return True
2076 return True
2068 return False
2077 return False
2069
2078
2070
2079
2071 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
2080 class HasUserGroupPermissionAnyDecorator(PermsDecorator):
2072 """
2081 """
2073 Checks for access permission for any of given predicates for specific
2082 Checks for access permission for any of given predicates for specific
2074 user group. In order to fulfill the request any of predicates must be meet
2083 user group. In order to fulfill the request any of predicates must be meet
2075 """
2084 """
2076 def _get_user_group_name(self):
2085 def _get_user_group_name(self):
2077 _request = self._get_request()
2086 _request = self._get_request()
2078 return get_user_group_slug(_request)
2087 return get_user_group_slug(_request)
2079
2088
2080 def check_permissions(self, user):
2089 def check_permissions(self, user):
2081 perms = user.permissions
2090 perms = user.permissions
2082 group_name = self._get_user_group_name()
2091 group_name = self._get_user_group_name()
2083 try:
2092 try:
2084 user_perms = {perms['user_groups'][group_name]}
2093 user_perms = {perms['user_groups'][group_name]}
2085 except KeyError:
2094 except KeyError:
2086 return False
2095 return False
2087
2096
2088 if self.required_perms.intersection(user_perms):
2097 if self.required_perms.intersection(user_perms):
2089 return True
2098 return True
2090 return False
2099 return False
2091
2100
2092
2101
2093 # CHECK FUNCTIONS
2102 # CHECK FUNCTIONS
2094 class PermsFunction(object):
2103 class PermsFunction(object):
2095 """Base function for other check functions"""
2104 """Base function for other check functions"""
2096
2105
2097 def __init__(self, *perms):
2106 def __init__(self, *perms):
2098 self.required_perms = set(perms)
2107 self.required_perms = set(perms)
2099 self.repo_name = None
2108 self.repo_name = None
2100 self.repo_group_name = None
2109 self.repo_group_name = None
2101 self.user_group_name = None
2110 self.user_group_name = None
2102
2111
2103 def __bool__(self):
2112 def __bool__(self):
2104 import inspect
2113 import inspect
2105 frame = inspect.currentframe()
2114 frame = inspect.currentframe()
2106 stack_trace = traceback.format_stack(frame)
2115 stack_trace = traceback.format_stack(frame)
2107 log.error('Checking bool value on a class instance of perm '
2116 log.error('Checking bool value on a class instance of perm '
2108 'function is not allowed: %s', ''.join(stack_trace))
2117 'function is not allowed: %s', ''.join(stack_trace))
2109 # rather than throwing errors, here we always return False so if by
2118 # rather than throwing errors, here we always return False so if by
2110 # accident someone checks truth for just an instance it will always end
2119 # accident someone checks truth for just an instance it will always end
2111 # up in returning False
2120 # up in returning False
2112 return False
2121 return False
2113 __nonzero__ = __bool__
2122 __nonzero__ = __bool__
2114
2123
2115 def __call__(self, check_location='', user=None):
2124 def __call__(self, check_location='', user=None):
2116 if not user:
2125 if not user:
2117 log.debug('Using user attribute from global request')
2126 log.debug('Using user attribute from global request')
2118 request = self._get_request()
2127 request = self._get_request()
2119 user = request.user
2128 user = request.user
2120
2129
2121 # init auth user if not already given
2130 # init auth user if not already given
2122 if not isinstance(user, AuthUser):
2131 if not isinstance(user, AuthUser):
2123 log.debug('Wrapping user %s into AuthUser', user)
2132 log.debug('Wrapping user %s into AuthUser', user)
2124 user = AuthUser(user.user_id)
2133 user = AuthUser(user.user_id)
2125
2134
2126 cls_name = self.__class__.__name__
2135 cls_name = self.__class__.__name__
2127 check_scope = self._get_check_scope(cls_name)
2136 check_scope = self._get_check_scope(cls_name)
2128 check_location = check_location or 'unspecified location'
2137 check_location = check_location or 'unspecified location'
2129
2138
2130 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
2139 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
2131 self.required_perms, user, check_scope, check_location)
2140 self.required_perms, user, check_scope, check_location)
2132 if not user:
2141 if not user:
2133 log.warning('Empty user given for permission check')
2142 log.warning('Empty user given for permission check')
2134 return False
2143 return False
2135
2144
2136 if self.check_permissions(user):
2145 if self.check_permissions(user):
2137 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2146 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2138 check_scope, user, check_location)
2147 check_scope, user, check_location)
2139 return True
2148 return True
2140
2149
2141 else:
2150 else:
2142 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2151 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2143 check_scope, user, check_location)
2152 check_scope, user, check_location)
2144 return False
2153 return False
2145
2154
2146 def _get_request(self):
2155 def _get_request(self):
2147 return get_request(self)
2156 return get_request(self)
2148
2157
2149 def _get_check_scope(self, cls_name):
2158 def _get_check_scope(self, cls_name):
2150 return {
2159 return {
2151 'HasPermissionAll': 'GLOBAL',
2160 'HasPermissionAll': 'GLOBAL',
2152 'HasPermissionAny': 'GLOBAL',
2161 'HasPermissionAny': 'GLOBAL',
2153 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
2162 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
2154 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
2163 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
2155 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
2164 'HasRepoGroupPermissionAll': 'repo_group:%s' % self.repo_group_name,
2156 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
2165 'HasRepoGroupPermissionAny': 'repo_group:%s' % self.repo_group_name,
2157 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
2166 'HasUserGroupPermissionAll': 'user_group:%s' % self.user_group_name,
2158 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
2167 'HasUserGroupPermissionAny': 'user_group:%s' % self.user_group_name,
2159 }.get(cls_name, '?:%s' % cls_name)
2168 }.get(cls_name, '?:%s' % cls_name)
2160
2169
2161 def check_permissions(self, user):
2170 def check_permissions(self, user):
2162 """Dummy function for overriding"""
2171 """Dummy function for overriding"""
2163 raise Exception('You have to write this function in child class')
2172 raise Exception('You have to write this function in child class')
2164
2173
2165
2174
2166 class HasPermissionAll(PermsFunction):
2175 class HasPermissionAll(PermsFunction):
2167 def check_permissions(self, user):
2176 def check_permissions(self, user):
2168 perms = user.permissions_with_scope({})
2177 perms = user.permissions_with_scope({})
2169 if self.required_perms.issubset(perms.get('global')):
2178 if self.required_perms.issubset(perms.get('global')):
2170 return True
2179 return True
2171 return False
2180 return False
2172
2181
2173
2182
2174 class HasPermissionAny(PermsFunction):
2183 class HasPermissionAny(PermsFunction):
2175 def check_permissions(self, user):
2184 def check_permissions(self, user):
2176 perms = user.permissions_with_scope({})
2185 perms = user.permissions_with_scope({})
2177 if self.required_perms.intersection(perms.get('global')):
2186 if self.required_perms.intersection(perms.get('global')):
2178 return True
2187 return True
2179 return False
2188 return False
2180
2189
2181
2190
2182 class HasRepoPermissionAll(PermsFunction):
2191 class HasRepoPermissionAll(PermsFunction):
2183 def __call__(self, repo_name=None, check_location='', user=None):
2192 def __call__(self, repo_name=None, check_location='', user=None):
2184 self.repo_name = repo_name
2193 self.repo_name = repo_name
2185 return super(HasRepoPermissionAll, self).__call__(check_location, user)
2194 return super(HasRepoPermissionAll, self).__call__(check_location, user)
2186
2195
2187 def _get_repo_name(self):
2196 def _get_repo_name(self):
2188 if not self.repo_name:
2197 if not self.repo_name:
2189 _request = self._get_request()
2198 _request = self._get_request()
2190 self.repo_name = get_repo_slug(_request)
2199 self.repo_name = get_repo_slug(_request)
2191 return self.repo_name
2200 return self.repo_name
2192
2201
2193 def check_permissions(self, user):
2202 def check_permissions(self, user):
2194 self.repo_name = self._get_repo_name()
2203 self.repo_name = self._get_repo_name()
2195 perms = user.permissions
2204 perms = user.permissions
2196 try:
2205 try:
2197 user_perms = {perms['repositories'][self.repo_name]}
2206 user_perms = {perms['repositories'][self.repo_name]}
2198 except KeyError:
2207 except KeyError:
2199 return False
2208 return False
2200 if self.required_perms.issubset(user_perms):
2209 if self.required_perms.issubset(user_perms):
2201 return True
2210 return True
2202 return False
2211 return False
2203
2212
2204
2213
2205 class HasRepoPermissionAny(PermsFunction):
2214 class HasRepoPermissionAny(PermsFunction):
2206 def __call__(self, repo_name=None, check_location='', user=None):
2215 def __call__(self, repo_name=None, check_location='', user=None):
2207 self.repo_name = repo_name
2216 self.repo_name = repo_name
2208 return super(HasRepoPermissionAny, self).__call__(check_location, user)
2217 return super(HasRepoPermissionAny, self).__call__(check_location, user)
2209
2218
2210 def _get_repo_name(self):
2219 def _get_repo_name(self):
2211 if not self.repo_name:
2220 if not self.repo_name:
2212 _request = self._get_request()
2221 _request = self._get_request()
2213 self.repo_name = get_repo_slug(_request)
2222 self.repo_name = get_repo_slug(_request)
2214 return self.repo_name
2223 return self.repo_name
2215
2224
2216 def check_permissions(self, user):
2225 def check_permissions(self, user):
2217 self.repo_name = self._get_repo_name()
2226 self.repo_name = self._get_repo_name()
2218 perms = user.permissions
2227 perms = user.permissions
2219 try:
2228 try:
2220 user_perms = {perms['repositories'][self.repo_name]}
2229 user_perms = {perms['repositories'][self.repo_name]}
2221 except KeyError:
2230 except KeyError:
2222 return False
2231 return False
2223 if self.required_perms.intersection(user_perms):
2232 if self.required_perms.intersection(user_perms):
2224 return True
2233 return True
2225 return False
2234 return False
2226
2235
2227
2236
2228 class HasRepoGroupPermissionAny(PermsFunction):
2237 class HasRepoGroupPermissionAny(PermsFunction):
2229 def __call__(self, group_name=None, check_location='', user=None):
2238 def __call__(self, group_name=None, check_location='', user=None):
2230 self.repo_group_name = group_name
2239 self.repo_group_name = group_name
2231 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
2240 return super(HasRepoGroupPermissionAny, self).__call__(check_location, user)
2232
2241
2233 def check_permissions(self, user):
2242 def check_permissions(self, user):
2234 perms = user.permissions
2243 perms = user.permissions
2235 try:
2244 try:
2236 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2245 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2237 except KeyError:
2246 except KeyError:
2238 return False
2247 return False
2239 if self.required_perms.intersection(user_perms):
2248 if self.required_perms.intersection(user_perms):
2240 return True
2249 return True
2241 return False
2250 return False
2242
2251
2243
2252
2244 class HasRepoGroupPermissionAll(PermsFunction):
2253 class HasRepoGroupPermissionAll(PermsFunction):
2245 def __call__(self, group_name=None, check_location='', user=None):
2254 def __call__(self, group_name=None, check_location='', user=None):
2246 self.repo_group_name = group_name
2255 self.repo_group_name = group_name
2247 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
2256 return super(HasRepoGroupPermissionAll, self).__call__(check_location, user)
2248
2257
2249 def check_permissions(self, user):
2258 def check_permissions(self, user):
2250 perms = user.permissions
2259 perms = user.permissions
2251 try:
2260 try:
2252 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2261 user_perms = {perms['repositories_groups'][self.repo_group_name]}
2253 except KeyError:
2262 except KeyError:
2254 return False
2263 return False
2255 if self.required_perms.issubset(user_perms):
2264 if self.required_perms.issubset(user_perms):
2256 return True
2265 return True
2257 return False
2266 return False
2258
2267
2259
2268
2260 class HasUserGroupPermissionAny(PermsFunction):
2269 class HasUserGroupPermissionAny(PermsFunction):
2261 def __call__(self, user_group_name=None, check_location='', user=None):
2270 def __call__(self, user_group_name=None, check_location='', user=None):
2262 self.user_group_name = user_group_name
2271 self.user_group_name = user_group_name
2263 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
2272 return super(HasUserGroupPermissionAny, self).__call__(check_location, user)
2264
2273
2265 def check_permissions(self, user):
2274 def check_permissions(self, user):
2266 perms = user.permissions
2275 perms = user.permissions
2267 try:
2276 try:
2268 user_perms = {perms['user_groups'][self.user_group_name]}
2277 user_perms = {perms['user_groups'][self.user_group_name]}
2269 except KeyError:
2278 except KeyError:
2270 return False
2279 return False
2271 if self.required_perms.intersection(user_perms):
2280 if self.required_perms.intersection(user_perms):
2272 return True
2281 return True
2273 return False
2282 return False
2274
2283
2275
2284
2276 class HasUserGroupPermissionAll(PermsFunction):
2285 class HasUserGroupPermissionAll(PermsFunction):
2277 def __call__(self, user_group_name=None, check_location='', user=None):
2286 def __call__(self, user_group_name=None, check_location='', user=None):
2278 self.user_group_name = user_group_name
2287 self.user_group_name = user_group_name
2279 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
2288 return super(HasUserGroupPermissionAll, self).__call__(check_location, user)
2280
2289
2281 def check_permissions(self, user):
2290 def check_permissions(self, user):
2282 perms = user.permissions
2291 perms = user.permissions
2283 try:
2292 try:
2284 user_perms = {perms['user_groups'][self.user_group_name]}
2293 user_perms = {perms['user_groups'][self.user_group_name]}
2285 except KeyError:
2294 except KeyError:
2286 return False
2295 return False
2287 if self.required_perms.issubset(user_perms):
2296 if self.required_perms.issubset(user_perms):
2288 return True
2297 return True
2289 return False
2298 return False
2290
2299
2291
2300
2292 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
2301 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
2293 class HasPermissionAnyMiddleware(object):
2302 class HasPermissionAnyMiddleware(object):
2294 def __init__(self, *perms):
2303 def __init__(self, *perms):
2295 self.required_perms = set(perms)
2304 self.required_perms = set(perms)
2296
2305
2297 def __call__(self, auth_user, repo_name):
2306 def __call__(self, auth_user, repo_name):
2298 # repo_name MUST be unicode, since we handle keys in permission
2307 # repo_name MUST be unicode, since we handle keys in permission
2299 # dict by unicode
2308 # dict by unicode
2300 repo_name = safe_unicode(repo_name)
2309 repo_name = safe_unicode(repo_name)
2301 log.debug(
2310 log.debug(
2302 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2311 'Checking VCS protocol permissions %s for user:%s repo:`%s`',
2303 self.required_perms, auth_user, repo_name)
2312 self.required_perms, auth_user, repo_name)
2304
2313
2305 if self.check_permissions(auth_user, repo_name):
2314 if self.check_permissions(auth_user, repo_name):
2306 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2315 log.debug('Permission to repo:`%s` GRANTED for user:%s @ %s',
2307 repo_name, auth_user, 'PermissionMiddleware')
2316 repo_name, auth_user, 'PermissionMiddleware')
2308 return True
2317 return True
2309
2318
2310 else:
2319 else:
2311 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2320 log.debug('Permission to repo:`%s` DENIED for user:%s @ %s',
2312 repo_name, auth_user, 'PermissionMiddleware')
2321 repo_name, auth_user, 'PermissionMiddleware')
2313 return False
2322 return False
2314
2323
2315 def check_permissions(self, user, repo_name):
2324 def check_permissions(self, user, repo_name):
2316 perms = user.permissions_with_scope({'repo_name': repo_name})
2325 perms = user.permissions_with_scope({'repo_name': repo_name})
2317
2326
2318 try:
2327 try:
2319 user_perms = {perms['repositories'][repo_name]}
2328 user_perms = {perms['repositories'][repo_name]}
2320 except Exception:
2329 except Exception:
2321 log.exception('Error while accessing user permissions')
2330 log.exception('Error while accessing user permissions')
2322 return False
2331 return False
2323
2332
2324 if self.required_perms.intersection(user_perms):
2333 if self.required_perms.intersection(user_perms):
2325 return True
2334 return True
2326 return False
2335 return False
2327
2336
2328
2337
2329 # SPECIAL VERSION TO HANDLE API AUTH
2338 # SPECIAL VERSION TO HANDLE API AUTH
2330 class _BaseApiPerm(object):
2339 class _BaseApiPerm(object):
2331 def __init__(self, *perms):
2340 def __init__(self, *perms):
2332 self.required_perms = set(perms)
2341 self.required_perms = set(perms)
2333
2342
2334 def __call__(self, check_location=None, user=None, repo_name=None,
2343 def __call__(self, check_location=None, user=None, repo_name=None,
2335 group_name=None, user_group_name=None):
2344 group_name=None, user_group_name=None):
2336 cls_name = self.__class__.__name__
2345 cls_name = self.__class__.__name__
2337 check_scope = 'global:%s' % (self.required_perms,)
2346 check_scope = 'global:%s' % (self.required_perms,)
2338 if repo_name:
2347 if repo_name:
2339 check_scope += ', repo_name:%s' % (repo_name,)
2348 check_scope += ', repo_name:%s' % (repo_name,)
2340
2349
2341 if group_name:
2350 if group_name:
2342 check_scope += ', repo_group_name:%s' % (group_name,)
2351 check_scope += ', repo_group_name:%s' % (group_name,)
2343
2352
2344 if user_group_name:
2353 if user_group_name:
2345 check_scope += ', user_group_name:%s' % (user_group_name,)
2354 check_scope += ', user_group_name:%s' % (user_group_name,)
2346
2355
2347 log.debug('checking cls:%s %s %s @ %s',
2356 log.debug('checking cls:%s %s %s @ %s',
2348 cls_name, self.required_perms, check_scope, check_location)
2357 cls_name, self.required_perms, check_scope, check_location)
2349 if not user:
2358 if not user:
2350 log.debug('Empty User passed into arguments')
2359 log.debug('Empty User passed into arguments')
2351 return False
2360 return False
2352
2361
2353 # process user
2362 # process user
2354 if not isinstance(user, AuthUser):
2363 if not isinstance(user, AuthUser):
2355 user = AuthUser(user.user_id)
2364 user = AuthUser(user.user_id)
2356 if not check_location:
2365 if not check_location:
2357 check_location = 'unspecified'
2366 check_location = 'unspecified'
2358 if self.check_permissions(user.permissions, repo_name, group_name,
2367 if self.check_permissions(user.permissions, repo_name, group_name,
2359 user_group_name):
2368 user_group_name):
2360 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2369 log.debug('Permission to repo:`%s` GRANTED for user:`%s` @ %s',
2361 check_scope, user, check_location)
2370 check_scope, user, check_location)
2362 return True
2371 return True
2363
2372
2364 else:
2373 else:
2365 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2374 log.debug('Permission to repo:`%s` DENIED for user:`%s` @ %s',
2366 check_scope, user, check_location)
2375 check_scope, user, check_location)
2367 return False
2376 return False
2368
2377
2369 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2378 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2370 user_group_name=None):
2379 user_group_name=None):
2371 """
2380 """
2372 implement in child class should return True if permissions are ok,
2381 implement in child class should return True if permissions are ok,
2373 False otherwise
2382 False otherwise
2374
2383
2375 :param perm_defs: dict with permission definitions
2384 :param perm_defs: dict with permission definitions
2376 :param repo_name: repo name
2385 :param repo_name: repo name
2377 """
2386 """
2378 raise NotImplementedError()
2387 raise NotImplementedError()
2379
2388
2380
2389
2381 class HasPermissionAllApi(_BaseApiPerm):
2390 class HasPermissionAllApi(_BaseApiPerm):
2382 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2391 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2383 user_group_name=None):
2392 user_group_name=None):
2384 if self.required_perms.issubset(perm_defs.get('global')):
2393 if self.required_perms.issubset(perm_defs.get('global')):
2385 return True
2394 return True
2386 return False
2395 return False
2387
2396
2388
2397
2389 class HasPermissionAnyApi(_BaseApiPerm):
2398 class HasPermissionAnyApi(_BaseApiPerm):
2390 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2399 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2391 user_group_name=None):
2400 user_group_name=None):
2392 if self.required_perms.intersection(perm_defs.get('global')):
2401 if self.required_perms.intersection(perm_defs.get('global')):
2393 return True
2402 return True
2394 return False
2403 return False
2395
2404
2396
2405
2397 class HasRepoPermissionAllApi(_BaseApiPerm):
2406 class HasRepoPermissionAllApi(_BaseApiPerm):
2398 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2407 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2399 user_group_name=None):
2408 user_group_name=None):
2400 try:
2409 try:
2401 _user_perms = {perm_defs['repositories'][repo_name]}
2410 _user_perms = {perm_defs['repositories'][repo_name]}
2402 except KeyError:
2411 except KeyError:
2403 log.warning(traceback.format_exc())
2412 log.warning(traceback.format_exc())
2404 return False
2413 return False
2405 if self.required_perms.issubset(_user_perms):
2414 if self.required_perms.issubset(_user_perms):
2406 return True
2415 return True
2407 return False
2416 return False
2408
2417
2409
2418
2410 class HasRepoPermissionAnyApi(_BaseApiPerm):
2419 class HasRepoPermissionAnyApi(_BaseApiPerm):
2411 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2420 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2412 user_group_name=None):
2421 user_group_name=None):
2413 try:
2422 try:
2414 _user_perms = {perm_defs['repositories'][repo_name]}
2423 _user_perms = {perm_defs['repositories'][repo_name]}
2415 except KeyError:
2424 except KeyError:
2416 log.warning(traceback.format_exc())
2425 log.warning(traceback.format_exc())
2417 return False
2426 return False
2418 if self.required_perms.intersection(_user_perms):
2427 if self.required_perms.intersection(_user_perms):
2419 return True
2428 return True
2420 return False
2429 return False
2421
2430
2422
2431
2423 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2432 class HasRepoGroupPermissionAnyApi(_BaseApiPerm):
2424 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2433 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2425 user_group_name=None):
2434 user_group_name=None):
2426 try:
2435 try:
2427 _user_perms = {perm_defs['repositories_groups'][group_name]}
2436 _user_perms = {perm_defs['repositories_groups'][group_name]}
2428 except KeyError:
2437 except KeyError:
2429 log.warning(traceback.format_exc())
2438 log.warning(traceback.format_exc())
2430 return False
2439 return False
2431 if self.required_perms.intersection(_user_perms):
2440 if self.required_perms.intersection(_user_perms):
2432 return True
2441 return True
2433 return False
2442 return False
2434
2443
2435
2444
2436 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2445 class HasRepoGroupPermissionAllApi(_BaseApiPerm):
2437 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2446 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2438 user_group_name=None):
2447 user_group_name=None):
2439 try:
2448 try:
2440 _user_perms = {perm_defs['repositories_groups'][group_name]}
2449 _user_perms = {perm_defs['repositories_groups'][group_name]}
2441 except KeyError:
2450 except KeyError:
2442 log.warning(traceback.format_exc())
2451 log.warning(traceback.format_exc())
2443 return False
2452 return False
2444 if self.required_perms.issubset(_user_perms):
2453 if self.required_perms.issubset(_user_perms):
2445 return True
2454 return True
2446 return False
2455 return False
2447
2456
2448
2457
2449 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2458 class HasUserGroupPermissionAnyApi(_BaseApiPerm):
2450 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2459 def check_permissions(self, perm_defs, repo_name=None, group_name=None,
2451 user_group_name=None):
2460 user_group_name=None):
2452 try:
2461 try:
2453 _user_perms = {perm_defs['user_groups'][user_group_name]}
2462 _user_perms = {perm_defs['user_groups'][user_group_name]}
2454 except KeyError:
2463 except KeyError:
2455 log.warning(traceback.format_exc())
2464 log.warning(traceback.format_exc())
2456 return False
2465 return False
2457 if self.required_perms.intersection(_user_perms):
2466 if self.required_perms.intersection(_user_perms):
2458 return True
2467 return True
2459 return False
2468 return False
2460
2469
2461
2470
2462 def check_ip_access(source_ip, allowed_ips=None):
2471 def check_ip_access(source_ip, allowed_ips=None):
2463 """
2472 """
2464 Checks if source_ip is a subnet of any of allowed_ips.
2473 Checks if source_ip is a subnet of any of allowed_ips.
2465
2474
2466 :param source_ip:
2475 :param source_ip:
2467 :param allowed_ips: list of allowed ips together with mask
2476 :param allowed_ips: list of allowed ips together with mask
2468 """
2477 """
2469 log.debug('checking if ip:%s is subnet of %s', source_ip, allowed_ips)
2478 log.debug('checking if ip:%s is subnet of %s', source_ip, allowed_ips)
2470 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2479 source_ip_address = ipaddress.ip_address(safe_unicode(source_ip))
2471 if isinstance(allowed_ips, (tuple, list, set)):
2480 if isinstance(allowed_ips, (tuple, list, set)):
2472 for ip in allowed_ips:
2481 for ip in allowed_ips:
2473 ip = safe_unicode(ip)
2482 ip = safe_unicode(ip)
2474 try:
2483 try:
2475 network_address = ipaddress.ip_network(ip, strict=False)
2484 network_address = ipaddress.ip_network(ip, strict=False)
2476 if source_ip_address in network_address:
2485 if source_ip_address in network_address:
2477 log.debug('IP %s is network %s', source_ip_address, network_address)
2486 log.debug('IP %s is network %s', source_ip_address, network_address)
2478 return True
2487 return True
2479 # for any case we cannot determine the IP, don't crash just
2488 # for any case we cannot determine the IP, don't crash just
2480 # skip it and log as error, we want to say forbidden still when
2489 # skip it and log as error, we want to say forbidden still when
2481 # sending bad IP
2490 # sending bad IP
2482 except Exception:
2491 except Exception:
2483 log.error(traceback.format_exc())
2492 log.error(traceback.format_exc())
2484 continue
2493 continue
2485 return False
2494 return False
2486
2495
2487
2496
2488 def get_cython_compat_decorator(wrapper, func):
2497 def get_cython_compat_decorator(wrapper, func):
2489 """
2498 """
2490 Creates a cython compatible decorator. The previously used
2499 Creates a cython compatible decorator. The previously used
2491 decorator.decorator() function seems to be incompatible with cython.
2500 decorator.decorator() function seems to be incompatible with cython.
2492
2501
2493 :param wrapper: __wrapper method of the decorator class
2502 :param wrapper: __wrapper method of the decorator class
2494 :param func: decorated function
2503 :param func: decorated function
2495 """
2504 """
2496 @wraps(func)
2505 @wraps(func)
2497 def local_wrapper(*args, **kwds):
2506 def local_wrapper(*args, **kwds):
2498 return wrapper(func, *args, **kwds)
2507 return wrapper(func, *args, **kwds)
2499 local_wrapper.__wrapped__ = func
2508 local_wrapper.__wrapped__ = func
2500 return local_wrapper
2509 return local_wrapper
2501
2510
2502
2511
@@ -1,299 +1,304 b''
1 <%namespace name="base" file="/base/base.mako"/>
1 <%namespace name="base" file="/base/base.mako"/>
2
2
3 <%
3 <%
4 elems = [
4 elems = [
5 (_('Repository ID'), c.rhodecode_db_repo.repo_id, '', ''),
5 (_('Repository ID'), c.rhodecode_db_repo.repo_id, '', ''),
6 (_('Owner'), lambda:base.gravatar_with_user(c.rhodecode_db_repo.user.email, tooltip=True), '', ''),
6 (_('Owner'), lambda:base.gravatar_with_user(c.rhodecode_db_repo.user.email, tooltip=True), '', ''),
7 (_('Created on'), h.format_date(c.rhodecode_db_repo.created_on), '', ''),
7 (_('Created on'), h.format_date(c.rhodecode_db_repo.created_on), '', ''),
8 (_('Updated on'), h.format_date(c.rhodecode_db_repo.updated_on), '', ''),
8 (_('Updated on'), h.format_date(c.rhodecode_db_repo.updated_on), '', ''),
9 (_('Cached Commit id'), lambda: h.link_to(c.rhodecode_db_repo.changeset_cache.get('short_id'), h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.changeset_cache.get('raw_id'))), '', ''),
9 (_('Cached Commit id'), lambda: h.link_to(c.rhodecode_db_repo.changeset_cache.get('short_id'), h.route_path('repo_commit',repo_name=c.repo_name,commit_id=c.rhodecode_db_repo.changeset_cache.get('raw_id'))), '', ''),
10 (_('Cached Commit date'), c.rhodecode_db_repo.changeset_cache.get('date'), '', ''),
10 (_('Cached Commit date'), c.rhodecode_db_repo.changeset_cache.get('date'), '', ''),
11 (_('Cached Commit data'), lambda: h.link_to('refresh now', h.current_route_path(request, update_commit_cache=1)), '', ''),
11 (_('Cached Commit data'), lambda: h.link_to('refresh now', h.current_route_path(request, update_commit_cache=1)), '', ''),
12 (_('Attached scoped tokens'), len(c.rhodecode_db_repo.scoped_tokens), '', [x.user for x in c.rhodecode_db_repo.scoped_tokens]),
12 (_('Attached scoped tokens'), len(c.rhodecode_db_repo.scoped_tokens), '', [x.user for x in c.rhodecode_db_repo.scoped_tokens]),
13 (_('Pull requests source'), len(c.rhodecode_db_repo.pull_requests_source), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.source_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_source]),
13 (_('Pull requests source'), len(c.rhodecode_db_repo.pull_requests_source), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.source_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_source]),
14 (_('Pull requests target'), len(c.rhodecode_db_repo.pull_requests_target), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.target_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_target]),
14 (_('Pull requests target'), len(c.rhodecode_db_repo.pull_requests_target), '', ['pr_id:{}, repo:{}'.format(x.pull_request_id,x.target_repo.repo_name) for x in c.rhodecode_db_repo.pull_requests_target]),
15 (_('Attached Artifacts'), len(c.rhodecode_db_repo.artifacts), '', ''),
15 (_('Attached Artifacts'), len(c.rhodecode_db_repo.artifacts), '', ''),
16 ]
16 ]
17 %>
17 %>
18
18
19 <div class="panel panel-default">
19 <div class="panel panel-default">
20 <div class="panel-heading" id="advanced-info" >
20 <div class="panel-heading" id="advanced-info" >
21 <h3 class="panel-title">${_('Repository: %s') % c.rhodecode_db_repo.repo_name} <a class="permalink" href="#advanced-info"> ΒΆ</a></h3>
21 <h3 class="panel-title">${_('Repository: %s') % c.rhodecode_db_repo.repo_name} <a class="permalink" href="#advanced-info"> ΒΆ</a></h3>
22 </div>
22 </div>
23 <div class="panel-body">
23 <div class="panel-body">
24 ${base.dt_info_panel(elems)}
24 ${base.dt_info_panel(elems)}
25 </div>
25 </div>
26 </div>
26 </div>
27
27
28
28
29 <div class="panel panel-default">
29 <div class="panel panel-default">
30 <div class="panel-heading" id="advanced-fork">
30 <div class="panel-heading" id="advanced-fork">
31 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"> ΒΆ</a></h3>
31 <h3 class="panel-title">${_('Fork Reference')} <a class="permalink" href="#advanced-fork"> ΒΆ</a></h3>
32 </div>
32 </div>
33 <div class="panel-body">
33 <div class="panel-body">
34 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
34 ${h.secure_form(h.route_path('edit_repo_advanced_fork', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
35
35
36 % if c.rhodecode_db_repo.fork:
36 % if c.rhodecode_db_repo.fork:
37 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.rhodecode_db_repo.fork.repo_name, h.route_path('repo_summary', repo_name=c.rhodecode_db_repo.fork.repo_name))})}
37 <div class="panel-body-title-text">${h.literal(_('This repository is a fork of %(repo_link)s') % {'repo_link': h.link_to_if(c.has_origin_repo_read_perm,c.rhodecode_db_repo.fork.repo_name, h.route_path('repo_summary', repo_name=c.rhodecode_db_repo.fork.repo_name))})}
38 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
38 | <button class="btn btn-link btn-danger" type="submit">Remove fork reference</button></div>
39 % endif
39 % endif
40
40
41 <div class="field">
41 <div class="field">
42 ${h.hidden('id_fork_of')}
42 ${h.hidden('id_fork_of')}
43 ${h.submit('set_as_fork_%s' % c.rhodecode_db_repo.repo_name,_('Set'),class_="btn btn-small",)}
43 ${h.submit('set_as_fork_%s' % c.rhodecode_db_repo.repo_name,_('Set'),class_="btn btn-small",)}
44 </div>
44 </div>
45 <div class="field">
45 <div class="field">
46 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
46 <span class="help-block">${_('Manually set this repository as a fork of another from the list')}</span>
47 </div>
47 </div>
48 ${h.end_form()}
48 ${h.end_form()}
49 </div>
49 </div>
50 </div>
50 </div>
51
51
52
52
53 <div class="panel panel-default">
53 <div class="panel panel-default">
54 <div class="panel-heading" id="advanced-journal">
54 <div class="panel-heading" id="advanced-journal">
55 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"> ΒΆ</a></h3>
55 <h3 class="panel-title">${_('Public Journal Visibility')} <a class="permalink" href="#advanced-journal"> ΒΆ</a></h3>
56 </div>
56 </div>
57 <div class="panel-body">
57 <div class="panel-body">
58 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
58 ${h.secure_form(h.route_path('edit_repo_advanced_journal', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
59 <div class="field">
59 <div class="field">
60 %if c.in_public_journal:
60 %if c.in_public_journal:
61 <button class="btn btn-small" type="submit">
61 <button class="btn btn-small" type="submit">
62 ${_('Remove from Public Journal')}
62 ${_('Remove from Public Journal')}
63 </button>
63 </button>
64 %else:
64 %else:
65 <button class="btn btn-small" type="submit">
65 <button class="btn btn-small" type="submit">
66 ${_('Add to Public Journal')}
66 ${_('Add to Public Journal')}
67 </button>
67 </button>
68 %endif
68 %endif
69 </div>
69 </div>
70 <div class="field" >
70 <div class="field" >
71 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
71 <span class="help-block">${_('All actions made on this repository will be visible to everyone following the public journal.')}</span>
72 </div>
72 </div>
73 ${h.end_form()}
73 ${h.end_form()}
74 </div>
74 </div>
75 </div>
75 </div>
76
76
77
77
78 <div class="panel panel-default">
78 <div class="panel panel-default">
79 <div class="panel-heading" id="advanced-locking">
79 <div class="panel-heading" id="advanced-locking">
80 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"> ΒΆ</a></h3>
80 <h3 class="panel-title">${_('Locking state')} <a class="permalink" href="#advanced-locking"> ΒΆ</a></h3>
81 </div>
81 </div>
82 <div class="panel-body">
82 <div class="panel-body">
83 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
83 ${h.secure_form(h.route_path('edit_repo_advanced_locking', repo_name=c.rhodecode_db_repo.repo_name), request=request)}
84
84
85 %if c.rhodecode_db_repo.locked[0]:
85 %if c.rhodecode_db_repo.locked[0]:
86 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.rhodecode_db_repo.locked[0]),
86 <div class="panel-body-title-text">${'Locked by %s on %s. Lock reason: %s' % (h.person_by_id(c.rhodecode_db_repo.locked[0]),
87 h.format_date(h. time_to_datetime(c.rhodecode_db_repo.locked[1])), c.rhodecode_db_repo.locked[2])}</div>
87 h.format_date(h. time_to_datetime(c.rhodecode_db_repo.locked[1])), c.rhodecode_db_repo.locked[2])}</div>
88 %else:
88 %else:
89 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
89 <div class="panel-body-title-text">${_('This Repository is not currently locked.')}</div>
90 %endif
90 %endif
91
91
92 <div class="field" >
92 <div class="field" >
93 %if c.rhodecode_db_repo.locked[0]:
93 %if c.rhodecode_db_repo.locked[0]:
94 ${h.hidden('set_unlock', '1')}
94 ${h.hidden('set_unlock', '1')}
95 <button class="btn btn-small" type="submit"
95 <button class="btn btn-small" type="submit"
96 onclick="submitConfirm(event, this, _gettext('Confirm to unlock this repository'), _gettext('Unlock'), '${c.rhodecode_db_repo.repo_name}')"
96 onclick="submitConfirm(event, this, _gettext('Confirm to unlock this repository'), _gettext('Unlock'), '${c.rhodecode_db_repo.repo_name}')"
97 >
97 >
98 <i class="icon-unlock"></i>
98 <i class="icon-unlock"></i>
99 ${_('Unlock repository')}
99 ${_('Unlock repository')}
100 </button>
100 </button>
101 %else:
101 %else:
102 ${h.hidden('set_lock', '1')}
102 ${h.hidden('set_lock', '1')}
103 <button class="btn btn-small" type="submit"
103 <button class="btn btn-small" type="submit"
104 onclick="submitConfirm(event, this, _gettext('Confirm to lock this repository'), _gettext('lock'), '${c.rhodecode_db_repo.repo_name}')"
104 onclick="submitConfirm(event, this, _gettext('Confirm to lock this repository'), _gettext('lock'), '${c.rhodecode_db_repo.repo_name}')"
105 >
105 >
106 <i class="icon-lock"></i>
106 <i class="icon-lock"></i>
107 ${_('Lock repository')}
107 ${_('Lock repository')}
108 </button>
108 </button>
109 %endif
109 %endif
110 </div>
110 </div>
111 <div class="field" >
111 <div class="field" >
112 <span class="help-block">
112 <span class="help-block">
113 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
113 ${_('Force repository locking. This only works when anonymous access is disabled. Pulling from the repository locks the repository to that user until the same user pushes to that repository again.')}
114 </span>
114 </span>
115 </div>
115 </div>
116 ${h.end_form()}
116 ${h.end_form()}
117 </div>
117 </div>
118 </div>
118 </div>
119
119
120
120
121 <div class="panel panel-default">
121 <div class="panel panel-default">
122 <div class="panel-heading" id="advanced-hooks">
122 <div class="panel-heading" id="advanced-hooks">
123 <h3 class="panel-title">${_('Hooks')} <a class="permalink" href="#advanced-hooks"> ΒΆ</a></h3>
123 <h3 class="panel-title">${_('Hooks')} <a class="permalink" href="#advanced-hooks"> ΒΆ</a></h3>
124 </div>
124 </div>
125 <div class="panel-body">
125 <div class="panel-body">
126 <table class="rctable">
126 <table class="rctable">
127 <th>${_('Hook type')}</th>
127 <th>${_('Hook type')}</th>
128 <th>${_('Hook version')}</th>
128 <th>${_('Hook version')}</th>
129 <th>${_('Current version')}</th>
129 <th>${_('Current version')}</th>
130 % if c.ver_info_dict:
130 % if c.ver_info_dict:
131 <tr>
131 <tr>
132 <td>${_('PRE HOOK')}</td>
132 <td>${_('PRE HOOK')}</td>
133 <td>${c.ver_info_dict['pre_version']}</td>
133 <td>${c.ver_info_dict['pre_version']}</td>
134 <td>${c.rhodecode_version}</td>
134 <td>${c.rhodecode_version}</td>
135 </tr>
135 </tr>
136 <tr>
136 <tr>
137 <td>${_('POST HOOK')}</td>
137 <td>${_('POST HOOK')}</td>
138 <td>${c.ver_info_dict['post_version']}</td>
138 <td>${c.ver_info_dict['post_version']}</td>
139 <td>${c.rhodecode_version}</td>
139 <td>${c.rhodecode_version}</td>
140 </tr>
140 </tr>
141 % else:
141 % else:
142 <tr>
142 <tr>
143 <td>${_('Unable to read hook information from VCS Server')}</td>
143 <td>${_('Unable to read hook information from VCS Server')}</td>
144 </tr>
144 </tr>
145 % endif
145 % endif
146 </table>
146 </table>
147
147
148 <a class="btn btn-primary" href="${h.route_path('edit_repo_advanced_hooks', repo_name=c.repo_name)}"
148 <a class="btn btn-primary" href="${h.route_path('edit_repo_advanced_hooks', repo_name=c.repo_name)}"
149 onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');">
149 onclick="return confirm('${_('Confirm to reinstall hooks for this repository.')}');">
150 ${_('Update Hooks')}
150 ${_('Update Hooks')}
151 </a>
151 </a>
152 % if c.hooks_outdated:
152 % if c.hooks_outdated:
153 <span class="alert-error" style="padding: 10px">
153 <span class="alert-error" style="padding: 10px">
154 ${_('Outdated hooks detected, please update hooks using `Update Hooks` action.')}
154 ${_('Outdated hooks detected, please update hooks using `Update Hooks` action.')}
155 </span>
155 </span>
156 % endif
156 % endif
157 </div>
157 </div>
158 </div>
158 </div>
159
159
160 <div class="panel panel-warning">
160 <div class="panel panel-warning">
161 <div class="panel-heading" id="advanced-archive">
161 <div class="panel-heading" id="advanced-archive">
162 <h3 class="panel-title">${_('Archive repository')} <a class="permalink" href="#advanced-archive"> ΒΆ</a></h3>
162 <h3 class="panel-title">${_('Archive repository')} <a class="permalink" href="#advanced-archive"> ΒΆ</a></h3>
163 </div>
163 </div>
164 <div class="panel-body">
164 <div class="panel-body">
165 ${h.secure_form(h.route_path('edit_repo_advanced_archive', repo_name=c.repo_name), request=request)}
165 ${h.secure_form(h.route_path('edit_repo_advanced_archive', repo_name=c.repo_name), request=request)}
166
166
167 <div style="margin: 0 0 20px 0" class="fake-space"></div>
167 <div style="margin: 0 0 20px 0" class="fake-space"></div>
168
168
169 <div class="field">
169 <div class="field">
170 % if c.rhodecode_db_repo.archived:
171 This repository is already archived. Only super-admin users can un-archive this repository.
172 % else:
170 <button class="btn btn-small btn-warning" type="submit"
173 <button class="btn btn-small btn-warning" type="submit"
171 onclick="submitConfirm(event, this, _gettext('Confirm to archive this repository'), _gettext('Archive'), '${c.rhodecode_db_repo.repo_name}')"
174 onclick="submitConfirm(event, this, _gettext('Confirm to archive this repository'), _gettext('Archive'), '${c.rhodecode_db_repo.repo_name}')"
172 >
175 >
173 ${_('Archive this repository')}
176 ${_('Archive this repository')}
174 </button>
177 </button>
178 % endif
179
175 </div>
180 </div>
176 <div class="field">
181 <div class="field">
177 <span class="help-block">
182 <span class="help-block">
178 ${_('Archiving the repository will make it entirely read-only. The repository cannot be committed to.'
183 ${_('Archiving the repository will make it entirely read-only. The repository cannot be committed to.'
179 'It is hidden from the search results and dashboard. ')}
184 'It is hidden from the search results and dashboard. ')}
180 </span>
185 </span>
181 </div>
186 </div>
182
187
183 ${h.end_form()}
188 ${h.end_form()}
184 </div>
189 </div>
185 </div>
190 </div>
186
191
187
192
188 <div class="panel panel-danger">
193 <div class="panel panel-danger">
189 <div class="panel-heading" id="advanced-delete">
194 <div class="panel-heading" id="advanced-delete">
190 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3>
195 <h3 class="panel-title">${_('Delete repository')} <a class="permalink" href="#advanced-delete"> ΒΆ</a></h3>
191 </div>
196 </div>
192 <div class="panel-body">
197 <div class="panel-body">
193 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), request=request)}
198 ${h.secure_form(h.route_path('edit_repo_advanced_delete', repo_name=c.repo_name), request=request)}
194 <table class="display">
199 <table class="display">
195 <tr>
200 <tr>
196 <td>
201 <td>
197 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.rhodecode_db_repo.forks.count()) % c.rhodecode_db_repo.forks.count()}
202 ${_ungettext('This repository has %s fork.', 'This repository has %s forks.', c.rhodecode_db_repo.forks.count()) % c.rhodecode_db_repo.forks.count()}
198 </td>
203 </td>
199 <td>
204 <td>
200 %if c.rhodecode_db_repo.forks.count():
205 %if c.rhodecode_db_repo.forks.count():
201 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
206 <input type="radio" name="forks" value="detach_forks" checked="checked"/> <label for="forks">${_('Detach forks')}</label>
202 %endif
207 %endif
203 </td>
208 </td>
204 <td>
209 <td>
205 %if c.rhodecode_db_repo.forks.count():
210 %if c.rhodecode_db_repo.forks.count():
206 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
211 <input type="radio" name="forks" value="delete_forks"/> <label for="forks">${_('Delete forks')}</label>
207 %endif
212 %endif
208 </td>
213 </td>
209 </tr>
214 </tr>
210 <% attached_prs = len(c.rhodecode_db_repo.pull_requests_source + c.rhodecode_db_repo.pull_requests_target) %>
215 <% attached_prs = len(c.rhodecode_db_repo.pull_requests_source + c.rhodecode_db_repo.pull_requests_target) %>
211 % if c.rhodecode_db_repo.pull_requests_source or c.rhodecode_db_repo.pull_requests_target:
216 % if c.rhodecode_db_repo.pull_requests_source or c.rhodecode_db_repo.pull_requests_target:
212 <tr>
217 <tr>
213 <td>
218 <td>
214 ${_ungettext('This repository has %s attached pull request.', 'This repository has %s attached pull requests.', attached_prs) % attached_prs}
219 ${_ungettext('This repository has %s attached pull request.', 'This repository has %s attached pull requests.', attached_prs) % attached_prs}
215 <br/>
220 <br/>
216 ${_('Consider to archive this repository instead.')}
221 ${_('Consider to archive this repository instead.')}
217 </td>
222 </td>
218 <td></td>
223 <td></td>
219 <td></td>
224 <td></td>
220 </tr>
225 </tr>
221 % endif
226 % endif
222 </table>
227 </table>
223 <div style="margin: 0 0 20px 0" class="fake-space"></div>
228 <div style="margin: 0 0 20px 0" class="fake-space"></div>
224
229
225 <div class="field">
230 <div class="field">
226 <button class="btn btn-small btn-danger" type="submit"
231 <button class="btn btn-small btn-danger" type="submit"
227 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${c.rhodecode_db_repo.repo_name}')"
232 onclick="submitConfirm(event, this, _gettext('Confirm to delete this repository'), _gettext('Delete'), '${c.rhodecode_db_repo.repo_name}')"
228 >
233 >
229 ${_('Delete this repository')}
234 ${_('Delete this repository')}
230 </button>
235 </button>
231 </div>
236 </div>
232 <div class="field">
237 <div class="field">
233 <span class="help-block">
238 <span class="help-block">
234 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
239 ${_('This repository will be renamed in a special way in order to make it inaccessible to RhodeCode Enterprise and its VCS systems. If you need to fully delete it from the file system, please do it manually, or with rhodecode-cleanup-repos command available in rhodecode-tools.')}
235 </span>
240 </span>
236 </div>
241 </div>
237
242
238 ${h.end_form()}
243 ${h.end_form()}
239 </div>
244 </div>
240 </div>
245 </div>
241
246
242
247
243 <script>
248 <script>
244
249
245 var currentRepoId = ${c.rhodecode_db_repo.repo_id};
250 var currentRepoId = ${c.rhodecode_db_repo.repo_id};
246
251
247 var repoTypeFilter = function(data) {
252 var repoTypeFilter = function(data) {
248 var results = [];
253 var results = [];
249
254
250 if (!data.results[0]) {
255 if (!data.results[0]) {
251 return data
256 return data
252 }
257 }
253
258
254 $.each(data.results[0].children, function() {
259 $.each(data.results[0].children, function() {
255 // filter out the SAME repo, it cannot be used as fork of itself
260 // filter out the SAME repo, it cannot be used as fork of itself
256 if (this.repo_id != currentRepoId) {
261 if (this.repo_id != currentRepoId) {
257 this.id = this.repo_id;
262 this.id = this.repo_id;
258 results.push(this)
263 results.push(this)
259 }
264 }
260 });
265 });
261 data.results[0].children = results;
266 data.results[0].children = results;
262 return data;
267 return data;
263 };
268 };
264
269
265 $("#id_fork_of").select2({
270 $("#id_fork_of").select2({
266 cachedDataSource: {},
271 cachedDataSource: {},
267 minimumInputLength: 2,
272 minimumInputLength: 2,
268 placeholder: "${_('Change repository') if c.rhodecode_db_repo.fork else _('Pick repository')}",
273 placeholder: "${_('Change repository') if c.rhodecode_db_repo.fork else _('Pick repository')}",
269 dropdownAutoWidth: true,
274 dropdownAutoWidth: true,
270 containerCssClass: "drop-menu",
275 containerCssClass: "drop-menu",
271 dropdownCssClass: "drop-menu-dropdown",
276 dropdownCssClass: "drop-menu-dropdown",
272 formatResult: formatRepoResult,
277 formatResult: formatRepoResult,
273 query: $.debounce(250, function(query){
278 query: $.debounce(250, function(query){
274 self = this;
279 self = this;
275 var cacheKey = query.term;
280 var cacheKey = query.term;
276 var cachedData = self.cachedDataSource[cacheKey];
281 var cachedData = self.cachedDataSource[cacheKey];
277
282
278 if (cachedData) {
283 if (cachedData) {
279 query.callback({results: cachedData.results});
284 query.callback({results: cachedData.results});
280 } else {
285 } else {
281 $.ajax({
286 $.ajax({
282 url: pyroutes.url('repo_list_data'),
287 url: pyroutes.url('repo_list_data'),
283 data: {'query': query.term, repo_type: '${c.rhodecode_db_repo.repo_type}'},
288 data: {'query': query.term, repo_type: '${c.rhodecode_db_repo.repo_type}'},
284 dataType: 'json',
289 dataType: 'json',
285 type: 'GET',
290 type: 'GET',
286 success: function(data) {
291 success: function(data) {
287 data = repoTypeFilter(data);
292 data = repoTypeFilter(data);
288 self.cachedDataSource[cacheKey] = data;
293 self.cachedDataSource[cacheKey] = data;
289 query.callback({results: data.results});
294 query.callback({results: data.results});
290 },
295 },
291 error: function(data, textStatus, errorThrown) {
296 error: function(data, textStatus, errorThrown) {
292 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
297 alert("Error while fetching entries.\nError code {0} ({1}).".format(data.status, data.statusText));
293 }
298 }
294 })
299 })
295 }
300 }
296 })
301 })
297 });
302 });
298 </script>
303 </script>
299
304
@@ -1,1206 +1,1206 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2
2
3 <%!
3 <%!
4 from rhodecode.lib import html_filters
4 from rhodecode.lib import html_filters
5 %>
5 %>
6
6
7 <%inherit file="root.mako"/>
7 <%inherit file="root.mako"/>
8
8
9 <%include file="/ejs_templates/templates.html"/>
9 <%include file="/ejs_templates/templates.html"/>
10
10
11 <div class="outerwrapper">
11 <div class="outerwrapper">
12 <!-- HEADER -->
12 <!-- HEADER -->
13 <div class="header">
13 <div class="header">
14 <div id="header-inner" class="wrapper">
14 <div id="header-inner" class="wrapper">
15 <div id="logo">
15 <div id="logo">
16 <div class="logo-wrapper">
16 <div class="logo-wrapper">
17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
17 <a href="${h.route_path('home')}"><img src="${h.asset('images/rhodecode-logo-white-60x60.png')}" alt="RhodeCode"/></a>
18 </div>
18 </div>
19 % if c.rhodecode_name:
19 % if c.rhodecode_name:
20 <div class="branding">
20 <div class="branding">
21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
21 <a href="${h.route_path('home')}">${h.branding(c.rhodecode_name)}</a>
22 </div>
22 </div>
23 % endif
23 % endif
24 </div>
24 </div>
25 <!-- MENU BAR NAV -->
25 <!-- MENU BAR NAV -->
26 ${self.menu_bar_nav()}
26 ${self.menu_bar_nav()}
27 <!-- END MENU BAR NAV -->
27 <!-- END MENU BAR NAV -->
28 </div>
28 </div>
29 </div>
29 </div>
30 ${self.menu_bar_subnav()}
30 ${self.menu_bar_subnav()}
31 <!-- END HEADER -->
31 <!-- END HEADER -->
32
32
33 <!-- CONTENT -->
33 <!-- CONTENT -->
34 <div id="content" class="wrapper">
34 <div id="content" class="wrapper">
35
35
36 <rhodecode-toast id="notifications"></rhodecode-toast>
36 <rhodecode-toast id="notifications"></rhodecode-toast>
37
37
38 <div class="main">
38 <div class="main">
39 ${next.main()}
39 ${next.main()}
40 </div>
40 </div>
41 </div>
41 </div>
42 <!-- END CONTENT -->
42 <!-- END CONTENT -->
43
43
44 </div>
44 </div>
45 <!-- FOOTER -->
45 <!-- FOOTER -->
46 <div id="footer">
46 <div id="footer">
47 <div id="footer-inner" class="title wrapper">
47 <div id="footer-inner" class="title wrapper">
48 <div>
48 <div>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
49 <% sid = 'block' if request.GET.get('showrcid') else 'none' %>
50
50
51 <p class="footer-link-right">
51 <p class="footer-link-right">
52 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
52 <a class="grey-link-action" href="${h.route_path('home', _query={'showrcid': 1})}">
53 RhodeCode
53 RhodeCode
54 % if c.visual.show_version:
54 % if c.visual.show_version:
55 ${c.rhodecode_version}
55 ${c.rhodecode_version}
56 % endif
56 % endif
57 ${c.rhodecode_edition}
57 ${c.rhodecode_edition}
58 </a> |
58 </a> |
59
59
60 % if c.visual.rhodecode_support_url:
60 % if c.visual.rhodecode_support_url:
61 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
61 <a class="grey-link-action" href="${c.visual.rhodecode_support_url}" target="_blank">${_('Support')}</a> |
62 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
62 <a class="grey-link-action" href="https://docs.rhodecode.com" target="_blank">${_('Documentation')}</a>
63 % endif
63 % endif
64
64
65 </p>
65 </p>
66
66
67 <p class="server-instance" style="display:${sid}">
67 <p class="server-instance" style="display:${sid}">
68 ## display hidden instance ID if specially defined
68 ## display hidden instance ID if specially defined
69 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
69 &copy; 2010-${h.datetime.today().year}, <a href="${h.route_url('rhodecode_official')}" target="_blank">RhodeCode GmbH</a>. All rights reserved.
70 % if c.rhodecode_instanceid:
70 % if c.rhodecode_instanceid:
71 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
71 ${_('RhodeCode instance id: {}').format(c.rhodecode_instanceid)}
72 % endif
72 % endif
73 </p>
73 </p>
74 </div>
74 </div>
75 </div>
75 </div>
76 </div>
76 </div>
77
77
78 <!-- END FOOTER -->
78 <!-- END FOOTER -->
79
79
80 ### MAKO DEFS ###
80 ### MAKO DEFS ###
81
81
82 <%def name="menu_bar_subnav()">
82 <%def name="menu_bar_subnav()">
83 </%def>
83 </%def>
84
84
85 <%def name="breadcrumbs(class_='breadcrumbs')">
85 <%def name="breadcrumbs(class_='breadcrumbs')">
86 <div class="${class_}">
86 <div class="${class_}">
87 ${self.breadcrumbs_links()}
87 ${self.breadcrumbs_links()}
88 </div>
88 </div>
89 </%def>
89 </%def>
90
90
91 <%def name="admin_menu(active=None)">
91 <%def name="admin_menu(active=None)">
92
92
93 <div id="context-bar">
93 <div id="context-bar">
94 <div class="wrapper">
94 <div class="wrapper">
95 <div class="title">
95 <div class="title">
96 <div class="title-content">
96 <div class="title-content">
97 <div class="title-main">
97 <div class="title-main">
98 % if c.is_super_admin:
98 % if c.is_super_admin:
99 ${_('Super-admin Panel')}
99 ${_('Super-admin Panel')}
100 % else:
100 % else:
101 ${_('Delegated Admin Panel')}
101 ${_('Delegated Admin Panel')}
102 % endif
102 % endif
103 </div>
103 </div>
104 </div>
104 </div>
105 </div>
105 </div>
106
106
107 <ul id="context-pages" class="navigation horizontal-list">
107 <ul id="context-pages" class="navigation horizontal-list">
108
108
109 ## super-admin case
109 ## super-admin case
110 % if c.is_super_admin:
110 % if c.is_super_admin:
111 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
111 <li class="${h.is_active('audit_logs', active)}"><a href="${h.route_path('admin_audit_logs')}">${_('Admin audit logs')}</a></li>
112 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
112 <li class="${h.is_active('repositories', active)}"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
113 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
113 <li class="${h.is_active('repository_groups', active)}"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
114 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
114 <li class="${h.is_active('users', active)}"><a href="${h.route_path('users')}">${_('Users')}</a></li>
115 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
115 <li class="${h.is_active('user_groups', active)}"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
116 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
116 <li class="${h.is_active('permissions', active)}"><a href="${h.route_path('admin_permissions_application')}">${_('Permissions')}</a></li>
117 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
117 <li class="${h.is_active('authentication', active)}"><a href="${h.route_path('auth_home', traverse='')}">${_('Authentication')}</a></li>
118 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
118 <li class="${h.is_active('integrations', active)}"><a href="${h.route_path('global_integrations_home')}">${_('Integrations')}</a></li>
119 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
119 <li class="${h.is_active('defaults', active)}"><a href="${h.route_path('admin_defaults_repositories')}">${_('Defaults')}</a></li>
120 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
120 <li class="${h.is_active('settings', active)}"><a href="${h.route_path('admin_settings')}">${_('Settings')}</a></li>
121
121
122 ## delegated admin
122 ## delegated admin
123 % elif c.is_delegated_admin:
123 % elif c.is_delegated_admin:
124 <%
124 <%
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
125 repositories=c.auth_user.repositories_admin or c.can_create_repo
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
126 repository_groups=c.auth_user.repository_groups_admin or c.can_create_repo_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
127 user_groups=c.auth_user.user_groups_admin or c.can_create_user_group
128 %>
128 %>
129
129
130 %if repositories:
130 %if repositories:
131 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
131 <li class="${h.is_active('repositories', active)} local-admin-repos"><a href="${h.route_path('repos')}">${_('Repositories')}</a></li>
132 %endif
132 %endif
133 %if repository_groups:
133 %if repository_groups:
134 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
134 <li class="${h.is_active('repository_groups', active)} local-admin-repo-groups"><a href="${h.route_path('repo_groups')}">${_('Repository groups')}</a></li>
135 %endif
135 %endif
136 %if user_groups:
136 %if user_groups:
137 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
137 <li class="${h.is_active('user_groups', active)} local-admin-user-groups"><a href="${h.route_path('user_groups')}">${_('User groups')}</a></li>
138 %endif
138 %endif
139 % endif
139 % endif
140 </ul>
140 </ul>
141
141
142 </div>
142 </div>
143 <div class="clear"></div>
143 <div class="clear"></div>
144 </div>
144 </div>
145 </%def>
145 </%def>
146
146
147 <%def name="dt_info_panel(elements)">
147 <%def name="dt_info_panel(elements)">
148 <dl class="dl-horizontal">
148 <dl class="dl-horizontal">
149 %for dt, dd, title, show_items in elements:
149 %for dt, dd, title, show_items in elements:
150 <dt>${dt}:</dt>
150 <dt>${dt}:</dt>
151 <dd title="${h.tooltip(title)}">
151 <dd title="${h.tooltip(title)}">
152 %if callable(dd):
152 %if callable(dd):
153 ## allow lazy evaluation of elements
153 ## allow lazy evaluation of elements
154 ${dd()}
154 ${dd()}
155 %else:
155 %else:
156 ${dd}
156 ${dd}
157 %endif
157 %endif
158 %if show_items:
158 %if show_items:
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
159 <span class="btn-collapse" data-toggle="item-${h.md5_safe(dt)[:6]}-details">${_('Show More')} </span>
160 %endif
160 %endif
161 </dd>
161 </dd>
162
162
163 %if show_items:
163 %if show_items:
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
164 <div class="collapsable-content" data-toggle="item-${h.md5_safe(dt)[:6]}-details" style="display: none">
165 %for item in show_items:
165 %for item in show_items:
166 <dt></dt>
166 <dt></dt>
167 <dd>${item}</dd>
167 <dd>${item}</dd>
168 %endfor
168 %endfor
169 </div>
169 </div>
170 %endif
170 %endif
171
171
172 %endfor
172 %endfor
173 </dl>
173 </dl>
174 </%def>
174 </%def>
175
175
176 <%def name="tr_info_entry(element)">
176 <%def name="tr_info_entry(element)">
177 <% key, val, title, show_items = element %>
177 <% key, val, title, show_items = element %>
178
178
179 <tr>
179 <tr>
180 <td style="vertical-align: top">${key}</td>
180 <td style="vertical-align: top">${key}</td>
181 <td title="${h.tooltip(title)}">
181 <td title="${h.tooltip(title)}">
182 %if callable(val):
182 %if callable(val):
183 ## allow lazy evaluation of elements
183 ## allow lazy evaluation of elements
184 ${val()}
184 ${val()}
185 %else:
185 %else:
186 ${val}
186 ${val}
187 %endif
187 %endif
188 %if show_items:
188 %if show_items:
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
189 <div class="collapsable-content" data-toggle="item-${h.md5_safe(val)[:6]}-details" style="display: none">
190 % for item in show_items:
190 % for item in show_items:
191 <dt></dt>
191 <dt></dt>
192 <dd>${item}</dd>
192 <dd>${item}</dd>
193 % endfor
193 % endfor
194 </div>
194 </div>
195 %endif
195 %endif
196 </td>
196 </td>
197 <td style="vertical-align: top">
197 <td style="vertical-align: top">
198 %if show_items:
198 %if show_items:
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
199 <span class="btn-collapse" data-toggle="item-${h.md5_safe(val)[:6]}-details">${_('Show More')} </span>
200 %endif
200 %endif
201 </td>
201 </td>
202 </tr>
202 </tr>
203
203
204 </%def>
204 </%def>
205
205
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
206 <%def name="gravatar(email, size=16, tooltip=False, tooltip_alt=None, user=None, extra_class=None)">
207 <%
207 <%
208 if size > 16:
208 if size > 16:
209 gravatar_class = ['gravatar','gravatar-large']
209 gravatar_class = ['gravatar','gravatar-large']
210 else:
210 else:
211 gravatar_class = ['gravatar']
211 gravatar_class = ['gravatar']
212
212
213 data_hovercard_url = ''
213 data_hovercard_url = ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
214 data_hovercard_alt = tooltip_alt.replace('<', '&lt;').replace('>', '&gt;') if tooltip_alt else ''
215
215
216 if tooltip:
216 if tooltip:
217 gravatar_class += ['tooltip-hovercard']
217 gravatar_class += ['tooltip-hovercard']
218 if extra_class:
218 if extra_class:
219 gravatar_class += extra_class
219 gravatar_class += extra_class
220 if tooltip and user:
220 if tooltip and user:
221 if user.username == h.DEFAULT_USER:
221 if user.username == h.DEFAULT_USER:
222 gravatar_class.pop(-1)
222 gravatar_class.pop(-1)
223 else:
223 else:
224 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
224 data_hovercard_url = request.route_path('hovercard_user', user_id=getattr(user, 'user_id', ''))
225 gravatar_class = ' '.join(gravatar_class)
225 gravatar_class = ' '.join(gravatar_class)
226
226
227 %>
227 %>
228 <%doc>
228 <%doc>
229 TODO: johbo: For now we serve double size images to make it smooth
229 TODO: johbo: For now we serve double size images to make it smooth
230 for retina. This is how it worked until now. Should be replaced
230 for retina. This is how it worked until now. Should be replaced
231 with a better solution at some point.
231 with a better solution at some point.
232 </%doc>
232 </%doc>
233
233
234 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
234 <img class="${gravatar_class}" height="${size}" width="${size}" data-hovercard-url="${data_hovercard_url}" data-hovercard-alt="${data_hovercard_alt}" src="${h.gravatar_url(email, size * 2)}" />
235 </%def>
235 </%def>
236
236
237
237
238 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
238 <%def name="gravatar_with_user(contact, size=16, show_disabled=False, tooltip=False, _class='rc-user')">
239 <%
239 <%
240 email = h.email_or_none(contact)
240 email = h.email_or_none(contact)
241 rc_user = h.discover_user(contact)
241 rc_user = h.discover_user(contact)
242 %>
242 %>
243
243
244 <div class="${_class}">
244 <div class="${_class}">
245 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
245 ${self.gravatar(email, size, tooltip=tooltip, tooltip_alt=contact, user=rc_user)}
246 <span class="${('user user-disabled' if show_disabled else 'user')}"> ${h.link_to_user(rc_user or contact)}</span>
246 <span class="${('user user-disabled' if show_disabled else 'user')}"> ${h.link_to_user(rc_user or contact)}</span>
247 </div>
247 </div>
248 </%def>
248 </%def>
249
249
250
250
251 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
251 <%def name="user_group_icon(user_group=None, size=16, tooltip=False)">
252 <%
252 <%
253 if (size > 16):
253 if (size > 16):
254 gravatar_class = 'icon-user-group-alt'
254 gravatar_class = 'icon-user-group-alt'
255 else:
255 else:
256 gravatar_class = 'icon-user-group-alt'
256 gravatar_class = 'icon-user-group-alt'
257
257
258 if tooltip:
258 if tooltip:
259 gravatar_class += ' tooltip-hovercard'
259 gravatar_class += ' tooltip-hovercard'
260
260
261 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
261 data_hovercard_url = request.route_path('hovercard_user_group', user_group_id=user_group.users_group_id)
262 %>
262 %>
263 <%doc>
263 <%doc>
264 TODO: johbo: For now we serve double size images to make it smooth
264 TODO: johbo: For now we serve double size images to make it smooth
265 for retina. This is how it worked until now. Should be replaced
265 for retina. This is how it worked until now. Should be replaced
266 with a better solution at some point.
266 with a better solution at some point.
267 </%doc>
267 </%doc>
268
268
269 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
269 <i style="font-size: ${size}px" class="${gravatar_class} x-icon-size-${size}" data-hovercard-url="${data_hovercard_url}"></i>
270 </%def>
270 </%def>
271
271
272 <%def name="repo_page_title(repo_instance)">
272 <%def name="repo_page_title(repo_instance)">
273 <div class="title-content repo-title">
273 <div class="title-content repo-title">
274
274
275 <div class="title-main">
275 <div class="title-main">
276 ## SVN/HG/GIT icons
276 ## SVN/HG/GIT icons
277 %if h.is_hg(repo_instance):
277 %if h.is_hg(repo_instance):
278 <i class="icon-hg"></i>
278 <i class="icon-hg"></i>
279 %endif
279 %endif
280 %if h.is_git(repo_instance):
280 %if h.is_git(repo_instance):
281 <i class="icon-git"></i>
281 <i class="icon-git"></i>
282 %endif
282 %endif
283 %if h.is_svn(repo_instance):
283 %if h.is_svn(repo_instance):
284 <i class="icon-svn"></i>
284 <i class="icon-svn"></i>
285 %endif
285 %endif
286
286
287 ## public/private
287 ## public/private
288 %if repo_instance.private:
288 %if repo_instance.private:
289 <i class="icon-repo-private"></i>
289 <i class="icon-repo-private"></i>
290 %else:
290 %else:
291 <i class="icon-repo-public"></i>
291 <i class="icon-repo-public"></i>
292 %endif
292 %endif
293
293
294 ## repo name with group name
294 ## repo name with group name
295 ${h.breadcrumb_repo_link(repo_instance)}
295 ${h.breadcrumb_repo_link(repo_instance)}
296
296
297 ## Context Actions
297 ## Context Actions
298 <div class="pull-right">
298 <div class="pull-right">
299 %if c.rhodecode_user.username != h.DEFAULT_USER:
299 %if c.rhodecode_user.username != h.DEFAULT_USER:
300 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
300 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid, _query=dict(auth_token=c.rhodecode_user.feed_token))}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
301
301
302 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
302 <a href="#WatchRepo" onclick="toggleFollowingRepo(this, templateContext.repo_id); return false" title="${_('Watch this Repository and actions on it in your personalized journal')}" class="btn btn-sm ${('watching' if c.repository_is_user_following else '')}">
303 % if c.repository_is_user_following:
303 % if c.repository_is_user_following:
304 <i class="icon-eye-off"></i>${_('Unwatch')}
304 <i class="icon-eye-off"></i>${_('Unwatch')}
305 % else:
305 % else:
306 <i class="icon-eye"></i>${_('Watch')}
306 <i class="icon-eye"></i>${_('Watch')}
307 % endif
307 % endif
308
308
309 </a>
309 </a>
310 %else:
310 %else:
311 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
311 <a href="${h.route_path('atom_feed_home', repo_name=c.rhodecode_db_repo.repo_uid)}" title="${_('RSS Feed')}" class="btn btn-sm"><i class="icon-rss-sign"></i>RSS</a>
312 %endif
312 %endif
313 </div>
313 </div>
314
314
315 </div>
315 </div>
316
316
317 ## FORKED
317 ## FORKED
318 %if repo_instance.fork:
318 %if repo_instance.fork:
319 <p class="discreet">
319 <p class="discreet">
320 <i class="icon-code-fork"></i> ${_('Fork of')}
320 <i class="icon-code-fork"></i> ${_('Fork of')}
321 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
321 ${h.link_to_if(c.has_origin_repo_read_perm,repo_instance.fork.repo_name, h.route_path('repo_summary', repo_name=repo_instance.fork.repo_name))}
322 </p>
322 </p>
323 %endif
323 %endif
324
324
325 ## IMPORTED FROM REMOTE
325 ## IMPORTED FROM REMOTE
326 %if repo_instance.clone_uri:
326 %if repo_instance.clone_uri:
327 <p class="discreet">
327 <p class="discreet">
328 <i class="icon-code-fork"></i> ${_('Clone from')}
328 <i class="icon-code-fork"></i> ${_('Clone from')}
329 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
329 <a href="${h.safe_str(h.hide_credentials(repo_instance.clone_uri))}">${h.hide_credentials(repo_instance.clone_uri)}</a>
330 </p>
330 </p>
331 %endif
331 %endif
332
332
333 ## LOCKING STATUS
333 ## LOCKING STATUS
334 %if repo_instance.locked[0]:
334 %if repo_instance.locked[0]:
335 <p class="locking_locked discreet">
335 <p class="locking_locked discreet">
336 <i class="icon-repo-lock"></i>
336 <i class="icon-repo-lock"></i>
337 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
337 ${_('Repository locked by %(user)s') % {'user': h.person_by_id(repo_instance.locked[0])}}
338 </p>
338 </p>
339 %elif repo_instance.enable_locking:
339 %elif repo_instance.enable_locking:
340 <p class="locking_unlocked discreet">
340 <p class="locking_unlocked discreet">
341 <i class="icon-repo-unlock"></i>
341 <i class="icon-repo-unlock"></i>
342 ${_('Repository not locked. Pull repository to lock it.')}
342 ${_('Repository not locked. Pull repository to lock it.')}
343 </p>
343 </p>
344 %endif
344 %endif
345
345
346 </div>
346 </div>
347 </%def>
347 </%def>
348
348
349 <%def name="repo_menu(active=None)">
349 <%def name="repo_menu(active=None)">
350 <%
350 <%
351 ## determine if we have "any" option available
351 ## determine if we have "any" option available
352 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
352 can_lock = h.HasRepoPermissionAny('repository.write','repository.admin')(c.repo_name) and c.rhodecode_db_repo.enable_locking
353 has_actions = can_lock
353 has_actions = can_lock
354
354
355 %>
355 %>
356 % if c.rhodecode_db_repo.archived:
356 % if c.rhodecode_db_repo.archived:
357 <div class="alert alert-warning text-center">
357 <div class="alert alert-warning text-center">
358 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
358 <strong>${_('This repository has been archived. It is now read-only.')}</strong>
359 </div>
359 </div>
360 % endif
360 % endif
361
361
362 <!--- REPO CONTEXT BAR -->
362 <!--- REPO CONTEXT BAR -->
363 <div id="context-bar">
363 <div id="context-bar">
364 <div class="wrapper">
364 <div class="wrapper">
365
365
366 <div class="title">
366 <div class="title">
367 ${self.repo_page_title(c.rhodecode_db_repo)}
367 ${self.repo_page_title(c.rhodecode_db_repo)}
368 </div>
368 </div>
369
369
370 <ul id="context-pages" class="navigation horizontal-list">
370 <ul id="context-pages" class="navigation horizontal-list">
371 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
371 <li class="${h.is_active('summary', active)}"><a class="menulink" href="${h.route_path('repo_summary_explicit', repo_name=c.repo_name)}"><div class="menulabel">${_('Summary')}</div></a></li>
372 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
372 <li class="${h.is_active('commits', active)}"><a class="menulink" href="${h.route_path('repo_commits', repo_name=c.repo_name)}"><div class="menulabel">${_('Commits')}</div></a></li>
373 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
373 <li class="${h.is_active('files', active)}"><a class="menulink" href="${h.repo_files_by_ref_url(c.repo_name, c.rhodecode_db_repo.repo_type, f_path='', ref_name=c.rhodecode_db_repo.landing_ref_name, commit_id='tip', query={'at':c.rhodecode_db_repo.landing_ref_name})}"><div class="menulabel">${_('Files')}</div></a></li>
374 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
374 <li class="${h.is_active('compare', active)}"><a class="menulink" href="${h.route_path('repo_compare_select',repo_name=c.repo_name)}"><div class="menulabel">${_('Compare')}</div></a></li>
375
375
376 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
376 ## TODO: anderson: ideally it would have a function on the scm_instance "enable_pullrequest() and enable_fork()"
377 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
377 %if c.rhodecode_db_repo.repo_type in ['git','hg']:
378 <li class="${h.is_active('showpullrequest', active)}">
378 <li class="${h.is_active('showpullrequest', active)}">
379 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
379 <a class="menulink" href="${h.route_path('pullrequest_show_all', repo_name=c.repo_name)}" title="${h.tooltip(_('Show Pull Requests for %s') % c.repo_name)}">
380 <div class="menulabel">
380 <div class="menulabel">
381 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
381 ${_('Pull Requests')} <span class="menulink-counter">${c.repository_pull_requests}</span>
382 </div>
382 </div>
383 </a>
383 </a>
384 </li>
384 </li>
385 %endif
385 %endif
386
386
387 <li class="${h.is_active('artifacts', active)}">
387 <li class="${h.is_active('artifacts', active)}">
388 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
388 <a class="menulink" href="${h.route_path('repo_artifacts_list',repo_name=c.repo_name)}">
389 <div class="menulabel">
389 <div class="menulabel">
390 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
390 ${_('Artifacts')} <span class="menulink-counter">${c.repository_artifacts}</span>
391 </div>
391 </div>
392 </a>
392 </a>
393 </li>
393 </li>
394
394
395 %if h.HasRepoPermissionAll('repository.admin')(c.repo_name):
395 %if not c.rhodecode_db_repo.archived and h.HasRepoPermissionAll('repository.admin')(c.repo_name):
396 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
396 <li class="${h.is_active('settings', active)}"><a class="menulink" href="${h.route_path('edit_repo',repo_name=c.repo_name)}"><div class="menulabel">${_('Repository Settings')}</div></a></li>
397 %endif
397 %endif
398
398
399 <li class="${h.is_active('options', active)}">
399 <li class="${h.is_active('options', active)}">
400 % if has_actions:
400 % if has_actions:
401 <a class="menulink dropdown">
401 <a class="menulink dropdown">
402 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
402 <div class="menulabel">${_('Options')}<div class="show_more"></div></div>
403 </a>
403 </a>
404 <ul class="submenu">
404 <ul class="submenu">
405 %if can_lock:
405 %if can_lock:
406 %if c.rhodecode_db_repo.locked[0]:
406 %if c.rhodecode_db_repo.locked[0]:
407 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
407 <li><a class="locking_del" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Unlock Repository')}</a></li>
408 %else:
408 %else:
409 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
409 <li><a class="locking_add" href="${h.route_path('repo_edit_toggle_locking',repo_name=c.repo_name)}">${_('Lock Repository')}</a></li>
410 %endif
410 %endif
411 %endif
411 %endif
412 </ul>
412 </ul>
413 % endif
413 % endif
414 </li>
414 </li>
415
415
416 </ul>
416 </ul>
417 </div>
417 </div>
418 <div class="clear"></div>
418 <div class="clear"></div>
419 </div>
419 </div>
420
420
421 <!--- REPO END CONTEXT BAR -->
421 <!--- REPO END CONTEXT BAR -->
422
422
423 </%def>
423 </%def>
424
424
425 <%def name="repo_group_page_title(repo_group_instance)">
425 <%def name="repo_group_page_title(repo_group_instance)">
426 <div class="title-content">
426 <div class="title-content">
427 <div class="title-main">
427 <div class="title-main">
428 ## Repository Group icon
428 ## Repository Group icon
429 <i class="icon-repo-group"></i>
429 <i class="icon-repo-group"></i>
430
430
431 ## repo name with group name
431 ## repo name with group name
432 ${h.breadcrumb_repo_group_link(repo_group_instance)}
432 ${h.breadcrumb_repo_group_link(repo_group_instance)}
433 </div>
433 </div>
434
434
435 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
435 <%namespace name="dt" file="/data_table/_dt_elements.mako"/>
436 <div class="repo-group-desc discreet">
436 <div class="repo-group-desc discreet">
437 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
437 ${dt.repo_group_desc(repo_group_instance.description_safe, repo_group_instance.personal, c.visual.stylify_metatags)}
438 </div>
438 </div>
439
439
440 </div>
440 </div>
441 </%def>
441 </%def>
442
442
443
443
444 <%def name="repo_group_menu(active=None)">
444 <%def name="repo_group_menu(active=None)">
445 <%
445 <%
446 gr_name = c.repo_group.group_name if c.repo_group else None
446 gr_name = c.repo_group.group_name if c.repo_group else None
447 # create repositories with write permission on group is set to true
447 # create repositories with write permission on group is set to true
448 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
448 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
449
449
450 %>
450 %>
451
451
452
452
453 <!--- REPO GROUP CONTEXT BAR -->
453 <!--- REPO GROUP CONTEXT BAR -->
454 <div id="context-bar">
454 <div id="context-bar">
455 <div class="wrapper">
455 <div class="wrapper">
456 <div class="title">
456 <div class="title">
457 ${self.repo_group_page_title(c.repo_group)}
457 ${self.repo_group_page_title(c.repo_group)}
458 </div>
458 </div>
459
459
460 <ul id="context-pages" class="navigation horizontal-list">
460 <ul id="context-pages" class="navigation horizontal-list">
461 <li class="${h.is_active('home', active)}">
461 <li class="${h.is_active('home', active)}">
462 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
462 <a class="menulink" href="${h.route_path('repo_group_home', repo_group_name=c.repo_group.group_name)}"><div class="menulabel">${_('Group Home')}</div></a>
463 </li>
463 </li>
464 % if c.is_super_admin or group_admin:
464 % if c.is_super_admin or group_admin:
465 <li class="${h.is_active('settings', active)}">
465 <li class="${h.is_active('settings', active)}">
466 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
466 <a class="menulink" href="${h.route_path('edit_repo_group',repo_group_name=c.repo_group.group_name)}" title="${_('You have admin right to this group, and can edit it')}"><div class="menulabel">${_('Group Settings')}</div></a>
467 </li>
467 </li>
468 % endif
468 % endif
469
469
470 </ul>
470 </ul>
471 </div>
471 </div>
472 <div class="clear"></div>
472 <div class="clear"></div>
473 </div>
473 </div>
474
474
475 <!--- REPO GROUP CONTEXT BAR -->
475 <!--- REPO GROUP CONTEXT BAR -->
476
476
477 </%def>
477 </%def>
478
478
479
479
480 <%def name="usermenu(active=False)">
480 <%def name="usermenu(active=False)">
481 <%
481 <%
482 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
482 not_anonymous = c.rhodecode_user.username != h.DEFAULT_USER
483
483
484 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
484 gr_name = c.repo_group.group_name if (hasattr(c, 'repo_group') and c.repo_group) else None
485 # create repositories with write permission on group is set to true
485 # create repositories with write permission on group is set to true
486
486
487 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
487 can_fork = c.is_super_admin or h.HasPermissionAny('hg.fork.repository')()
488 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
488 create_on_write = h.HasPermissionAny('hg.create.write_on_repogroup.true')()
489 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
489 group_write = h.HasRepoGroupPermissionAny('group.write')(gr_name, 'can write into group index page')
490 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
490 group_admin = h.HasRepoGroupPermissionAny('group.admin')(gr_name, 'group admin index page')
491
491
492 can_create_repos = c.is_super_admin or c.can_create_repo
492 can_create_repos = c.is_super_admin or c.can_create_repo
493 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
493 can_create_repo_groups = c.is_super_admin or c.can_create_repo_group
494
494
495 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
495 can_create_repos_in_group = c.is_super_admin or group_admin or (group_write and create_on_write)
496 can_create_repo_groups_in_group = c.is_super_admin or group_admin
496 can_create_repo_groups_in_group = c.is_super_admin or group_admin
497 %>
497 %>
498
498
499 % if not_anonymous:
499 % if not_anonymous:
500 <%
500 <%
501 default_target_group = dict()
501 default_target_group = dict()
502 if c.rhodecode_user.personal_repo_group:
502 if c.rhodecode_user.personal_repo_group:
503 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
503 default_target_group = dict(parent_group=c.rhodecode_user.personal_repo_group.group_id)
504 %>
504 %>
505
505
506 ## create action
506 ## create action
507 <li>
507 <li>
508 <a href="#create-actions" onclick="return false;" class="menulink childs">
508 <a href="#create-actions" onclick="return false;" class="menulink childs">
509 <i class="tooltip icon-plus-circled" title="${_('Create')}"></i>
509 <i class="tooltip icon-plus-circled" title="${_('Create')}"></i>
510 </a>
510 </a>
511
511
512 <div class="action-menu submenu">
512 <div class="action-menu submenu">
513
513
514 <ol>
514 <ol>
515 ## scope of within a repository
515 ## scope of within a repository
516 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
516 % if hasattr(c, 'rhodecode_db_repo') and c.rhodecode_db_repo:
517 <li class="submenu-title">${_('This Repository')}</li>
517 <li class="submenu-title">${_('This Repository')}</li>
518 <li>
518 <li>
519 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
519 <a href="${h.route_path('pullrequest_new',repo_name=c.repo_name)}">${_('Create Pull Request')}</a>
520 </li>
520 </li>
521 % if can_fork:
521 % if can_fork:
522 <li>
522 <li>
523 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
523 <a href="${h.route_path('repo_fork_new',repo_name=c.repo_name,_query=default_target_group)}">${_('Fork this repository')}</a>
524 </li>
524 </li>
525 % endif
525 % endif
526 % endif
526 % endif
527
527
528 ## scope of within repository groups
528 ## scope of within repository groups
529 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
529 % if hasattr(c, 'repo_group') and c.repo_group and (can_create_repos_in_group or can_create_repo_groups_in_group):
530 <li class="submenu-title">${_('This Repository Group')}</li>
530 <li class="submenu-title">${_('This Repository Group')}</li>
531
531
532 % if can_create_repos_in_group:
532 % if can_create_repos_in_group:
533 <li>
533 <li>
534 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
534 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.repo_group.group_id))}">${_('New Repository')}</a>
535 </li>
535 </li>
536 % endif
536 % endif
537
537
538 % if can_create_repo_groups_in_group:
538 % if can_create_repo_groups_in_group:
539 <li>
539 <li>
540 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
540 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.repo_group.group_id))}">${_(u'New Repository Group')}</a>
541 </li>
541 </li>
542 % endif
542 % endif
543 % endif
543 % endif
544
544
545 ## personal group
545 ## personal group
546 % if c.rhodecode_user.personal_repo_group:
546 % if c.rhodecode_user.personal_repo_group:
547 <li class="submenu-title">Personal Group</li>
547 <li class="submenu-title">Personal Group</li>
548
548
549 <li>
549 <li>
550 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
550 <a href="${h.route_path('repo_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}" >${_('New Repository')} </a>
551 </li>
551 </li>
552
552
553 <li>
553 <li>
554 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
554 <a href="${h.route_path('repo_group_new',_query=dict(parent_group=c.rhodecode_user.personal_repo_group.group_id))}">${_('New Repository Group')} </a>
555 </li>
555 </li>
556 % endif
556 % endif
557
557
558 ## Global actions
558 ## Global actions
559 <li class="submenu-title">RhodeCode</li>
559 <li class="submenu-title">RhodeCode</li>
560 % if can_create_repos:
560 % if can_create_repos:
561 <li>
561 <li>
562 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
562 <a href="${h.route_path('repo_new')}" >${_('New Repository')}</a>
563 </li>
563 </li>
564 % endif
564 % endif
565
565
566 % if can_create_repo_groups:
566 % if can_create_repo_groups:
567 <li>
567 <li>
568 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
568 <a href="${h.route_path('repo_group_new')}" >${_(u'New Repository Group')}</a>
569 </li>
569 </li>
570 % endif
570 % endif
571
571
572 <li>
572 <li>
573 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
573 <a href="${h.route_path('gists_new')}">${_(u'New Gist')}</a>
574 </li>
574 </li>
575
575
576 </ol>
576 </ol>
577
577
578 </div>
578 </div>
579 </li>
579 </li>
580
580
581 ## notifications
581 ## notifications
582 <li>
582 <li>
583 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
583 <a class="${('empty' if c.unread_notifications == 0 else '')}" href="${h.route_path('notifications_show_all')}">
584 ${c.unread_notifications}
584 ${c.unread_notifications}
585 </a>
585 </a>
586 </li>
586 </li>
587 % endif
587 % endif
588
588
589 ## USER MENU
589 ## USER MENU
590 <li id="quick_login_li" class="${'active' if active else ''}">
590 <li id="quick_login_li" class="${'active' if active else ''}">
591 % if c.rhodecode_user.username == h.DEFAULT_USER:
591 % if c.rhodecode_user.username == h.DEFAULT_USER:
592 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
592 <a id="quick_login_link" class="menulink childs" href="${h.route_path('login', _query={'came_from': h.current_route_path(request)})}">
593 ${gravatar(c.rhodecode_user.email, 20)}
593 ${gravatar(c.rhodecode_user.email, 20)}
594 <span class="user">
594 <span class="user">
595 <span>${_('Sign in')}</span>
595 <span>${_('Sign in')}</span>
596 </span>
596 </span>
597 </a>
597 </a>
598 % else:
598 % else:
599 ## logged in user
599 ## logged in user
600 <a id="quick_login_link" class="menulink childs">
600 <a id="quick_login_link" class="menulink childs">
601 ${gravatar(c.rhodecode_user.email, 20)}
601 ${gravatar(c.rhodecode_user.email, 20)}
602 <span class="user">
602 <span class="user">
603 <span class="menu_link_user">${c.rhodecode_user.username}</span>
603 <span class="menu_link_user">${c.rhodecode_user.username}</span>
604 <div class="show_more"></div>
604 <div class="show_more"></div>
605 </span>
605 </span>
606 </a>
606 </a>
607 ## subnav with menu for logged in user
607 ## subnav with menu for logged in user
608 <div class="user-menu submenu">
608 <div class="user-menu submenu">
609 <div id="quick_login">
609 <div id="quick_login">
610 %if c.rhodecode_user.username != h.DEFAULT_USER:
610 %if c.rhodecode_user.username != h.DEFAULT_USER:
611 <div class="">
611 <div class="">
612 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
612 <div class="big_gravatar">${gravatar(c.rhodecode_user.email, 48)}</div>
613 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
613 <div class="full_name">${c.rhodecode_user.full_name_or_username}</div>
614 <div class="email">${c.rhodecode_user.email}</div>
614 <div class="email">${c.rhodecode_user.email}</div>
615 </div>
615 </div>
616 <div class="">
616 <div class="">
617 <ol class="links">
617 <ol class="links">
618 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
618 <li>${h.link_to(_(u'My account'),h.route_path('my_account_profile'))}</li>
619 % if c.rhodecode_user.personal_repo_group:
619 % if c.rhodecode_user.personal_repo_group:
620 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
620 <li>${h.link_to(_(u'My personal group'), h.route_path('repo_group_home', repo_group_name=c.rhodecode_user.personal_repo_group.group_name))}</li>
621 % endif
621 % endif
622 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
622 <li>${h.link_to(_(u'Pull Requests'), h.route_path('my_account_pullrequests'))}</li>
623
623
624 % if c.debug_style:
624 % if c.debug_style:
625 <li>
625 <li>
626 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
626 <a class="menulink" title="${_('Style')}" href="${h.route_path('debug_style_home')}">
627 <div class="menulabel">${_('[Style]')}</div>
627 <div class="menulabel">${_('[Style]')}</div>
628 </a>
628 </a>
629 </li>
629 </li>
630 % endif
630 % endif
631
631
632 ## bookmark-items
632 ## bookmark-items
633 <li class="bookmark-items">
633 <li class="bookmark-items">
634 ${_('Bookmarks')}
634 ${_('Bookmarks')}
635 <div class="pull-right">
635 <div class="pull-right">
636 <a href="${h.route_path('my_account_bookmarks')}">
636 <a href="${h.route_path('my_account_bookmarks')}">
637
637
638 <i class="icon-cog"></i>
638 <i class="icon-cog"></i>
639 </a>
639 </a>
640 </div>
640 </div>
641 </li>
641 </li>
642 % if not c.bookmark_items:
642 % if not c.bookmark_items:
643 <li>
643 <li>
644 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
644 <a href="${h.route_path('my_account_bookmarks')}">${_('No Bookmarks yet.')}</a>
645 </li>
645 </li>
646 % endif
646 % endif
647 % for item in c.bookmark_items:
647 % for item in c.bookmark_items:
648 <li>
648 <li>
649 % if item.repository:
649 % if item.repository:
650 <div>
650 <div>
651 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
651 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
652 <code>${item.position}</code>
652 <code>${item.position}</code>
653 % if item.repository.repo_type == 'hg':
653 % if item.repository.repo_type == 'hg':
654 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
654 <i class="icon-hg" title="${_('Repository')}" style="font-size: 16px"></i>
655 % elif item.repository.repo_type == 'git':
655 % elif item.repository.repo_type == 'git':
656 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
656 <i class="icon-git" title="${_('Repository')}" style="font-size: 16px"></i>
657 % elif item.repository.repo_type == 'svn':
657 % elif item.repository.repo_type == 'svn':
658 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
658 <i class="icon-svn" title="${_('Repository')}" style="font-size: 16px"></i>
659 % endif
659 % endif
660 ${(item.title or h.shorter(item.repository.repo_name, 30))}
660 ${(item.title or h.shorter(item.repository.repo_name, 30))}
661 </a>
661 </a>
662 </div>
662 </div>
663 % elif item.repository_group:
663 % elif item.repository_group:
664 <div>
664 <div>
665 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
665 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
666 <code>${item.position}</code>
666 <code>${item.position}</code>
667 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
667 <i class="icon-repo-group" title="${_('Repository group')}" style="font-size: 14px"></i>
668 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
668 ${(item.title or h.shorter(item.repository_group.group_name, 30))}
669 </a>
669 </a>
670 </div>
670 </div>
671 % else:
671 % else:
672 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
672 <a class="bookmark-item" href="${h.route_path('my_account_goto_bookmark', bookmark_id=item.position)}">
673 <code>${item.position}</code>
673 <code>${item.position}</code>
674 ${item.title}
674 ${item.title}
675 </a>
675 </a>
676 % endif
676 % endif
677 </li>
677 </li>
678 % endfor
678 % endfor
679
679
680 <li class="logout">
680 <li class="logout">
681 ${h.secure_form(h.route_path('logout'), request=request)}
681 ${h.secure_form(h.route_path('logout'), request=request)}
682 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
682 ${h.submit('log_out', _(u'Sign Out'),class_="btn btn-primary")}
683 ${h.end_form()}
683 ${h.end_form()}
684 </li>
684 </li>
685 </ol>
685 </ol>
686 </div>
686 </div>
687 %endif
687 %endif
688 </div>
688 </div>
689 </div>
689 </div>
690
690
691 % endif
691 % endif
692 </li>
692 </li>
693 </%def>
693 </%def>
694
694
695 <%def name="menu_items(active=None)">
695 <%def name="menu_items(active=None)">
696 <%
696 <%
697 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
697 notice_messages, notice_level = c.rhodecode_user.get_notice_messages()
698 notice_display = 'none' if len(notice_messages) == 0 else ''
698 notice_display = 'none' if len(notice_messages) == 0 else ''
699 %>
699 %>
700 <style>
700 <style>
701
701
702 </style>
702 </style>
703
703
704 <ul id="quick" class="main_nav navigation horizontal-list">
704 <ul id="quick" class="main_nav navigation horizontal-list">
705 ## notice box for important system messages
705 ## notice box for important system messages
706 <li style="display: ${notice_display}">
706 <li style="display: ${notice_display}">
707 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
707 <a class="notice-box" href="#openNotice" onclick="$('.notice-messages-container').toggle(); return false">
708 <div class="menulabel-notice ${notice_level}" >
708 <div class="menulabel-notice ${notice_level}" >
709 ${len(notice_messages)}
709 ${len(notice_messages)}
710 </div>
710 </div>
711 </a>
711 </a>
712 </li>
712 </li>
713 <div class="notice-messages-container" style="display: none">
713 <div class="notice-messages-container" style="display: none">
714 <div class="notice-messages">
714 <div class="notice-messages">
715 <table class="rctable">
715 <table class="rctable">
716 % for notice in notice_messages:
716 % for notice in notice_messages:
717 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
717 <tr id="notice-message-${notice['msg_id']}" class="notice-message-${notice['level']}">
718 <td style="vertical-align: text-top; width: 20px">
718 <td style="vertical-align: text-top; width: 20px">
719 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
719 <i class="tooltip icon-info notice-color-${notice['level']}" title="${notice['level']}"></i>
720 </td>
720 </td>
721 <td>
721 <td>
722 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
722 <span><i class="icon-plus-squared cursor-pointer" onclick="$('#notice-${notice['msg_id']}').toggle()"></i> </span>
723 ${notice['subject']}
723 ${notice['subject']}
724
724
725 <div id="notice-${notice['msg_id']}" style="display: none">
725 <div id="notice-${notice['msg_id']}" style="display: none">
726 ${h.render(notice['body'], renderer='markdown')}
726 ${h.render(notice['body'], renderer='markdown')}
727 </div>
727 </div>
728 </td>
728 </td>
729 <td style="vertical-align: text-top; width: 35px;">
729 <td style="vertical-align: text-top; width: 35px;">
730 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
730 <a class="tooltip" title="${_('dismiss')}" href="#dismiss" onclick="dismissNotice(${notice['msg_id']});return false">
731 <i class="icon-remove icon-filled-red"></i>
731 <i class="icon-remove icon-filled-red"></i>
732 </a>
732 </a>
733 </td>
733 </td>
734 </tr>
734 </tr>
735
735
736 % endfor
736 % endfor
737 </table>
737 </table>
738 </div>
738 </div>
739 </div>
739 </div>
740 ## Main filter
740 ## Main filter
741 <li>
741 <li>
742 <div class="menulabel main_filter_box">
742 <div class="menulabel main_filter_box">
743 <div class="main_filter_input_box">
743 <div class="main_filter_input_box">
744 <ul class="searchItems">
744 <ul class="searchItems">
745
745
746 <li class="searchTag searchTagIcon">
746 <li class="searchTag searchTagIcon">
747 <i class="icon-search"></i>
747 <i class="icon-search"></i>
748 </li>
748 </li>
749
749
750 % if c.template_context['search_context']['repo_id']:
750 % if c.template_context['search_context']['repo_id']:
751 <li class="searchTag searchTagFilter searchTagHidable" >
751 <li class="searchTag searchTagFilter searchTagHidable" >
752 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
752 ##<a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">
753 <span class="tag">
753 <span class="tag">
754 This repo
754 This repo
755 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
755 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
756 </span>
756 </span>
757 ##</a>
757 ##</a>
758 </li>
758 </li>
759 % elif c.template_context['search_context']['repo_group_id']:
759 % elif c.template_context['search_context']['repo_group_id']:
760 <li class="searchTag searchTagFilter searchTagHidable">
760 <li class="searchTag searchTagFilter searchTagHidable">
761 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
761 ##<a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">
762 <span class="tag">
762 <span class="tag">
763 This group
763 This group
764 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
764 <a href="#removeGoToFilter" onclick="removeGoToFilter(); return false"><i class="icon-cancel-circled"></i></a>
765 </span>
765 </span>
766 ##</a>
766 ##</a>
767 </li>
767 </li>
768 % endif
768 % endif
769
769
770 <li class="searchTagInput">
770 <li class="searchTagInput">
771 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
771 <input class="main_filter_input" id="main_filter" size="25" type="text" name="main_filter" placeholder="${_('search / go to...')}" value="" />
772 </li>
772 </li>
773 <li class="searchTag searchTagHelp">
773 <li class="searchTag searchTagHelp">
774 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
774 <a href="#showFilterHelp" onclick="showMainFilterBox(); return false">?</a>
775 </li>
775 </li>
776 </ul>
776 </ul>
777 </div>
777 </div>
778 </div>
778 </div>
779
779
780 <div id="main_filter_help" style="display: none">
780 <div id="main_filter_help" style="display: none">
781 - Use '/' key to quickly access this field.
781 - Use '/' key to quickly access this field.
782
782
783 - Enter a name of repository, or repository group for quick search.
783 - Enter a name of repository, or repository group for quick search.
784
784
785 - Prefix query to allow special search:
785 - Prefix query to allow special search:
786
786
787 user:admin, to search for usernames, always global
787 user:admin, to search for usernames, always global
788
788
789 user_group:devops, to search for user groups, always global
789 user_group:devops, to search for user groups, always global
790
790
791 pr:303, to search for pull request number, title, or description, always global
791 pr:303, to search for pull request number, title, or description, always global
792
792
793 commit:efced4, to search for commits, scoped to repositories or groups
793 commit:efced4, to search for commits, scoped to repositories or groups
794
794
795 file:models.py, to search for file paths, scoped to repositories or groups
795 file:models.py, to search for file paths, scoped to repositories or groups
796
796
797 % if c.template_context['search_context']['repo_id']:
797 % if c.template_context['search_context']['repo_id']:
798 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
798 For advanced full text search visit: <a href="${h.route_path('search_repo',repo_name=c.template_context['search_context']['repo_name'])}">repository search</a>
799 % elif c.template_context['search_context']['repo_group_id']:
799 % elif c.template_context['search_context']['repo_group_id']:
800 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
800 For advanced full text search visit: <a href="${h.route_path('search_repo_group',repo_group_name=c.template_context['search_context']['repo_group_name'])}">repository group search</a>
801 % else:
801 % else:
802 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
802 For advanced full text search visit: <a href="${h.route_path('search')}">global search</a>
803 % endif
803 % endif
804 </div>
804 </div>
805 </li>
805 </li>
806
806
807 ## ROOT MENU
807 ## ROOT MENU
808 <li class="${h.is_active('home', active)}">
808 <li class="${h.is_active('home', active)}">
809 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
809 <a class="menulink" title="${_('Home')}" href="${h.route_path('home')}">
810 <div class="menulabel">${_('Home')}</div>
810 <div class="menulabel">${_('Home')}</div>
811 </a>
811 </a>
812 </li>
812 </li>
813
813
814 %if c.rhodecode_user.username != h.DEFAULT_USER:
814 %if c.rhodecode_user.username != h.DEFAULT_USER:
815 <li class="${h.is_active('journal', active)}">
815 <li class="${h.is_active('journal', active)}">
816 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
816 <a class="menulink" title="${_('Show activity journal')}" href="${h.route_path('journal')}">
817 <div class="menulabel">${_('Journal')}</div>
817 <div class="menulabel">${_('Journal')}</div>
818 </a>
818 </a>
819 </li>
819 </li>
820 %else:
820 %else:
821 <li class="${h.is_active('journal', active)}">
821 <li class="${h.is_active('journal', active)}">
822 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
822 <a class="menulink" title="${_('Show Public activity journal')}" href="${h.route_path('journal_public')}">
823 <div class="menulabel">${_('Public journal')}</div>
823 <div class="menulabel">${_('Public journal')}</div>
824 </a>
824 </a>
825 </li>
825 </li>
826 %endif
826 %endif
827
827
828 <li class="${h.is_active('gists', active)}">
828 <li class="${h.is_active('gists', active)}">
829 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
829 <a class="menulink childs" title="${_('Show Gists')}" href="${h.route_path('gists_show')}">
830 <div class="menulabel">${_('Gists')}</div>
830 <div class="menulabel">${_('Gists')}</div>
831 </a>
831 </a>
832 </li>
832 </li>
833
833
834 % if c.is_super_admin or c.is_delegated_admin:
834 % if c.is_super_admin or c.is_delegated_admin:
835 <li class="${h.is_active('admin', active)}">
835 <li class="${h.is_active('admin', active)}">
836 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
836 <a class="menulink childs" title="${_('Admin settings')}" href="${h.route_path('admin_home')}">
837 <div class="menulabel">${_('Admin')} </div>
837 <div class="menulabel">${_('Admin')} </div>
838 </a>
838 </a>
839 </li>
839 </li>
840 % endif
840 % endif
841
841
842 ## render extra user menu
842 ## render extra user menu
843 ${usermenu(active=(active=='my_account'))}
843 ${usermenu(active=(active=='my_account'))}
844
844
845 </ul>
845 </ul>
846
846
847 <script type="text/javascript">
847 <script type="text/javascript">
848 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
848 var visualShowPublicIcon = "${c.visual.show_public_icon}" == "True";
849
849
850 var formatRepoResult = function(result, container, query, escapeMarkup) {
850 var formatRepoResult = function(result, container, query, escapeMarkup) {
851 return function(data, escapeMarkup) {
851 return function(data, escapeMarkup) {
852 if (!data.repo_id){
852 if (!data.repo_id){
853 return data.text; // optgroup text Repositories
853 return data.text; // optgroup text Repositories
854 }
854 }
855
855
856 var tmpl = '';
856 var tmpl = '';
857 var repoType = data['repo_type'];
857 var repoType = data['repo_type'];
858 var repoName = data['text'];
858 var repoName = data['text'];
859
859
860 if(data && data.type == 'repo'){
860 if(data && data.type == 'repo'){
861 if(repoType === 'hg'){
861 if(repoType === 'hg'){
862 tmpl += '<i class="icon-hg"></i> ';
862 tmpl += '<i class="icon-hg"></i> ';
863 }
863 }
864 else if(repoType === 'git'){
864 else if(repoType === 'git'){
865 tmpl += '<i class="icon-git"></i> ';
865 tmpl += '<i class="icon-git"></i> ';
866 }
866 }
867 else if(repoType === 'svn'){
867 else if(repoType === 'svn'){
868 tmpl += '<i class="icon-svn"></i> ';
868 tmpl += '<i class="icon-svn"></i> ';
869 }
869 }
870 if(data['private']){
870 if(data['private']){
871 tmpl += '<i class="icon-lock" ></i> ';
871 tmpl += '<i class="icon-lock" ></i> ';
872 }
872 }
873 else if(visualShowPublicIcon){
873 else if(visualShowPublicIcon){
874 tmpl += '<i class="icon-unlock-alt"></i> ';
874 tmpl += '<i class="icon-unlock-alt"></i> ';
875 }
875 }
876 }
876 }
877 tmpl += escapeMarkup(repoName);
877 tmpl += escapeMarkup(repoName);
878 return tmpl;
878 return tmpl;
879
879
880 }(result, escapeMarkup);
880 }(result, escapeMarkup);
881 };
881 };
882
882
883 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
883 var formatRepoGroupResult = function(result, container, query, escapeMarkup) {
884 return function(data, escapeMarkup) {
884 return function(data, escapeMarkup) {
885 if (!data.repo_group_id){
885 if (!data.repo_group_id){
886 return data.text; // optgroup text Repositories
886 return data.text; // optgroup text Repositories
887 }
887 }
888
888
889 var tmpl = '';
889 var tmpl = '';
890 var repoGroupName = data['text'];
890 var repoGroupName = data['text'];
891
891
892 if(data){
892 if(data){
893
893
894 tmpl += '<i class="icon-repo-group"></i> ';
894 tmpl += '<i class="icon-repo-group"></i> ';
895
895
896 }
896 }
897 tmpl += escapeMarkup(repoGroupName);
897 tmpl += escapeMarkup(repoGroupName);
898 return tmpl;
898 return tmpl;
899
899
900 }(result, escapeMarkup);
900 }(result, escapeMarkup);
901 };
901 };
902
902
903 var escapeRegExChars = function (value) {
903 var escapeRegExChars = function (value) {
904 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
904 return value.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
905 };
905 };
906
906
907 var getRepoIcon = function(repo_type) {
907 var getRepoIcon = function(repo_type) {
908 if (repo_type === 'hg') {
908 if (repo_type === 'hg') {
909 return '<i class="icon-hg"></i> ';
909 return '<i class="icon-hg"></i> ';
910 }
910 }
911 else if (repo_type === 'git') {
911 else if (repo_type === 'git') {
912 return '<i class="icon-git"></i> ';
912 return '<i class="icon-git"></i> ';
913 }
913 }
914 else if (repo_type === 'svn') {
914 else if (repo_type === 'svn') {
915 return '<i class="icon-svn"></i> ';
915 return '<i class="icon-svn"></i> ';
916 }
916 }
917 return ''
917 return ''
918 };
918 };
919
919
920 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
920 var autocompleteMainFilterFormatResult = function (data, value, org_formatter) {
921
921
922 if (value.split(':').length === 2) {
922 if (value.split(':').length === 2) {
923 value = value.split(':')[1]
923 value = value.split(':')[1]
924 }
924 }
925
925
926 var searchType = data['type'];
926 var searchType = data['type'];
927 var searchSubType = data['subtype'];
927 var searchSubType = data['subtype'];
928 var valueDisplay = data['value_display'];
928 var valueDisplay = data['value_display'];
929 var valueIcon = data['value_icon'];
929 var valueIcon = data['value_icon'];
930
930
931 var pattern = '(' + escapeRegExChars(value) + ')';
931 var pattern = '(' + escapeRegExChars(value) + ')';
932
932
933 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
933 valueDisplay = Select2.util.escapeMarkup(valueDisplay);
934
934
935 // highlight match
935 // highlight match
936 if (searchType != 'text') {
936 if (searchType != 'text') {
937 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
937 valueDisplay = valueDisplay.replace(new RegExp(pattern, 'gi'), '<strong>$1<\/strong>');
938 }
938 }
939
939
940 var icon = '';
940 var icon = '';
941
941
942 if (searchType === 'hint') {
942 if (searchType === 'hint') {
943 icon += '<i class="icon-repo-group"></i> ';
943 icon += '<i class="icon-repo-group"></i> ';
944 }
944 }
945 // full text search/hints
945 // full text search/hints
946 else if (searchType === 'search') {
946 else if (searchType === 'search') {
947 if (valueIcon === undefined) {
947 if (valueIcon === undefined) {
948 icon += '<i class="icon-more"></i> ';
948 icon += '<i class="icon-more"></i> ';
949 } else {
949 } else {
950 icon += valueIcon + ' ';
950 icon += valueIcon + ' ';
951 }
951 }
952
952
953 if (searchSubType !== undefined && searchSubType == 'repo') {
953 if (searchSubType !== undefined && searchSubType == 'repo') {
954 valueDisplay += '<div class="pull-right tag">repository</div>';
954 valueDisplay += '<div class="pull-right tag">repository</div>';
955 }
955 }
956 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
956 else if (searchSubType !== undefined && searchSubType == 'repo_group') {
957 valueDisplay += '<div class="pull-right tag">repo group</div>';
957 valueDisplay += '<div class="pull-right tag">repo group</div>';
958 }
958 }
959 }
959 }
960 // repository
960 // repository
961 else if (searchType === 'repo') {
961 else if (searchType === 'repo') {
962
962
963 var repoIcon = getRepoIcon(data['repo_type']);
963 var repoIcon = getRepoIcon(data['repo_type']);
964 icon += repoIcon;
964 icon += repoIcon;
965
965
966 if (data['private']) {
966 if (data['private']) {
967 icon += '<i class="icon-lock" ></i> ';
967 icon += '<i class="icon-lock" ></i> ';
968 }
968 }
969 else if (visualShowPublicIcon) {
969 else if (visualShowPublicIcon) {
970 icon += '<i class="icon-unlock-alt"></i> ';
970 icon += '<i class="icon-unlock-alt"></i> ';
971 }
971 }
972 }
972 }
973 // repository groups
973 // repository groups
974 else if (searchType === 'repo_group') {
974 else if (searchType === 'repo_group') {
975 icon += '<i class="icon-repo-group"></i> ';
975 icon += '<i class="icon-repo-group"></i> ';
976 }
976 }
977 // user group
977 // user group
978 else if (searchType === 'user_group') {
978 else if (searchType === 'user_group') {
979 icon += '<i class="icon-group"></i> ';
979 icon += '<i class="icon-group"></i> ';
980 }
980 }
981 // user
981 // user
982 else if (searchType === 'user') {
982 else if (searchType === 'user') {
983 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
983 icon += '<img class="gravatar" src="{0}"/>'.format(data['icon_link']);
984 }
984 }
985 // pull request
985 // pull request
986 else if (searchType === 'pull_request') {
986 else if (searchType === 'pull_request') {
987 icon += '<i class="icon-merge"></i> ';
987 icon += '<i class="icon-merge"></i> ';
988 }
988 }
989 // commit
989 // commit
990 else if (searchType === 'commit') {
990 else if (searchType === 'commit') {
991 var repo_data = data['repo_data'];
991 var repo_data = data['repo_data'];
992 var repoIcon = getRepoIcon(repo_data['repository_type']);
992 var repoIcon = getRepoIcon(repo_data['repository_type']);
993 if (repoIcon) {
993 if (repoIcon) {
994 icon += repoIcon;
994 icon += repoIcon;
995 } else {
995 } else {
996 icon += '<i class="icon-tag"></i>';
996 icon += '<i class="icon-tag"></i>';
997 }
997 }
998 }
998 }
999 // file
999 // file
1000 else if (searchType === 'file') {
1000 else if (searchType === 'file') {
1001 var repo_data = data['repo_data'];
1001 var repo_data = data['repo_data'];
1002 var repoIcon = getRepoIcon(repo_data['repository_type']);
1002 var repoIcon = getRepoIcon(repo_data['repository_type']);
1003 if (repoIcon) {
1003 if (repoIcon) {
1004 icon += repoIcon;
1004 icon += repoIcon;
1005 } else {
1005 } else {
1006 icon += '<i class="icon-tag"></i>';
1006 icon += '<i class="icon-tag"></i>';
1007 }
1007 }
1008 }
1008 }
1009 // generic text
1009 // generic text
1010 else if (searchType === 'text') {
1010 else if (searchType === 'text') {
1011 icon = '';
1011 icon = '';
1012 }
1012 }
1013
1013
1014 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1014 var tmpl = '<div class="ac-container-wrap">{0}{1}</div>';
1015 return tmpl.format(icon, valueDisplay);
1015 return tmpl.format(icon, valueDisplay);
1016 };
1016 };
1017
1017
1018 var handleSelect = function(element, suggestion) {
1018 var handleSelect = function(element, suggestion) {
1019 if (suggestion.type === "hint") {
1019 if (suggestion.type === "hint") {
1020 // we skip action
1020 // we skip action
1021 $('#main_filter').focus();
1021 $('#main_filter').focus();
1022 }
1022 }
1023 else if (suggestion.type === "text") {
1023 else if (suggestion.type === "text") {
1024 // we skip action
1024 // we skip action
1025 $('#main_filter').focus();
1025 $('#main_filter').focus();
1026
1026
1027 } else {
1027 } else {
1028 window.location = suggestion['url'];
1028 window.location = suggestion['url'];
1029 }
1029 }
1030 };
1030 };
1031
1031
1032 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1032 var autocompleteMainFilterResult = function (suggestion, originalQuery, queryLowerCase) {
1033 if (queryLowerCase.split(':').length === 2) {
1033 if (queryLowerCase.split(':').length === 2) {
1034 queryLowerCase = queryLowerCase.split(':')[1]
1034 queryLowerCase = queryLowerCase.split(':')[1]
1035 }
1035 }
1036 if (suggestion.type === "text") {
1036 if (suggestion.type === "text") {
1037 // special case we don't want to "skip" display for
1037 // special case we don't want to "skip" display for
1038 return true
1038 return true
1039 }
1039 }
1040 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1040 return suggestion.value_display.toLowerCase().indexOf(queryLowerCase) !== -1;
1041 };
1041 };
1042
1042
1043 var cleanContext = {
1043 var cleanContext = {
1044 repo_view_type: null,
1044 repo_view_type: null,
1045
1045
1046 repo_id: null,
1046 repo_id: null,
1047 repo_name: "",
1047 repo_name: "",
1048
1048
1049 repo_group_id: null,
1049 repo_group_id: null,
1050 repo_group_name: null
1050 repo_group_name: null
1051 };
1051 };
1052 var removeGoToFilter = function () {
1052 var removeGoToFilter = function () {
1053 $('.searchTagHidable').hide();
1053 $('.searchTagHidable').hide();
1054 $('#main_filter').autocomplete(
1054 $('#main_filter').autocomplete(
1055 'setOptions', {params:{search_context: cleanContext}});
1055 'setOptions', {params:{search_context: cleanContext}});
1056 };
1056 };
1057
1057
1058 $('#main_filter').autocomplete({
1058 $('#main_filter').autocomplete({
1059 serviceUrl: pyroutes.url('goto_switcher_data'),
1059 serviceUrl: pyroutes.url('goto_switcher_data'),
1060 params: {
1060 params: {
1061 "search_context": templateContext.search_context
1061 "search_context": templateContext.search_context
1062 },
1062 },
1063 minChars:2,
1063 minChars:2,
1064 maxHeight:400,
1064 maxHeight:400,
1065 deferRequestBy: 300, //miliseconds
1065 deferRequestBy: 300, //miliseconds
1066 tabDisabled: true,
1066 tabDisabled: true,
1067 autoSelectFirst: false,
1067 autoSelectFirst: false,
1068 containerClass: 'autocomplete-qfilter-suggestions',
1068 containerClass: 'autocomplete-qfilter-suggestions',
1069 formatResult: autocompleteMainFilterFormatResult,
1069 formatResult: autocompleteMainFilterFormatResult,
1070 lookupFilter: autocompleteMainFilterResult,
1070 lookupFilter: autocompleteMainFilterResult,
1071 onSelect: function (element, suggestion) {
1071 onSelect: function (element, suggestion) {
1072 handleSelect(element, suggestion);
1072 handleSelect(element, suggestion);
1073 return false;
1073 return false;
1074 },
1074 },
1075 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1075 onSearchError: function (element, query, jqXHR, textStatus, errorThrown) {
1076 if (jqXHR !== 'abort') {
1076 if (jqXHR !== 'abort') {
1077 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1077 var message = formatErrorMessage(jqXHR, textStatus, errorThrown);
1078 SwalNoAnimation.fire({
1078 SwalNoAnimation.fire({
1079 icon: 'error',
1079 icon: 'error',
1080 title: _gettext('Error during search operation'),
1080 title: _gettext('Error during search operation'),
1081 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1081 html: '<span style="white-space: pre-line">{0}</span>'.format(message),
1082 }).then(function(result) {
1082 }).then(function(result) {
1083 window.location.reload();
1083 window.location.reload();
1084 })
1084 })
1085 }
1085 }
1086 },
1086 },
1087 onSearchStart: function (params) {
1087 onSearchStart: function (params) {
1088 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1088 $('.searchTag.searchTagIcon').html('<i class="icon-spin animate-spin"></i>')
1089 },
1089 },
1090 onSearchComplete: function (query, suggestions) {
1090 onSearchComplete: function (query, suggestions) {
1091 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1091 $('.searchTag.searchTagIcon').html('<i class="icon-search"></i>')
1092 },
1092 },
1093 });
1093 });
1094
1094
1095 showMainFilterBox = function () {
1095 showMainFilterBox = function () {
1096 $('#main_filter_help').toggle();
1096 $('#main_filter_help').toggle();
1097 };
1097 };
1098
1098
1099 $('#main_filter').on('keydown.autocomplete', function (e) {
1099 $('#main_filter').on('keydown.autocomplete', function (e) {
1100
1100
1101 var BACKSPACE = 8;
1101 var BACKSPACE = 8;
1102 var el = $(e.currentTarget);
1102 var el = $(e.currentTarget);
1103 if(e.which === BACKSPACE){
1103 if(e.which === BACKSPACE){
1104 var inputVal = el.val();
1104 var inputVal = el.val();
1105 if (inputVal === ""){
1105 if (inputVal === ""){
1106 removeGoToFilter()
1106 removeGoToFilter()
1107 }
1107 }
1108 }
1108 }
1109 });
1109 });
1110
1110
1111 var dismissNotice = function(noticeId) {
1111 var dismissNotice = function(noticeId) {
1112
1112
1113 var url = pyroutes.url('user_notice_dismiss',
1113 var url = pyroutes.url('user_notice_dismiss',
1114 {"user_id": templateContext.rhodecode_user.user_id});
1114 {"user_id": templateContext.rhodecode_user.user_id});
1115
1115
1116 var postData = {
1116 var postData = {
1117 'csrf_token': CSRF_TOKEN,
1117 'csrf_token': CSRF_TOKEN,
1118 'notice_id': noticeId,
1118 'notice_id': noticeId,
1119 };
1119 };
1120
1120
1121 var success = function(response) {
1121 var success = function(response) {
1122 $('#notice-message-' + noticeId).remove();
1122 $('#notice-message-' + noticeId).remove();
1123 return false;
1123 return false;
1124 };
1124 };
1125 var failure = function(data, textStatus, xhr) {
1125 var failure = function(data, textStatus, xhr) {
1126 alert("error processing request: " + textStatus);
1126 alert("error processing request: " + textStatus);
1127 return false;
1127 return false;
1128 };
1128 };
1129 ajaxPOST(url, postData, success, failure);
1129 ajaxPOST(url, postData, success, failure);
1130 }
1130 }
1131 </script>
1131 </script>
1132 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1132 <script src="${h.asset('js/rhodecode/base/keyboard-bindings.js', ver=c.rhodecode_version_hash)}"></script>
1133 </%def>
1133 </%def>
1134
1134
1135 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1135 <div class="modal" id="help_kb" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
1136 <div class="modal-dialog">
1136 <div class="modal-dialog">
1137 <div class="modal-content">
1137 <div class="modal-content">
1138 <div class="modal-header">
1138 <div class="modal-header">
1139 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1139 <button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
1140 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1140 <h4 class="modal-title" id="myModalLabel">${_('Keyboard shortcuts')}</h4>
1141 </div>
1141 </div>
1142 <div class="modal-body">
1142 <div class="modal-body">
1143 <div class="block-left">
1143 <div class="block-left">
1144 <table class="keyboard-mappings">
1144 <table class="keyboard-mappings">
1145 <tbody>
1145 <tbody>
1146 <tr>
1146 <tr>
1147 <th></th>
1147 <th></th>
1148 <th>${_('Site-wide shortcuts')}</th>
1148 <th>${_('Site-wide shortcuts')}</th>
1149 </tr>
1149 </tr>
1150 <%
1150 <%
1151 elems = [
1151 elems = [
1152 ('/', 'Use quick search box'),
1152 ('/', 'Use quick search box'),
1153 ('g h', 'Goto home page'),
1153 ('g h', 'Goto home page'),
1154 ('g g', 'Goto my private gists page'),
1154 ('g g', 'Goto my private gists page'),
1155 ('g G', 'Goto my public gists page'),
1155 ('g G', 'Goto my public gists page'),
1156 ('g 0-9', 'Goto bookmarked items from 0-9'),
1156 ('g 0-9', 'Goto bookmarked items from 0-9'),
1157 ('n r', 'New repository page'),
1157 ('n r', 'New repository page'),
1158 ('n g', 'New gist page'),
1158 ('n g', 'New gist page'),
1159 ]
1159 ]
1160 %>
1160 %>
1161 %for key, desc in elems:
1161 %for key, desc in elems:
1162 <tr>
1162 <tr>
1163 <td class="keys">
1163 <td class="keys">
1164 <span class="key tag">${key}</span>
1164 <span class="key tag">${key}</span>
1165 </td>
1165 </td>
1166 <td>${desc}</td>
1166 <td>${desc}</td>
1167 </tr>
1167 </tr>
1168 %endfor
1168 %endfor
1169 </tbody>
1169 </tbody>
1170 </table>
1170 </table>
1171 </div>
1171 </div>
1172 <div class="block-left">
1172 <div class="block-left">
1173 <table class="keyboard-mappings">
1173 <table class="keyboard-mappings">
1174 <tbody>
1174 <tbody>
1175 <tr>
1175 <tr>
1176 <th></th>
1176 <th></th>
1177 <th>${_('Repositories')}</th>
1177 <th>${_('Repositories')}</th>
1178 </tr>
1178 </tr>
1179 <%
1179 <%
1180 elems = [
1180 elems = [
1181 ('g s', 'Goto summary page'),
1181 ('g s', 'Goto summary page'),
1182 ('g c', 'Goto changelog page'),
1182 ('g c', 'Goto changelog page'),
1183 ('g f', 'Goto files page'),
1183 ('g f', 'Goto files page'),
1184 ('g F', 'Goto files page with file search activated'),
1184 ('g F', 'Goto files page with file search activated'),
1185 ('g p', 'Goto pull requests page'),
1185 ('g p', 'Goto pull requests page'),
1186 ('g o', 'Goto repository settings'),
1186 ('g o', 'Goto repository settings'),
1187 ('g O', 'Goto repository access permissions settings'),
1187 ('g O', 'Goto repository access permissions settings'),
1188 ]
1188 ]
1189 %>
1189 %>
1190 %for key, desc in elems:
1190 %for key, desc in elems:
1191 <tr>
1191 <tr>
1192 <td class="keys">
1192 <td class="keys">
1193 <span class="key tag">${key}</span>
1193 <span class="key tag">${key}</span>
1194 </td>
1194 </td>
1195 <td>${desc}</td>
1195 <td>${desc}</td>
1196 </tr>
1196 </tr>
1197 %endfor
1197 %endfor
1198 </tbody>
1198 </tbody>
1199 </table>
1199 </table>
1200 </div>
1200 </div>
1201 </div>
1201 </div>
1202 <div class="modal-footer">
1202 <div class="modal-footer">
1203 </div>
1203 </div>
1204 </div><!-- /.modal-content -->
1204 </div><!-- /.modal-content -->
1205 </div><!-- /.modal-dialog -->
1205 </div><!-- /.modal-dialog -->
1206 </div><!-- /.modal -->
1206 </div><!-- /.modal -->
@@ -1,632 +1,632 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2020 RhodeCode GmbH
3 # Copyright (C) 2010-2020 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import os
21 import os
22 from hashlib import sha1
22 from hashlib import sha1
23
23
24 import pytest
24 import pytest
25 from mock import patch
25 from mock import patch
26
26
27 from rhodecode.lib import auth
27 from rhodecode.lib import auth
28 from rhodecode.lib.utils2 import md5
28 from rhodecode.lib.utils2 import md5
29 from rhodecode.model.auth_token import AuthTokenModel
29 from rhodecode.model.auth_token import AuthTokenModel
30 from rhodecode.model.db import Session, User
30 from rhodecode.model.db import Session, User
31 from rhodecode.model.repo import RepoModel
31 from rhodecode.model.repo import RepoModel
32 from rhodecode.model.user import UserModel
32 from rhodecode.model.user import UserModel
33 from rhodecode.model.user_group import UserGroupModel
33 from rhodecode.model.user_group import UserGroupModel
34
34
35
35
36 def test_perm_origin_dict():
36 def test_perm_origin_dict():
37 pod = auth.PermOriginDict()
37 pod = auth.PermOriginDict()
38 pod['thing'] = 'read', 'default', 1
38 pod['thing'] = 'read', 'default', 1
39 assert pod['thing'] == 'read'
39 assert pod['thing'] == 'read'
40
40
41 assert pod.perm_origin_stack == {
41 assert pod.perm_origin_stack == {
42 'thing': [('read', 'default', 1)]}
42 'thing': [('read', 'default', 1)]}
43
43
44 pod['thing'] = 'write', 'admin', 1
44 pod['thing'] = 'write', 'admin', 1
45 assert pod['thing'] == 'write'
45 assert pod['thing'] == 'write'
46
46
47 assert pod.perm_origin_stack == {
47 assert pod.perm_origin_stack == {
48 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
48 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
49
49
50 pod['other'] = 'write', 'default', 8
50 pod['other'] = 'write', 'default', 8
51
51
52 assert pod.perm_origin_stack == {
52 assert pod.perm_origin_stack == {
53 'other': [('write', 'default', 8)],
53 'other': [('write', 'default', 8)],
54 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
54 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
55
55
56 pod['other'] = 'none', 'override', 8
56 pod['other'] = 'none', 'override', 8
57
57
58 assert pod.perm_origin_stack == {
58 assert pod.perm_origin_stack == {
59 'other': [('write', 'default', 8), ('none', 'override', 8)],
59 'other': [('write', 'default', 8), ('none', 'override', 8)],
60 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
60 'thing': [('read', 'default', 1), ('write', 'admin', 1)]}
61
61
62 with pytest.raises(ValueError):
62 with pytest.raises(ValueError):
63 pod['thing'] = 'read'
63 pod['thing'] = 'read'
64
64
65
65
66 def test_cached_perms_data(user_regular, backend_random):
66 def test_cached_perms_data(user_regular, backend_random):
67 permissions = get_permissions(user_regular)
67 permissions = get_permissions(user_regular)
68 repo_name = backend_random.repo.repo_name
68 repo_name = backend_random.repo.repo_name
69 expected_global_permissions = {
69 expected_global_permissions = {
70 'repository.read', 'group.read', 'usergroup.read'}
70 'repository.read', 'group.read', 'usergroup.read'}
71 assert expected_global_permissions.issubset(permissions['global'])
71 assert expected_global_permissions.issubset(permissions['global'])
72 assert permissions['repositories'][repo_name] == 'repository.read'
72 assert permissions['repositories'][repo_name] == 'repository.read'
73
73
74
74
75 def test_cached_perms_data_with_admin_user(user_regular, backend_random):
75 def test_cached_perms_data_with_admin_user(user_regular, backend_random):
76 permissions = get_permissions(user_regular, user_is_admin=True)
76 permissions = get_permissions(user_regular, user_is_admin=True)
77 repo_name = backend_random.repo.repo_name
77 repo_name = backend_random.repo.repo_name
78 assert 'hg.admin' in permissions['global']
78 assert 'hg.admin' in permissions['global']
79 assert permissions['repositories'][repo_name] == 'repository.admin'
79 assert permissions['repositories'][repo_name] == 'repository.admin'
80
80
81
81
82 def test_cached_perms_data_with_admin_user_extended_calculation(user_regular, backend_random):
82 def test_cached_perms_data_with_admin_user_extended_calculation(user_regular, backend_random):
83 permissions = get_permissions(user_regular, user_is_admin=True,
83 permissions = get_permissions(user_regular, user_is_admin=True,
84 calculate_super_admin=True)
84 calculate_super_admin=True)
85 repo_name = backend_random.repo.repo_name
85 repo_name = backend_random.repo.repo_name
86 assert 'hg.admin' in permissions['global']
86 assert 'hg.admin' in permissions['global']
87 assert permissions['repositories'][repo_name] == 'repository.admin'
87 assert permissions['repositories'][repo_name] == 'repository.admin'
88
88
89
89
90 def test_cached_perms_data_user_group_global_permissions(user_util):
90 def test_cached_perms_data_user_group_global_permissions(user_util):
91 user, user_group = user_util.create_user_with_group()
91 user, user_group = user_util.create_user_with_group()
92 user_group.inherit_default_permissions = False
92 user_group.inherit_default_permissions = False
93
93
94 granted_permission = 'repository.write'
94 granted_permission = 'repository.write'
95 UserGroupModel().grant_perm(user_group, granted_permission)
95 UserGroupModel().grant_perm(user_group, granted_permission)
96 Session().commit()
96 Session().commit()
97
97
98 permissions = get_permissions(user)
98 permissions = get_permissions(user)
99 assert granted_permission in permissions['global']
99 assert granted_permission in permissions['global']
100
100
101
101
102 @pytest.mark.xfail(reason="Not implemented, see TODO note")
102 @pytest.mark.xfail(reason="Not implemented, see TODO note")
103 def test_cached_perms_data_user_group_global_permissions_(user_util):
103 def test_cached_perms_data_user_group_global_permissions_(user_util):
104 user, user_group = user_util.create_user_with_group()
104 user, user_group = user_util.create_user_with_group()
105
105
106 granted_permission = 'repository.write'
106 granted_permission = 'repository.write'
107 UserGroupModel().grant_perm(user_group, granted_permission)
107 UserGroupModel().grant_perm(user_group, granted_permission)
108 Session().commit()
108 Session().commit()
109
109
110 permissions = get_permissions(user)
110 permissions = get_permissions(user)
111 assert granted_permission in permissions['global']
111 assert granted_permission in permissions['global']
112
112
113
113
114 def test_cached_perms_data_user_global_permissions(user_util):
114 def test_cached_perms_data_user_global_permissions(user_util):
115 user = user_util.create_user()
115 user = user_util.create_user()
116 UserModel().grant_perm(user, 'repository.none')
116 UserModel().grant_perm(user, 'repository.none')
117 Session().commit()
117 Session().commit()
118
118
119 permissions = get_permissions(user, user_inherit_default_permissions=True)
119 permissions = get_permissions(user, user_inherit_default_permissions=True)
120 assert 'repository.read' in permissions['global']
120 assert 'repository.read' in permissions['global']
121
121
122
122
123 def test_cached_perms_data_repository_permissions_on_private_repository(
123 def test_cached_perms_data_repository_permissions_on_private_repository(
124 backend_random, user_util):
124 backend_random, user_util):
125 user, user_group = user_util.create_user_with_group()
125 user, user_group = user_util.create_user_with_group()
126
126
127 repo = backend_random.create_repo()
127 repo = backend_random.create_repo()
128 repo.private = True
128 repo.private = True
129
129
130 granted_permission = 'repository.write'
130 granted_permission = 'repository.write'
131 RepoModel().grant_user_group_permission(
131 RepoModel().grant_user_group_permission(
132 repo, user_group.users_group_name, granted_permission)
132 repo, user_group.users_group_name, granted_permission)
133 Session().commit()
133 Session().commit()
134
134
135 permissions = get_permissions(user)
135 permissions = get_permissions(user)
136 assert permissions['repositories'][repo.repo_name] == granted_permission
136 assert permissions['repositories'][repo.repo_name] == granted_permission
137
137
138
138
139 def test_cached_perms_data_repository_permissions_for_owner(
139 def test_cached_perms_data_repository_permissions_for_owner(
140 backend_random, user_util):
140 backend_random, user_util):
141 user = user_util.create_user()
141 user = user_util.create_user()
142
142
143 repo = backend_random.create_repo()
143 repo = backend_random.create_repo()
144 repo.user_id = user.user_id
144 repo.user_id = user.user_id
145
145
146 permissions = get_permissions(user)
146 permissions = get_permissions(user)
147 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
147 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
148
148
149 # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack
149 # TODO: johbo: Make cleanup in UserUtility smarter, then remove this hack
150 repo.user_id = User.get_default_user_id()
150 repo.user_id = User.get_default_user_id()
151
151
152
152
153 def test_cached_perms_data_repository_permissions_not_inheriting_defaults(
153 def test_cached_perms_data_repository_permissions_not_inheriting_defaults(
154 backend_random, user_util):
154 backend_random, user_util):
155 user = user_util.create_user()
155 user = user_util.create_user()
156 repo = backend_random.create_repo()
156 repo = backend_random.create_repo()
157
157
158 # Don't inherit default object permissions
158 # Don't inherit default object permissions
159 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
159 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
160 Session().commit()
160 Session().commit()
161
161
162 permissions = get_permissions(user)
162 permissions = get_permissions(user)
163 assert permissions['repositories'][repo.repo_name] == 'repository.none'
163 assert permissions['repositories'][repo.repo_name] == 'repository.none'
164
164
165
165
166 def test_cached_perms_data_default_permissions_on_repository_group(user_util):
166 def test_cached_perms_data_default_permissions_on_repository_group(user_util):
167 # Have a repository group with default permissions set
167 # Have a repository group with default permissions set
168 repo_group = user_util.create_repo_group()
168 repo_group = user_util.create_repo_group()
169 default_user = User.get_default_user()
169 default_user = User.get_default_user()
170 user_util.grant_user_permission_to_repo_group(
170 user_util.grant_user_permission_to_repo_group(
171 repo_group, default_user, 'repository.write')
171 repo_group, default_user, 'repository.write')
172 user = user_util.create_user()
172 user = user_util.create_user()
173
173
174 permissions = get_permissions(user)
174 permissions = get_permissions(user)
175 assert permissions['repositories_groups'][repo_group.group_name] == \
175 assert permissions['repositories_groups'][repo_group.group_name] == \
176 'repository.write'
176 'repository.write'
177
177
178
178
179 def test_cached_perms_data_default_permissions_on_repository_group_owner(
179 def test_cached_perms_data_default_permissions_on_repository_group_owner(
180 user_util):
180 user_util):
181 # Have a repository group
181 # Have a repository group
182 repo_group = user_util.create_repo_group()
182 repo_group = user_util.create_repo_group()
183 default_user = User.get_default_user()
183 default_user = User.get_default_user()
184
184
185 # Add a permission for the default user to hit the code path
185 # Add a permission for the default user to hit the code path
186 user_util.grant_user_permission_to_repo_group(
186 user_util.grant_user_permission_to_repo_group(
187 repo_group, default_user, 'repository.write')
187 repo_group, default_user, 'repository.write')
188
188
189 # Have an owner of the group
189 # Have an owner of the group
190 user = user_util.create_user()
190 user = user_util.create_user()
191 repo_group.user_id = user.user_id
191 repo_group.user_id = user.user_id
192
192
193 permissions = get_permissions(user)
193 permissions = get_permissions(user)
194 assert permissions['repositories_groups'][repo_group.group_name] == \
194 assert permissions['repositories_groups'][repo_group.group_name] == \
195 'group.admin'
195 'group.admin'
196
196
197
197
198 def test_cached_perms_data_default_permissions_on_repository_group_no_inherit(
198 def test_cached_perms_data_default_permissions_on_repository_group_no_inherit(
199 user_util):
199 user_util):
200 # Have a repository group
200 # Have a repository group
201 repo_group = user_util.create_repo_group()
201 repo_group = user_util.create_repo_group()
202 default_user = User.get_default_user()
202 default_user = User.get_default_user()
203
203
204 # Add a permission for the default user to hit the code path
204 # Add a permission for the default user to hit the code path
205 user_util.grant_user_permission_to_repo_group(
205 user_util.grant_user_permission_to_repo_group(
206 repo_group, default_user, 'repository.write')
206 repo_group, default_user, 'repository.write')
207
207
208 # Don't inherit default object permissions
208 # Don't inherit default object permissions
209 user = user_util.create_user()
209 user = user_util.create_user()
210 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
210 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
211 Session().commit()
211 Session().commit()
212
212
213 permissions = get_permissions(user)
213 permissions = get_permissions(user)
214 assert permissions['repositories_groups'][repo_group.group_name] == \
214 assert permissions['repositories_groups'][repo_group.group_name] == \
215 'group.none'
215 'group.none'
216
216
217
217
218 def test_cached_perms_data_repository_permissions_from_user_group(
218 def test_cached_perms_data_repository_permissions_from_user_group(
219 user_util, backend_random):
219 user_util, backend_random):
220 user, user_group = user_util.create_user_with_group()
220 user, user_group = user_util.create_user_with_group()
221
221
222 # Needs a second user group to make sure that we select the right
222 # Needs a second user group to make sure that we select the right
223 # permissions.
223 # permissions.
224 user_group2 = user_util.create_user_group()
224 user_group2 = user_util.create_user_group()
225 UserGroupModel().add_user_to_group(user_group2, user)
225 UserGroupModel().add_user_to_group(user_group2, user)
226
226
227 repo = backend_random.create_repo()
227 repo = backend_random.create_repo()
228
228
229 RepoModel().grant_user_group_permission(
229 RepoModel().grant_user_group_permission(
230 repo, user_group.users_group_name, 'repository.read')
230 repo, user_group.users_group_name, 'repository.read')
231 RepoModel().grant_user_group_permission(
231 RepoModel().grant_user_group_permission(
232 repo, user_group2.users_group_name, 'repository.write')
232 repo, user_group2.users_group_name, 'repository.write')
233 Session().commit()
233 Session().commit()
234
234
235 permissions = get_permissions(user)
235 permissions = get_permissions(user)
236 assert permissions['repositories'][repo.repo_name] == 'repository.write'
236 assert permissions['repositories'][repo.repo_name] == 'repository.write'
237
237
238
238
239 def test_cached_perms_data_repository_permissions_from_user_group_owner(
239 def test_cached_perms_data_repository_permissions_from_user_group_owner(
240 user_util, backend_random):
240 user_util, backend_random):
241 user, user_group = user_util.create_user_with_group()
241 user, user_group = user_util.create_user_with_group()
242
242
243 repo = backend_random.create_repo()
243 repo = backend_random.create_repo()
244 repo.user_id = user.user_id
244 repo.user_id = user.user_id
245
245
246 RepoModel().grant_user_group_permission(
246 RepoModel().grant_user_group_permission(
247 repo, user_group.users_group_name, 'repository.write')
247 repo, user_group.users_group_name, 'repository.write')
248 Session().commit()
248 Session().commit()
249
249
250 permissions = get_permissions(user)
250 permissions = get_permissions(user)
251 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
251 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
252
252
253
253
254 def test_cached_perms_data_user_repository_permissions(
254 def test_cached_perms_data_user_repository_permissions(
255 user_util, backend_random):
255 user_util, backend_random):
256 user = user_util.create_user()
256 user = user_util.create_user()
257 repo = backend_random.create_repo()
257 repo = backend_random.create_repo()
258 granted_permission = 'repository.write'
258 granted_permission = 'repository.write'
259 RepoModel().grant_user_permission(repo, user, granted_permission)
259 RepoModel().grant_user_permission(repo, user, granted_permission)
260 Session().commit()
260 Session().commit()
261
261
262 permissions = get_permissions(user)
262 permissions = get_permissions(user)
263 assert permissions['repositories'][repo.repo_name] == granted_permission
263 assert permissions['repositories'][repo.repo_name] == granted_permission
264
264
265
265
266 def test_cached_perms_data_user_repository_permissions_explicit(
266 def test_cached_perms_data_user_repository_permissions_explicit(
267 user_util, backend_random):
267 user_util, backend_random):
268 user = user_util.create_user()
268 user = user_util.create_user()
269 repo = backend_random.create_repo()
269 repo = backend_random.create_repo()
270 granted_permission = 'repository.none'
270 granted_permission = 'repository.none'
271 RepoModel().grant_user_permission(repo, user, granted_permission)
271 RepoModel().grant_user_permission(repo, user, granted_permission)
272 Session().commit()
272 Session().commit()
273
273
274 permissions = get_permissions(user, explicit=True)
274 permissions = get_permissions(user, explicit=True)
275 assert permissions['repositories'][repo.repo_name] == granted_permission
275 assert permissions['repositories'][repo.repo_name] == granted_permission
276
276
277
277
278 def test_cached_perms_data_user_repository_permissions_owner(
278 def test_cached_perms_data_user_repository_permissions_owner(
279 user_util, backend_random):
279 user_util, backend_random):
280 user = user_util.create_user()
280 user = user_util.create_user()
281 repo = backend_random.create_repo()
281 repo = backend_random.create_repo()
282 repo.user_id = user.user_id
282 repo.user_id = user.user_id
283 RepoModel().grant_user_permission(repo, user, 'repository.write')
283 RepoModel().grant_user_permission(repo, user, 'repository.write')
284 Session().commit()
284 Session().commit()
285
285
286 permissions = get_permissions(user)
286 permissions = get_permissions(user)
287 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
287 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
288
288
289
289
290 def test_cached_perms_data_repository_groups_permissions_inherited(
290 def test_cached_perms_data_repository_groups_permissions_inherited(
291 user_util, backend_random):
291 user_util, backend_random):
292 user, user_group = user_util.create_user_with_group()
292 user, user_group = user_util.create_user_with_group()
293
293
294 # Needs a second group to hit the last condition
294 # Needs a second group to hit the last condition
295 user_group2 = user_util.create_user_group()
295 user_group2 = user_util.create_user_group()
296 UserGroupModel().add_user_to_group(user_group2, user)
296 UserGroupModel().add_user_to_group(user_group2, user)
297
297
298 repo_group = user_util.create_repo_group()
298 repo_group = user_util.create_repo_group()
299
299
300 user_util.grant_user_group_permission_to_repo_group(
300 user_util.grant_user_group_permission_to_repo_group(
301 repo_group, user_group, 'group.read')
301 repo_group, user_group, 'group.read')
302 user_util.grant_user_group_permission_to_repo_group(
302 user_util.grant_user_group_permission_to_repo_group(
303 repo_group, user_group2, 'group.write')
303 repo_group, user_group2, 'group.write')
304
304
305 permissions = get_permissions(user)
305 permissions = get_permissions(user)
306 assert permissions['repositories_groups'][repo_group.group_name] == \
306 assert permissions['repositories_groups'][repo_group.group_name] == \
307 'group.write'
307 'group.write'
308
308
309
309
310 def test_cached_perms_data_repository_groups_permissions_inherited_owner(
310 def test_cached_perms_data_repository_groups_permissions_inherited_owner(
311 user_util, backend_random):
311 user_util, backend_random):
312 user, user_group = user_util.create_user_with_group()
312 user, user_group = user_util.create_user_with_group()
313 repo_group = user_util.create_repo_group()
313 repo_group = user_util.create_repo_group()
314 repo_group.user_id = user.user_id
314 repo_group.user_id = user.user_id
315
315
316 granted_permission = 'group.write'
316 granted_permission = 'group.write'
317 user_util.grant_user_group_permission_to_repo_group(
317 user_util.grant_user_group_permission_to_repo_group(
318 repo_group, user_group, granted_permission)
318 repo_group, user_group, granted_permission)
319
319
320 permissions = get_permissions(user)
320 permissions = get_permissions(user)
321 assert permissions['repositories_groups'][repo_group.group_name] == \
321 assert permissions['repositories_groups'][repo_group.group_name] == \
322 'group.admin'
322 'group.admin'
323
323
324
324
325 def test_cached_perms_data_repository_groups_permissions(
325 def test_cached_perms_data_repository_groups_permissions(
326 user_util, backend_random):
326 user_util, backend_random):
327 user = user_util.create_user()
327 user = user_util.create_user()
328
328
329 repo_group = user_util.create_repo_group()
329 repo_group = user_util.create_repo_group()
330
330
331 granted_permission = 'group.write'
331 granted_permission = 'group.write'
332 user_util.grant_user_permission_to_repo_group(
332 user_util.grant_user_permission_to_repo_group(
333 repo_group, user, granted_permission)
333 repo_group, user, granted_permission)
334
334
335 permissions = get_permissions(user)
335 permissions = get_permissions(user)
336 assert permissions['repositories_groups'][repo_group.group_name] == \
336 assert permissions['repositories_groups'][repo_group.group_name] == \
337 'group.write'
337 'group.write'
338
338
339
339
340 def test_cached_perms_data_repository_groups_permissions_explicit(
340 def test_cached_perms_data_repository_groups_permissions_explicit(
341 user_util, backend_random):
341 user_util, backend_random):
342 user = user_util.create_user()
342 user = user_util.create_user()
343
343
344 repo_group = user_util.create_repo_group()
344 repo_group = user_util.create_repo_group()
345
345
346 granted_permission = 'group.none'
346 granted_permission = 'group.none'
347 user_util.grant_user_permission_to_repo_group(
347 user_util.grant_user_permission_to_repo_group(
348 repo_group, user, granted_permission)
348 repo_group, user, granted_permission)
349
349
350 permissions = get_permissions(user, explicit=True)
350 permissions = get_permissions(user, explicit=True)
351 assert permissions['repositories_groups'][repo_group.group_name] == \
351 assert permissions['repositories_groups'][repo_group.group_name] == \
352 'group.none'
352 'group.none'
353
353
354
354
355 def test_cached_perms_data_repository_groups_permissions_owner(
355 def test_cached_perms_data_repository_groups_permissions_owner(
356 user_util, backend_random):
356 user_util, backend_random):
357 user = user_util.create_user()
357 user = user_util.create_user()
358
358
359 repo_group = user_util.create_repo_group()
359 repo_group = user_util.create_repo_group()
360 repo_group.user_id = user.user_id
360 repo_group.user_id = user.user_id
361
361
362 granted_permission = 'group.write'
362 granted_permission = 'group.write'
363 user_util.grant_user_permission_to_repo_group(
363 user_util.grant_user_permission_to_repo_group(
364 repo_group, user, granted_permission)
364 repo_group, user, granted_permission)
365
365
366 permissions = get_permissions(user)
366 permissions = get_permissions(user)
367 assert permissions['repositories_groups'][repo_group.group_name] == \
367 assert permissions['repositories_groups'][repo_group.group_name] == \
368 'group.admin'
368 'group.admin'
369
369
370
370
371 def test_cached_perms_data_user_group_permissions_inherited(
371 def test_cached_perms_data_user_group_permissions_inherited(
372 user_util, backend_random):
372 user_util, backend_random):
373 user, user_group = user_util.create_user_with_group()
373 user, user_group = user_util.create_user_with_group()
374 user_group2 = user_util.create_user_group()
374 user_group2 = user_util.create_user_group()
375 UserGroupModel().add_user_to_group(user_group2, user)
375 UserGroupModel().add_user_to_group(user_group2, user)
376
376
377 target_user_group = user_util.create_user_group()
377 target_user_group = user_util.create_user_group()
378
378
379 user_util.grant_user_group_permission_to_user_group(
379 user_util.grant_user_group_permission_to_user_group(
380 target_user_group, user_group, 'usergroup.read')
380 target_user_group, user_group, 'usergroup.read')
381 user_util.grant_user_group_permission_to_user_group(
381 user_util.grant_user_group_permission_to_user_group(
382 target_user_group, user_group2, 'usergroup.write')
382 target_user_group, user_group2, 'usergroup.write')
383
383
384 permissions = get_permissions(user)
384 permissions = get_permissions(user)
385 assert permissions['user_groups'][target_user_group.users_group_name] == \
385 assert permissions['user_groups'][target_user_group.users_group_name] == \
386 'usergroup.write'
386 'usergroup.write'
387
387
388
388
389 def test_cached_perms_data_user_group_permissions(
389 def test_cached_perms_data_user_group_permissions(
390 user_util, backend_random):
390 user_util, backend_random):
391 user = user_util.create_user()
391 user = user_util.create_user()
392 user_group = user_util.create_user_group()
392 user_group = user_util.create_user_group()
393 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.write')
393 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.write')
394 Session().commit()
394 Session().commit()
395
395
396 permissions = get_permissions(user)
396 permissions = get_permissions(user)
397 assert permissions['user_groups'][user_group.users_group_name] == \
397 assert permissions['user_groups'][user_group.users_group_name] == \
398 'usergroup.write'
398 'usergroup.write'
399
399
400
400
401 def test_cached_perms_data_user_group_permissions_explicit(
401 def test_cached_perms_data_user_group_permissions_explicit(
402 user_util, backend_random):
402 user_util, backend_random):
403 user = user_util.create_user()
403 user = user_util.create_user()
404 user_group = user_util.create_user_group()
404 user_group = user_util.create_user_group()
405 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.none')
405 UserGroupModel().grant_user_permission(user_group, user, 'usergroup.none')
406 Session().commit()
406 Session().commit()
407
407
408 permissions = get_permissions(user, explicit=True)
408 permissions = get_permissions(user, explicit=True)
409 assert permissions['user_groups'][user_group.users_group_name] == \
409 assert permissions['user_groups'][user_group.users_group_name] == \
410 'usergroup.none'
410 'usergroup.none'
411
411
412
412
413 def test_cached_perms_data_user_group_permissions_not_inheriting_defaults(
413 def test_cached_perms_data_user_group_permissions_not_inheriting_defaults(
414 user_util, backend_random):
414 user_util, backend_random):
415 user = user_util.create_user()
415 user = user_util.create_user()
416 user_group = user_util.create_user_group()
416 user_group = user_util.create_user_group()
417
417
418 # Don't inherit default object permissions
418 # Don't inherit default object permissions
419 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
419 UserModel().grant_perm(user, 'hg.inherit_default_perms.false')
420 Session().commit()
420 Session().commit()
421
421
422 permissions = get_permissions(user)
422 permissions = get_permissions(user)
423 assert permissions['user_groups'][user_group.users_group_name] == \
423 assert permissions['user_groups'][user_group.users_group_name] == \
424 'usergroup.none'
424 'usergroup.none'
425
425
426
426
427 def test_permission_calculator_admin_permissions(
427 def test_permission_calculator_admin_permissions(
428 user_util, backend_random):
428 user_util, backend_random):
429 user = user_util.create_user()
429 user = user_util.create_user()
430 user_group = user_util.create_user_group()
430 user_group = user_util.create_user_group()
431 repo = backend_random.repo
431 repo = backend_random.repo
432 repo_group = user_util.create_repo_group()
432 repo_group = user_util.create_repo_group()
433
433
434 calculator = auth.PermissionCalculator(
434 calculator = auth.PermissionCalculator(
435 user.user_id, {}, False, False, True, 'higherwin')
435 user.user_id, {}, False, False, True, 'higherwin')
436 permissions = calculator._calculate_admin_permissions()
436 permissions = calculator._calculate_super_admin_permissions()
437
437
438 assert permissions['repositories_groups'][repo_group.group_name] == \
438 assert permissions['repositories_groups'][repo_group.group_name] == \
439 'group.admin'
439 'group.admin'
440 assert permissions['user_groups'][user_group.users_group_name] == \
440 assert permissions['user_groups'][user_group.users_group_name] == \
441 'usergroup.admin'
441 'usergroup.admin'
442 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
442 assert permissions['repositories'][repo.repo_name] == 'repository.admin'
443 assert 'hg.admin' in permissions['global']
443 assert 'hg.admin' in permissions['global']
444
444
445
445
446 def test_permission_calculator_repository_permissions_robustness_from_group(
446 def test_permission_calculator_repository_permissions_robustness_from_group(
447 user_util, backend_random):
447 user_util, backend_random):
448 user, user_group = user_util.create_user_with_group()
448 user, user_group = user_util.create_user_with_group()
449
449
450 RepoModel().grant_user_group_permission(
450 RepoModel().grant_user_group_permission(
451 backend_random.repo, user_group.users_group_name, 'repository.write')
451 backend_random.repo, user_group.users_group_name, 'repository.write')
452
452
453 calculator = auth.PermissionCalculator(
453 calculator = auth.PermissionCalculator(
454 user.user_id, {}, False, False, False, 'higherwin')
454 user.user_id, {}, False, False, False, 'higherwin')
455 calculator._calculate_repository_permissions()
455 calculator._calculate_repository_permissions()
456
456
457
457
458 def test_permission_calculator_repository_permissions_robustness_from_user(
458 def test_permission_calculator_repository_permissions_robustness_from_user(
459 user_util, backend_random):
459 user_util, backend_random):
460 user = user_util.create_user()
460 user = user_util.create_user()
461
461
462 RepoModel().grant_user_permission(
462 RepoModel().grant_user_permission(
463 backend_random.repo, user, 'repository.write')
463 backend_random.repo, user, 'repository.write')
464 Session().commit()
464 Session().commit()
465
465
466 calculator = auth.PermissionCalculator(
466 calculator = auth.PermissionCalculator(
467 user.user_id, {}, False, False, False, 'higherwin')
467 user.user_id, {}, False, False, False, 'higherwin')
468 calculator._calculate_repository_permissions()
468 calculator._calculate_repository_permissions()
469
469
470
470
471 def test_permission_calculator_repo_group_permissions_robustness_from_group(
471 def test_permission_calculator_repo_group_permissions_robustness_from_group(
472 user_util, backend_random):
472 user_util, backend_random):
473 user, user_group = user_util.create_user_with_group()
473 user, user_group = user_util.create_user_with_group()
474 repo_group = user_util.create_repo_group()
474 repo_group = user_util.create_repo_group()
475
475
476 user_util.grant_user_group_permission_to_repo_group(
476 user_util.grant_user_group_permission_to_repo_group(
477 repo_group, user_group, 'group.write')
477 repo_group, user_group, 'group.write')
478
478
479 calculator = auth.PermissionCalculator(
479 calculator = auth.PermissionCalculator(
480 user.user_id, {}, False, False, False, 'higherwin')
480 user.user_id, {}, False, False, False, 'higherwin')
481 calculator._calculate_repository_group_permissions()
481 calculator._calculate_repository_group_permissions()
482
482
483
483
484 def test_permission_calculator_repo_group_permissions_robustness_from_user(
484 def test_permission_calculator_repo_group_permissions_robustness_from_user(
485 user_util, backend_random):
485 user_util, backend_random):
486 user = user_util.create_user()
486 user = user_util.create_user()
487 repo_group = user_util.create_repo_group()
487 repo_group = user_util.create_repo_group()
488
488
489 user_util.grant_user_permission_to_repo_group(
489 user_util.grant_user_permission_to_repo_group(
490 repo_group, user, 'group.write')
490 repo_group, user, 'group.write')
491
491
492 calculator = auth.PermissionCalculator(
492 calculator = auth.PermissionCalculator(
493 user.user_id, {}, False, False, False, 'higherwin')
493 user.user_id, {}, False, False, False, 'higherwin')
494 calculator._calculate_repository_group_permissions()
494 calculator._calculate_repository_group_permissions()
495
495
496
496
497 def test_permission_calculator_user_group_permissions_robustness_from_group(
497 def test_permission_calculator_user_group_permissions_robustness_from_group(
498 user_util, backend_random):
498 user_util, backend_random):
499 user, user_group = user_util.create_user_with_group()
499 user, user_group = user_util.create_user_with_group()
500 target_user_group = user_util.create_user_group()
500 target_user_group = user_util.create_user_group()
501
501
502 user_util.grant_user_group_permission_to_user_group(
502 user_util.grant_user_group_permission_to_user_group(
503 target_user_group, user_group, 'usergroup.write')
503 target_user_group, user_group, 'usergroup.write')
504
504
505 calculator = auth.PermissionCalculator(
505 calculator = auth.PermissionCalculator(
506 user.user_id, {}, False, False, False, 'higherwin')
506 user.user_id, {}, False, False, False, 'higherwin')
507 calculator._calculate_user_group_permissions()
507 calculator._calculate_user_group_permissions()
508
508
509
509
510 def test_permission_calculator_user_group_permissions_robustness_from_user(
510 def test_permission_calculator_user_group_permissions_robustness_from_user(
511 user_util, backend_random):
511 user_util, backend_random):
512 user = user_util.create_user()
512 user = user_util.create_user()
513 target_user_group = user_util.create_user_group()
513 target_user_group = user_util.create_user_group()
514
514
515 user_util.grant_user_permission_to_user_group(
515 user_util.grant_user_permission_to_user_group(
516 target_user_group, user, 'usergroup.write')
516 target_user_group, user, 'usergroup.write')
517
517
518 calculator = auth.PermissionCalculator(
518 calculator = auth.PermissionCalculator(
519 user.user_id, {}, False, False, False, 'higherwin')
519 user.user_id, {}, False, False, False, 'higherwin')
520 calculator._calculate_user_group_permissions()
520 calculator._calculate_user_group_permissions()
521
521
522
522
523 @pytest.mark.parametrize("algo, new_permission, old_permission, expected", [
523 @pytest.mark.parametrize("algo, new_permission, old_permission, expected", [
524 ('higherwin', 'repository.none', 'repository.none', 'repository.none'),
524 ('higherwin', 'repository.none', 'repository.none', 'repository.none'),
525 ('higherwin', 'repository.read', 'repository.none', 'repository.read'),
525 ('higherwin', 'repository.read', 'repository.none', 'repository.read'),
526 ('lowerwin', 'repository.write', 'repository.write', 'repository.write'),
526 ('lowerwin', 'repository.write', 'repository.write', 'repository.write'),
527 ('lowerwin', 'repository.read', 'repository.write', 'repository.read'),
527 ('lowerwin', 'repository.read', 'repository.write', 'repository.read'),
528 ])
528 ])
529 def test_permission_calculator_choose_permission(
529 def test_permission_calculator_choose_permission(
530 user_regular, algo, new_permission, old_permission, expected):
530 user_regular, algo, new_permission, old_permission, expected):
531 calculator = auth.PermissionCalculator(
531 calculator = auth.PermissionCalculator(
532 user_regular.user_id, {}, False, False, False, algo)
532 user_regular.user_id, {}, False, False, False, algo)
533 result = calculator._choose_permission(new_permission, old_permission)
533 result = calculator._choose_permission(new_permission, old_permission)
534 assert result == expected
534 assert result == expected
535
535
536
536
537 def test_permission_calculator_choose_permission_raises_on_wrong_algo(
537 def test_permission_calculator_choose_permission_raises_on_wrong_algo(
538 user_regular):
538 user_regular):
539 calculator = auth.PermissionCalculator(
539 calculator = auth.PermissionCalculator(
540 user_regular.user_id, {}, False, False, False, 'invalid')
540 user_regular.user_id, {}, False, False, False, 'invalid')
541 result = calculator._choose_permission(
541 result = calculator._choose_permission(
542 'repository.read', 'repository.read')
542 'repository.read', 'repository.read')
543 # TODO: johbo: This documents the existing behavior. Think of an
543 # TODO: johbo: This documents the existing behavior. Think of an
544 # improvement.
544 # improvement.
545 assert result is None
545 assert result is None
546
546
547
547
548 def test_auth_user_get_cookie_store_for_normal_user(user_util):
548 def test_auth_user_get_cookie_store_for_normal_user(user_util):
549 user = user_util.create_user()
549 user = user_util.create_user()
550 auth_user = auth.AuthUser(user_id=user.user_id)
550 auth_user = auth.AuthUser(user_id=user.user_id)
551 expected_data = {
551 expected_data = {
552 'username': user.username,
552 'username': user.username,
553 'user_id': user.user_id,
553 'user_id': user.user_id,
554 'password': md5(user.password),
554 'password': md5(user.password),
555 'is_authenticated': False
555 'is_authenticated': False
556 }
556 }
557 assert auth_user.get_cookie_store() == expected_data
557 assert auth_user.get_cookie_store() == expected_data
558
558
559
559
560 def test_auth_user_get_cookie_store_for_default_user():
560 def test_auth_user_get_cookie_store_for_default_user():
561 default_user = User.get_default_user()
561 default_user = User.get_default_user()
562 auth_user = auth.AuthUser()
562 auth_user = auth.AuthUser()
563 expected_data = {
563 expected_data = {
564 'username': User.DEFAULT_USER,
564 'username': User.DEFAULT_USER,
565 'user_id': default_user.user_id,
565 'user_id': default_user.user_id,
566 'password': md5(default_user.password),
566 'password': md5(default_user.password),
567 'is_authenticated': True
567 'is_authenticated': True
568 }
568 }
569 assert auth_user.get_cookie_store() == expected_data
569 assert auth_user.get_cookie_store() == expected_data
570
570
571
571
572 def get_permissions(user, **kwargs):
572 def get_permissions(user, **kwargs):
573 """
573 """
574 Utility filling in useful defaults into the call to `_cached_perms_data`.
574 Utility filling in useful defaults into the call to `_cached_perms_data`.
575
575
576 Fill in `**kwargs` if specific values are needed for a test.
576 Fill in `**kwargs` if specific values are needed for a test.
577 """
577 """
578 call_args = {
578 call_args = {
579 'user_id': user.user_id,
579 'user_id': user.user_id,
580 'scope': {},
580 'scope': {},
581 'user_is_admin': False,
581 'user_is_admin': False,
582 'user_inherit_default_permissions': False,
582 'user_inherit_default_permissions': False,
583 'explicit': False,
583 'explicit': False,
584 'algo': 'higherwin',
584 'algo': 'higherwin',
585 'calculate_super_admin': False,
585 'calculate_super_admin': False,
586 }
586 }
587 call_args.update(kwargs)
587 call_args.update(kwargs)
588 permissions = auth._cached_perms_data(**call_args)
588 permissions = auth._cached_perms_data(**call_args)
589 return permissions
589 return permissions
590
590
591
591
592 class TestGenerateAuthToken(object):
592 class TestGenerateAuthToken(object):
593 def test_salt_is_used_when_specified(self):
593 def test_salt_is_used_when_specified(self):
594 salt = 'abcde'
594 salt = 'abcde'
595 user_name = 'test_user'
595 user_name = 'test_user'
596 result = auth.generate_auth_token(user_name, salt)
596 result = auth.generate_auth_token(user_name, salt)
597 expected_result = sha1(user_name + salt).hexdigest()
597 expected_result = sha1(user_name + salt).hexdigest()
598 assert result == expected_result
598 assert result == expected_result
599
599
600 def test_salt_is_geneated_when_not_specified(self):
600 def test_salt_is_geneated_when_not_specified(self):
601 user_name = 'test_user'
601 user_name = 'test_user'
602 random_salt = os.urandom(16)
602 random_salt = os.urandom(16)
603 with patch.object(auth, 'os') as os_mock:
603 with patch.object(auth, 'os') as os_mock:
604 os_mock.urandom.return_value = random_salt
604 os_mock.urandom.return_value = random_salt
605 result = auth.generate_auth_token(user_name)
605 result = auth.generate_auth_token(user_name)
606 expected_result = sha1(user_name + random_salt).hexdigest()
606 expected_result = sha1(user_name + random_salt).hexdigest()
607 assert result == expected_result
607 assert result == expected_result
608
608
609
609
610 @pytest.mark.parametrize("test_token, test_roles, auth_result, expected_tokens", [
610 @pytest.mark.parametrize("test_token, test_roles, auth_result, expected_tokens", [
611 ('', None, False,
611 ('', None, False,
612 []),
612 []),
613 ('wrongtoken', None, False,
613 ('wrongtoken', None, False,
614 []),
614 []),
615 ('abracadabra_vcs', [AuthTokenModel.cls.ROLE_API], False,
615 ('abracadabra_vcs', [AuthTokenModel.cls.ROLE_API], False,
616 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
616 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
617 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
617 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
618 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
618 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1)]),
619 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
619 ('abracadabra_api', [AuthTokenModel.cls.ROLE_API], True,
620 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1),
620 [('abracadabra_api', AuthTokenModel.cls.ROLE_API, -1),
621 ('abracadabra_http', AuthTokenModel.cls.ROLE_HTTP, -1)]),
621 ('abracadabra_http', AuthTokenModel.cls.ROLE_HTTP, -1)]),
622 ])
622 ])
623 def test_auth_by_token(test_token, test_roles, auth_result, expected_tokens,
623 def test_auth_by_token(test_token, test_roles, auth_result, expected_tokens,
624 user_util):
624 user_util):
625 user = user_util.create_user()
625 user = user_util.create_user()
626 user_id = user.user_id
626 user_id = user.user_id
627 for token, role, expires in expected_tokens:
627 for token, role, expires in expected_tokens:
628 new_token = AuthTokenModel().create(user_id, u'test-token', expires, role)
628 new_token = AuthTokenModel().create(user_id, u'test-token', expires, role)
629 new_token.api_key = token # inject known name for testing...
629 new_token.api_key = token # inject known name for testing...
630
630
631 assert auth_result == user.authenticate_by_token(
631 assert auth_result == user.authenticate_by_token(
632 test_token, roles=test_roles)
632 test_token, roles=test_roles)
General Comments 0
You need to be logged in to leave comments. Login now