##// END OF EJS Templates
fixed tests and missing replacements from 5f1850e4712a
marcink -
r3415:b8f929bf beta
parent child Browse files
Show More
@@ -1,1029 +1,1029 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.lib.auth
3 rhodecode.lib.auth
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 authentication and permission libraries
6 authentication and permission libraries
7
7
8 :created_on: Apr 4, 2010
8 :created_on: Apr 4, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import random
26 import random
27 import logging
27 import logging
28 import traceback
28 import traceback
29 import hashlib
29 import hashlib
30
30
31 from tempfile import _RandomNameSequence
31 from tempfile import _RandomNameSequence
32 from decorator import decorator
32 from decorator import decorator
33
33
34 from pylons import config, url, request
34 from pylons import config, url, request
35 from pylons.controllers.util import abort, redirect
35 from pylons.controllers.util import abort, redirect
36 from pylons.i18n.translation import _
36 from pylons.i18n.translation import _
37 from sqlalchemy.orm.exc import ObjectDeletedError
37 from sqlalchemy.orm.exc import ObjectDeletedError
38
38
39 from rhodecode import __platform__, is_windows, is_unix
39 from rhodecode import __platform__, is_windows, is_unix
40 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
41
41
42 from rhodecode.lib.utils2 import str2bool, safe_unicode
42 from rhodecode.lib.utils2 import str2bool, safe_unicode
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
43 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
44 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
44 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
45 from rhodecode.lib.auth_ldap import AuthLdap
45 from rhodecode.lib.auth_ldap import AuthLdap
46
46
47 from rhodecode.model import meta
47 from rhodecode.model import meta
48 from rhodecode.model.user import UserModel
48 from rhodecode.model.user import UserModel
49 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
49 from rhodecode.model.db import Permission, RhodeCodeSetting, User, UserIpMap
50 from rhodecode.lib.caching_query import FromCache
50 from rhodecode.lib.caching_query import FromCache
51
51
52 log = logging.getLogger(__name__)
52 log = logging.getLogger(__name__)
53
53
54
54
55 class PasswordGenerator(object):
55 class PasswordGenerator(object):
56 """
56 """
57 This is a simple class for generating password from different sets of
57 This is a simple class for generating password from different sets of
58 characters
58 characters
59 usage::
59 usage::
60
60
61 passwd_gen = PasswordGenerator()
61 passwd_gen = PasswordGenerator()
62 #print 8-letter password containing only big and small letters
62 #print 8-letter password containing only big and small letters
63 of alphabet
63 of alphabet
64 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
65 """
65 """
66 ALPHABETS_NUM = r'''1234567890'''
66 ALPHABETS_NUM = r'''1234567890'''
67 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
67 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
68 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
68 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
69 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
69 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
70 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
70 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
71 + ALPHABETS_NUM + ALPHABETS_SPECIAL
71 + ALPHABETS_NUM + ALPHABETS_SPECIAL
72 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
72 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
73 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
74 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
74 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
75 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
75 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
76
76
77 def __init__(self, passwd=''):
77 def __init__(self, passwd=''):
78 self.passwd = passwd
78 self.passwd = passwd
79
79
80 def gen_password(self, length, type_=None):
80 def gen_password(self, length, type_=None):
81 if type_ is None:
81 if type_ is None:
82 type_ = self.ALPHABETS_FULL
82 type_ = self.ALPHABETS_FULL
83 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
83 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
84 return self.passwd
84 return self.passwd
85
85
86
86
87 class RhodeCodeCrypto(object):
87 class RhodeCodeCrypto(object):
88
88
89 @classmethod
89 @classmethod
90 def hash_string(cls, str_):
90 def hash_string(cls, str_):
91 """
91 """
92 Cryptographic function used for password hashing based on pybcrypt
92 Cryptographic function used for password hashing based on pybcrypt
93 or pycrypto in windows
93 or pycrypto in windows
94
94
95 :param password: password to hash
95 :param password: password to hash
96 """
96 """
97 if is_windows:
97 if is_windows:
98 from hashlib import sha256
98 from hashlib import sha256
99 return sha256(str_).hexdigest()
99 return sha256(str_).hexdigest()
100 elif is_unix:
100 elif is_unix:
101 import bcrypt
101 import bcrypt
102 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
102 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
103 else:
103 else:
104 raise Exception('Unknown or unsupported platform %s' \
104 raise Exception('Unknown or unsupported platform %s' \
105 % __platform__)
105 % __platform__)
106
106
107 @classmethod
107 @classmethod
108 def hash_check(cls, password, hashed):
108 def hash_check(cls, password, hashed):
109 """
109 """
110 Checks matching password with it's hashed value, runs different
110 Checks matching password with it's hashed value, runs different
111 implementation based on platform it runs on
111 implementation based on platform it runs on
112
112
113 :param password: password
113 :param password: password
114 :param hashed: password in hashed form
114 :param hashed: password in hashed form
115 """
115 """
116
116
117 if is_windows:
117 if is_windows:
118 from hashlib import sha256
118 from hashlib import sha256
119 return sha256(password).hexdigest() == hashed
119 return sha256(password).hexdigest() == hashed
120 elif is_unix:
120 elif is_unix:
121 import bcrypt
121 import bcrypt
122 return bcrypt.hashpw(password, hashed) == hashed
122 return bcrypt.hashpw(password, hashed) == hashed
123 else:
123 else:
124 raise Exception('Unknown or unsupported platform %s' \
124 raise Exception('Unknown or unsupported platform %s' \
125 % __platform__)
125 % __platform__)
126
126
127
127
128 def get_crypt_password(password):
128 def get_crypt_password(password):
129 return RhodeCodeCrypto.hash_string(password)
129 return RhodeCodeCrypto.hash_string(password)
130
130
131
131
132 def check_password(password, hashed):
132 def check_password(password, hashed):
133 return RhodeCodeCrypto.hash_check(password, hashed)
133 return RhodeCodeCrypto.hash_check(password, hashed)
134
134
135
135
136 def generate_api_key(str_, salt=None):
136 def generate_api_key(str_, salt=None):
137 """
137 """
138 Generates API KEY from given string
138 Generates API KEY from given string
139
139
140 :param str_:
140 :param str_:
141 :param salt:
141 :param salt:
142 """
142 """
143
143
144 if salt is None:
144 if salt is None:
145 salt = _RandomNameSequence().next()
145 salt = _RandomNameSequence().next()
146
146
147 return hashlib.sha1(str_ + salt).hexdigest()
147 return hashlib.sha1(str_ + salt).hexdigest()
148
148
149
149
150 def authfunc(environ, username, password):
150 def authfunc(environ, username, password):
151 """
151 """
152 Dummy authentication wrapper function used in Mercurial and Git for
152 Dummy authentication wrapper function used in Mercurial and Git for
153 access control.
153 access control.
154
154
155 :param environ: needed only for using in Basic auth
155 :param environ: needed only for using in Basic auth
156 """
156 """
157 return authenticate(username, password)
157 return authenticate(username, password)
158
158
159
159
160 def authenticate(username, password):
160 def authenticate(username, password):
161 """
161 """
162 Authentication function used for access control,
162 Authentication function used for access control,
163 firstly checks for db authentication then if ldap is enabled for ldap
163 firstly checks for db authentication then if ldap is enabled for ldap
164 authentication, also creates ldap user if not in database
164 authentication, also creates ldap user if not in database
165
165
166 :param username: username
166 :param username: username
167 :param password: password
167 :param password: password
168 """
168 """
169
169
170 user_model = UserModel()
170 user_model = UserModel()
171 user = User.get_by_username(username)
171 user = User.get_by_username(username)
172
172
173 log.debug('Authenticating user using RhodeCode account')
173 log.debug('Authenticating user using RhodeCode account')
174 if user is not None and not user.ldap_dn:
174 if user is not None and not user.ldap_dn:
175 if user.active:
175 if user.active:
176 if user.username == 'default' and user.active:
176 if user.username == 'default' and user.active:
177 log.info('user %s authenticated correctly as anonymous user' %
177 log.info('user %s authenticated correctly as anonymous user' %
178 username)
178 username)
179 return True
179 return True
180
180
181 elif user.username == username and check_password(password,
181 elif user.username == username and check_password(password,
182 user.password):
182 user.password):
183 log.info('user %s authenticated correctly' % username)
183 log.info('user %s authenticated correctly' % username)
184 return True
184 return True
185 else:
185 else:
186 log.warning('user %s tried auth but is disabled' % username)
186 log.warning('user %s tried auth but is disabled' % username)
187
187
188 else:
188 else:
189 log.debug('Regular authentication failed')
189 log.debug('Regular authentication failed')
190 user_obj = User.get_by_username(username, case_insensitive=True)
190 user_obj = User.get_by_username(username, case_insensitive=True)
191
191
192 if user_obj is not None and not user_obj.ldap_dn:
192 if user_obj is not None and not user_obj.ldap_dn:
193 log.debug('this user already exists as non ldap')
193 log.debug('this user already exists as non ldap')
194 return False
194 return False
195
195
196 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 ldap_settings = RhodeCodeSetting.get_ldap_settings()
197 #======================================================================
197 #======================================================================
198 # FALLBACK TO LDAP AUTH IF ENABLE
198 # FALLBACK TO LDAP AUTH IF ENABLE
199 #======================================================================
199 #======================================================================
200 if str2bool(ldap_settings.get('ldap_active')):
200 if str2bool(ldap_settings.get('ldap_active')):
201 log.debug("Authenticating user using ldap")
201 log.debug("Authenticating user using ldap")
202 kwargs = {
202 kwargs = {
203 'server': ldap_settings.get('ldap_host', ''),
203 'server': ldap_settings.get('ldap_host', ''),
204 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 'base_dn': ldap_settings.get('ldap_base_dn', ''),
205 'port': ldap_settings.get('ldap_port'),
205 'port': ldap_settings.get('ldap_port'),
206 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 'bind_dn': ldap_settings.get('ldap_dn_user'),
207 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 'bind_pass': ldap_settings.get('ldap_dn_pass'),
208 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 'tls_kind': ldap_settings.get('ldap_tls_kind'),
209 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
210 'ldap_filter': ldap_settings.get('ldap_filter'),
210 'ldap_filter': ldap_settings.get('ldap_filter'),
211 'search_scope': ldap_settings.get('ldap_search_scope'),
211 'search_scope': ldap_settings.get('ldap_search_scope'),
212 'attr_login': ldap_settings.get('ldap_attr_login'),
212 'attr_login': ldap_settings.get('ldap_attr_login'),
213 'ldap_version': 3,
213 'ldap_version': 3,
214 }
214 }
215 log.debug('Checking for ldap authentication')
215 log.debug('Checking for ldap authentication')
216 try:
216 try:
217 aldap = AuthLdap(**kwargs)
217 aldap = AuthLdap(**kwargs)
218 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
219 password)
219 password)
220 log.debug('Got ldap DN response %s' % user_dn)
220 log.debug('Got ldap DN response %s' % user_dn)
221
221
222 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
223 .get(k), [''])[0]
223 .get(k), [''])[0]
224
224
225 user_attrs = {
225 user_attrs = {
226 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
227 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
228 'email': get_ldap_attr('ldap_attr_email'),
228 'email': get_ldap_attr('ldap_attr_email'),
229 'active': 'hg.register.auto_activate' in User\
229 'active': 'hg.register.auto_activate' in User\
230 .get_by_username('default').AuthUser.permissions['global']
230 .get_by_username('default').AuthUser.permissions['global']
231 }
231 }
232
232
233 # don't store LDAP password since we don't need it. Override
233 # don't store LDAP password since we don't need it. Override
234 # with some random generated password
234 # with some random generated password
235 _password = PasswordGenerator().gen_password(length=8)
235 _password = PasswordGenerator().gen_password(length=8)
236 # create this user on the fly if it doesn't exist in rhodecode
236 # create this user on the fly if it doesn't exist in rhodecode
237 # database
237 # database
238 if user_model.create_ldap(username, _password, user_dn,
238 if user_model.create_ldap(username, _password, user_dn,
239 user_attrs):
239 user_attrs):
240 log.info('created new ldap user %s' % username)
240 log.info('created new ldap user %s' % username)
241
241
242 Session().commit()
242 Session().commit()
243 return True
243 return True
244 except (LdapUsernameError, LdapPasswordError,):
244 except (LdapUsernameError, LdapPasswordError,):
245 pass
245 pass
246 except (Exception,):
246 except (Exception,):
247 log.error(traceback.format_exc())
247 log.error(traceback.format_exc())
248 pass
248 pass
249 return False
249 return False
250
250
251
251
252 def login_container_auth(username):
252 def login_container_auth(username):
253 user = User.get_by_username(username)
253 user = User.get_by_username(username)
254 if user is None:
254 if user is None:
255 user_attrs = {
255 user_attrs = {
256 'name': username,
256 'name': username,
257 'lastname': None,
257 'lastname': None,
258 'email': None,
258 'email': None,
259 'active': 'hg.register.auto_activate' in User\
259 'active': 'hg.register.auto_activate' in User\
260 .get_by_username('default').AuthUser.permissions['global']
260 .get_by_username('default').AuthUser.permissions['global']
261 }
261 }
262 user = UserModel().create_for_container_auth(username, user_attrs)
262 user = UserModel().create_for_container_auth(username, user_attrs)
263 if not user:
263 if not user:
264 return None
264 return None
265 log.info('User %s was created by container authentication' % username)
265 log.info('User %s was created by container authentication' % username)
266
266
267 if not user.active:
267 if not user.active:
268 return None
268 return None
269
269
270 user.update_lastlogin()
270 user.update_lastlogin()
271 Session().commit()
271 Session().commit()
272
272
273 log.debug('User %s is now logged in by container authentication',
273 log.debug('User %s is now logged in by container authentication',
274 user.username)
274 user.username)
275 return user
275 return user
276
276
277
277
278 def get_container_username(environ, config, clean_username=False):
278 def get_container_username(environ, config, clean_username=False):
279 """
279 """
280 Get's the container_auth username (or email). It tries to get username
280 Get's the container_auth username (or email). It tries to get username
281 from REMOTE_USER if container_auth_enabled is enabled, if that fails
281 from REMOTE_USER if container_auth_enabled is enabled, if that fails
282 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
282 it tries to get username from HTTP_X_FORWARDED_USER if proxypass_auth_enabled
283 is enabled. clean_username extracts the username from this data if it's
283 is enabled. clean_username extracts the username from this data if it's
284 having @ in it.
284 having @ in it.
285
285
286 :param environ:
286 :param environ:
287 :param config:
287 :param config:
288 :param clean_username:
288 :param clean_username:
289 """
289 """
290 username = None
290 username = None
291
291
292 if str2bool(config.get('container_auth_enabled', False)):
292 if str2bool(config.get('container_auth_enabled', False)):
293 from paste.httpheaders import REMOTE_USER
293 from paste.httpheaders import REMOTE_USER
294 username = REMOTE_USER(environ)
294 username = REMOTE_USER(environ)
295 log.debug('extracted REMOTE_USER:%s' % (username))
295 log.debug('extracted REMOTE_USER:%s' % (username))
296
296
297 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
297 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
298 username = environ.get('HTTP_X_FORWARDED_USER')
298 username = environ.get('HTTP_X_FORWARDED_USER')
299 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
299 log.debug('extracted HTTP_X_FORWARDED_USER:%s' % (username))
300
300
301 if username and clean_username:
301 if username and clean_username:
302 # Removing realm and domain from username
302 # Removing realm and domain from username
303 username = username.partition('@')[0]
303 username = username.partition('@')[0]
304 username = username.rpartition('\\')[2]
304 username = username.rpartition('\\')[2]
305 log.debug('Received username %s from container' % username)
305 log.debug('Received username %s from container' % username)
306
306
307 return username
307 return username
308
308
309
309
310 class CookieStoreWrapper(object):
310 class CookieStoreWrapper(object):
311
311
312 def __init__(self, cookie_store):
312 def __init__(self, cookie_store):
313 self.cookie_store = cookie_store
313 self.cookie_store = cookie_store
314
314
315 def __repr__(self):
315 def __repr__(self):
316 return 'CookieStore<%s>' % (self.cookie_store)
316 return 'CookieStore<%s>' % (self.cookie_store)
317
317
318 def get(self, key, other=None):
318 def get(self, key, other=None):
319 if isinstance(self.cookie_store, dict):
319 if isinstance(self.cookie_store, dict):
320 return self.cookie_store.get(key, other)
320 return self.cookie_store.get(key, other)
321 elif isinstance(self.cookie_store, AuthUser):
321 elif isinstance(self.cookie_store, AuthUser):
322 return self.cookie_store.__dict__.get(key, other)
322 return self.cookie_store.__dict__.get(key, other)
323
323
324
324
325 class AuthUser(object):
325 class AuthUser(object):
326 """
326 """
327 A simple object that handles all attributes of user in RhodeCode
327 A simple object that handles all attributes of user in RhodeCode
328
328
329 It does lookup based on API key,given user, or user present in session
329 It does lookup based on API key,given user, or user present in session
330 Then it fills all required information for such user. It also checks if
330 Then it fills all required information for such user. It also checks if
331 anonymous access is enabled and if so, it returns default user as logged
331 anonymous access is enabled and if so, it returns default user as logged
332 in
332 in
333 """
333 """
334
334
335 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
335 def __init__(self, user_id=None, api_key=None, username=None, ip_addr=None):
336
336
337 self.user_id = user_id
337 self.user_id = user_id
338 self.api_key = None
338 self.api_key = None
339 self.username = username
339 self.username = username
340 self.ip_addr = ip_addr
340 self.ip_addr = ip_addr
341
341
342 self.name = ''
342 self.name = ''
343 self.lastname = ''
343 self.lastname = ''
344 self.email = ''
344 self.email = ''
345 self.is_authenticated = False
345 self.is_authenticated = False
346 self.admin = False
346 self.admin = False
347 self.inherit_default_permissions = False
347 self.inherit_default_permissions = False
348 self.permissions = {}
348 self.permissions = {}
349 self._api_key = api_key
349 self._api_key = api_key
350 self.propagate_data()
350 self.propagate_data()
351 self._instance = None
351 self._instance = None
352
352
353 def propagate_data(self):
353 def propagate_data(self):
354 user_model = UserModel()
354 user_model = UserModel()
355 self.anonymous_user = User.get_by_username('default', cache=True)
355 self.anonymous_user = User.get_by_username('default', cache=True)
356 is_user_loaded = False
356 is_user_loaded = False
357
357
358 # try go get user by api key
358 # try go get user by api key
359 if self._api_key and self._api_key != self.anonymous_user.api_key:
359 if self._api_key and self._api_key != self.anonymous_user.api_key:
360 log.debug('Auth User lookup by API KEY %s' % self._api_key)
360 log.debug('Auth User lookup by API KEY %s' % self._api_key)
361 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
361 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
362 # lookup by userid
362 # lookup by userid
363 elif (self.user_id is not None and
363 elif (self.user_id is not None and
364 self.user_id != self.anonymous_user.user_id):
364 self.user_id != self.anonymous_user.user_id):
365 log.debug('Auth User lookup by USER ID %s' % self.user_id)
365 log.debug('Auth User lookup by USER ID %s' % self.user_id)
366 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
366 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
367 # lookup by username
367 # lookup by username
368 elif self.username and \
368 elif self.username and \
369 str2bool(config.get('container_auth_enabled', False)):
369 str2bool(config.get('container_auth_enabled', False)):
370
370
371 log.debug('Auth User lookup by USER NAME %s' % self.username)
371 log.debug('Auth User lookup by USER NAME %s' % self.username)
372 dbuser = login_container_auth(self.username)
372 dbuser = login_container_auth(self.username)
373 if dbuser is not None:
373 if dbuser is not None:
374 log.debug('filling all attributes to object')
374 log.debug('filling all attributes to object')
375 for k, v in dbuser.get_dict().items():
375 for k, v in dbuser.get_dict().items():
376 setattr(self, k, v)
376 setattr(self, k, v)
377 self.set_authenticated()
377 self.set_authenticated()
378 is_user_loaded = True
378 is_user_loaded = True
379 else:
379 else:
380 log.debug('No data in %s that could been used to log in' % self)
380 log.debug('No data in %s that could been used to log in' % self)
381
381
382 if not is_user_loaded:
382 if not is_user_loaded:
383 # if we cannot authenticate user try anonymous
383 # if we cannot authenticate user try anonymous
384 if self.anonymous_user.active is True:
384 if self.anonymous_user.active is True:
385 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
385 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
386 # then we set this user is logged in
386 # then we set this user is logged in
387 self.is_authenticated = True
387 self.is_authenticated = True
388 else:
388 else:
389 self.user_id = None
389 self.user_id = None
390 self.username = None
390 self.username = None
391 self.is_authenticated = False
391 self.is_authenticated = False
392
392
393 if not self.username:
393 if not self.username:
394 self.username = 'None'
394 self.username = 'None'
395
395
396 log.debug('Auth User is now %s' % self)
396 log.debug('Auth User is now %s' % self)
397 user_model.fill_perms(self)
397 user_model.fill_perms(self)
398
398
399 @property
399 @property
400 def is_admin(self):
400 def is_admin(self):
401 return self.admin
401 return self.admin
402
402
403 @property
403 @property
404 def repos_admin(self):
404 def repos_admin(self):
405 """
405 """
406 Returns list of repositories you're an admin of
406 Returns list of repositories you're an admin of
407 """
407 """
408 return [x[0] for x in self.permissions['repositories'].iteritems()
408 return [x[0] for x in self.permissions['repositories'].iteritems()
409 if x[1] == 'repository.admin']
409 if x[1] == 'repository.admin']
410
410
411 @property
411 @property
412 def groups_admin(self):
412 def groups_admin(self):
413 """
413 """
414 Returns list of repositories groups you're an admin of
414 Returns list of repository groups you're an admin of
415 """
415 """
416 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
416 return [x[0] for x in self.permissions['repositories_groups'].iteritems()
417 if x[1] == 'group.admin']
417 if x[1] == 'group.admin']
418
418
419 @property
419 @property
420 def ip_allowed(self):
420 def ip_allowed(self):
421 """
421 """
422 Checks if ip_addr used in constructor is allowed from defined list of
422 Checks if ip_addr used in constructor is allowed from defined list of
423 allowed ip_addresses for user
423 allowed ip_addresses for user
424
424
425 :returns: boolean, True if ip is in allowed ip range
425 :returns: boolean, True if ip is in allowed ip range
426 """
426 """
427 #check IP
427 #check IP
428 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
428 allowed_ips = AuthUser.get_allowed_ips(self.user_id, cache=True)
429 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
429 if check_ip_access(source_ip=self.ip_addr, allowed_ips=allowed_ips):
430 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
430 log.debug('IP:%s is in range of %s' % (self.ip_addr, allowed_ips))
431 return True
431 return True
432 else:
432 else:
433 log.info('Access for IP:%s forbidden, '
433 log.info('Access for IP:%s forbidden, '
434 'not in %s' % (self.ip_addr, allowed_ips))
434 'not in %s' % (self.ip_addr, allowed_ips))
435 return False
435 return False
436
436
437 def __repr__(self):
437 def __repr__(self):
438 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
438 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
439 self.is_authenticated)
439 self.is_authenticated)
440
440
441 def set_authenticated(self, authenticated=True):
441 def set_authenticated(self, authenticated=True):
442 if self.user_id != self.anonymous_user.user_id:
442 if self.user_id != self.anonymous_user.user_id:
443 self.is_authenticated = authenticated
443 self.is_authenticated = authenticated
444
444
445 def get_cookie_store(self):
445 def get_cookie_store(self):
446 return {'username': self.username,
446 return {'username': self.username,
447 'user_id': self.user_id,
447 'user_id': self.user_id,
448 'is_authenticated': self.is_authenticated}
448 'is_authenticated': self.is_authenticated}
449
449
450 @classmethod
450 @classmethod
451 def from_cookie_store(cls, cookie_store):
451 def from_cookie_store(cls, cookie_store):
452 """
452 """
453 Creates AuthUser from a cookie store
453 Creates AuthUser from a cookie store
454
454
455 :param cls:
455 :param cls:
456 :param cookie_store:
456 :param cookie_store:
457 """
457 """
458 user_id = cookie_store.get('user_id')
458 user_id = cookie_store.get('user_id')
459 username = cookie_store.get('username')
459 username = cookie_store.get('username')
460 api_key = cookie_store.get('api_key')
460 api_key = cookie_store.get('api_key')
461 return AuthUser(user_id, api_key, username)
461 return AuthUser(user_id, api_key, username)
462
462
463 @classmethod
463 @classmethod
464 def get_allowed_ips(cls, user_id, cache=False):
464 def get_allowed_ips(cls, user_id, cache=False):
465 _set = set()
465 _set = set()
466 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
466 user_ips = UserIpMap.query().filter(UserIpMap.user_id == user_id)
467 if cache:
467 if cache:
468 user_ips = user_ips.options(FromCache("sql_cache_short",
468 user_ips = user_ips.options(FromCache("sql_cache_short",
469 "get_user_ips_%s" % user_id))
469 "get_user_ips_%s" % user_id))
470 for ip in user_ips:
470 for ip in user_ips:
471 try:
471 try:
472 _set.add(ip.ip_addr)
472 _set.add(ip.ip_addr)
473 except ObjectDeletedError:
473 except ObjectDeletedError:
474 # since we use heavy caching sometimes it happens that we get
474 # since we use heavy caching sometimes it happens that we get
475 # deleted objects here, we just skip them
475 # deleted objects here, we just skip them
476 pass
476 pass
477 return _set or set(['0.0.0.0/0', '::/0'])
477 return _set or set(['0.0.0.0/0', '::/0'])
478
478
479
479
480 def set_available_permissions(config):
480 def set_available_permissions(config):
481 """
481 """
482 This function will propagate pylons globals with all available defined
482 This function will propagate pylons globals with all available defined
483 permission given in db. We don't want to check each time from db for new
483 permission given in db. We don't want to check each time from db for new
484 permissions since adding a new permission also requires application restart
484 permissions since adding a new permission also requires application restart
485 ie. to decorate new views with the newly created permission
485 ie. to decorate new views with the newly created permission
486
486
487 :param config: current pylons config instance
487 :param config: current pylons config instance
488
488
489 """
489 """
490 log.info('getting information about all available permissions')
490 log.info('getting information about all available permissions')
491 try:
491 try:
492 sa = meta.Session
492 sa = meta.Session
493 all_perms = sa.query(Permission).all()
493 all_perms = sa.query(Permission).all()
494 except Exception:
494 except Exception:
495 pass
495 pass
496 finally:
496 finally:
497 meta.Session.remove()
497 meta.Session.remove()
498
498
499 config['available_permissions'] = [x.permission_name for x in all_perms]
499 config['available_permissions'] = [x.permission_name for x in all_perms]
500
500
501
501
502 #==============================================================================
502 #==============================================================================
503 # CHECK DECORATORS
503 # CHECK DECORATORS
504 #==============================================================================
504 #==============================================================================
505 class LoginRequired(object):
505 class LoginRequired(object):
506 """
506 """
507 Must be logged in to execute this function else
507 Must be logged in to execute this function else
508 redirect to login page
508 redirect to login page
509
509
510 :param api_access: if enabled this checks only for valid auth token
510 :param api_access: if enabled this checks only for valid auth token
511 and grants access based on valid token
511 and grants access based on valid token
512 """
512 """
513
513
514 def __init__(self, api_access=False):
514 def __init__(self, api_access=False):
515 self.api_access = api_access
515 self.api_access = api_access
516
516
517 def __call__(self, func):
517 def __call__(self, func):
518 return decorator(self.__wrapper, func)
518 return decorator(self.__wrapper, func)
519
519
520 def __wrapper(self, func, *fargs, **fkwargs):
520 def __wrapper(self, func, *fargs, **fkwargs):
521 cls = fargs[0]
521 cls = fargs[0]
522 user = cls.rhodecode_user
522 user = cls.rhodecode_user
523 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
523 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
524
524
525 #check IP
525 #check IP
526 ip_access_ok = True
526 ip_access_ok = True
527 if not user.ip_allowed:
527 if not user.ip_allowed:
528 from rhodecode.lib import helpers as h
528 from rhodecode.lib import helpers as h
529 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
529 h.flash(h.literal(_('IP %s not allowed' % (user.ip_addr))),
530 category='warning')
530 category='warning')
531 ip_access_ok = False
531 ip_access_ok = False
532
532
533 api_access_ok = False
533 api_access_ok = False
534 if self.api_access:
534 if self.api_access:
535 log.debug('Checking API KEY access for %s' % cls)
535 log.debug('Checking API KEY access for %s' % cls)
536 if user.api_key == request.GET.get('api_key'):
536 if user.api_key == request.GET.get('api_key'):
537 api_access_ok = True
537 api_access_ok = True
538 else:
538 else:
539 log.debug("API KEY token not valid")
539 log.debug("API KEY token not valid")
540
540
541 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
541 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
542 if (user.is_authenticated or api_access_ok) and ip_access_ok:
542 if (user.is_authenticated or api_access_ok) and ip_access_ok:
543 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
543 reason = 'RegularAuth' if user.is_authenticated else 'APIAuth'
544 log.info('user %s is authenticated and granted access to %s '
544 log.info('user %s is authenticated and granted access to %s '
545 'using %s' % (user.username, loc, reason)
545 'using %s' % (user.username, loc, reason)
546 )
546 )
547 return func(*fargs, **fkwargs)
547 return func(*fargs, **fkwargs)
548 else:
548 else:
549 log.warn('user %s NOT authenticated on func: %s' % (
549 log.warn('user %s NOT authenticated on func: %s' % (
550 user, loc)
550 user, loc)
551 )
551 )
552 p = url.current()
552 p = url.current()
553
553
554 log.debug('redirecting to login page with %s' % p)
554 log.debug('redirecting to login page with %s' % p)
555 return redirect(url('login_home', came_from=p))
555 return redirect(url('login_home', came_from=p))
556
556
557
557
558 class NotAnonymous(object):
558 class NotAnonymous(object):
559 """
559 """
560 Must be logged in to execute this function else
560 Must be logged in to execute this function else
561 redirect to login page"""
561 redirect to login page"""
562
562
563 def __call__(self, func):
563 def __call__(self, func):
564 return decorator(self.__wrapper, func)
564 return decorator(self.__wrapper, func)
565
565
566 def __wrapper(self, func, *fargs, **fkwargs):
566 def __wrapper(self, func, *fargs, **fkwargs):
567 cls = fargs[0]
567 cls = fargs[0]
568 self.user = cls.rhodecode_user
568 self.user = cls.rhodecode_user
569
569
570 log.debug('Checking if user is not anonymous @%s' % cls)
570 log.debug('Checking if user is not anonymous @%s' % cls)
571
571
572 anonymous = self.user.username == 'default'
572 anonymous = self.user.username == 'default'
573
573
574 if anonymous:
574 if anonymous:
575 p = url.current()
575 p = url.current()
576
576
577 import rhodecode.lib.helpers as h
577 import rhodecode.lib.helpers as h
578 h.flash(_('You need to be a registered user to '
578 h.flash(_('You need to be a registered user to '
579 'perform this action'),
579 'perform this action'),
580 category='warning')
580 category='warning')
581 return redirect(url('login_home', came_from=p))
581 return redirect(url('login_home', came_from=p))
582 else:
582 else:
583 return func(*fargs, **fkwargs)
583 return func(*fargs, **fkwargs)
584
584
585
585
586 class PermsDecorator(object):
586 class PermsDecorator(object):
587 """Base class for controller decorators"""
587 """Base class for controller decorators"""
588
588
589 def __init__(self, *required_perms):
589 def __init__(self, *required_perms):
590 available_perms = config['available_permissions']
590 available_perms = config['available_permissions']
591 for perm in required_perms:
591 for perm in required_perms:
592 if perm not in available_perms:
592 if perm not in available_perms:
593 raise Exception("'%s' permission is not defined" % perm)
593 raise Exception("'%s' permission is not defined" % perm)
594 self.required_perms = set(required_perms)
594 self.required_perms = set(required_perms)
595 self.user_perms = None
595 self.user_perms = None
596
596
597 def __call__(self, func):
597 def __call__(self, func):
598 return decorator(self.__wrapper, func)
598 return decorator(self.__wrapper, func)
599
599
600 def __wrapper(self, func, *fargs, **fkwargs):
600 def __wrapper(self, func, *fargs, **fkwargs):
601 cls = fargs[0]
601 cls = fargs[0]
602 self.user = cls.rhodecode_user
602 self.user = cls.rhodecode_user
603 self.user_perms = self.user.permissions
603 self.user_perms = self.user.permissions
604 log.debug('checking %s permissions %s for %s %s',
604 log.debug('checking %s permissions %s for %s %s',
605 self.__class__.__name__, self.required_perms, cls, self.user)
605 self.__class__.__name__, self.required_perms, cls, self.user)
606
606
607 if self.check_permissions():
607 if self.check_permissions():
608 log.debug('Permission granted for %s %s' % (cls, self.user))
608 log.debug('Permission granted for %s %s' % (cls, self.user))
609 return func(*fargs, **fkwargs)
609 return func(*fargs, **fkwargs)
610
610
611 else:
611 else:
612 log.debug('Permission denied for %s %s' % (cls, self.user))
612 log.debug('Permission denied for %s %s' % (cls, self.user))
613 anonymous = self.user.username == 'default'
613 anonymous = self.user.username == 'default'
614
614
615 if anonymous:
615 if anonymous:
616 p = url.current()
616 p = url.current()
617
617
618 import rhodecode.lib.helpers as h
618 import rhodecode.lib.helpers as h
619 h.flash(_('You need to be a signed in to '
619 h.flash(_('You need to be a signed in to '
620 'view this page'),
620 'view this page'),
621 category='warning')
621 category='warning')
622 return redirect(url('login_home', came_from=p))
622 return redirect(url('login_home', came_from=p))
623
623
624 else:
624 else:
625 # redirect with forbidden ret code
625 # redirect with forbidden ret code
626 return abort(403)
626 return abort(403)
627
627
628 def check_permissions(self):
628 def check_permissions(self):
629 """Dummy function for overriding"""
629 """Dummy function for overriding"""
630 raise Exception('You have to write this function in child class')
630 raise Exception('You have to write this function in child class')
631
631
632
632
633 class HasPermissionAllDecorator(PermsDecorator):
633 class HasPermissionAllDecorator(PermsDecorator):
634 """
634 """
635 Checks for access permission for all given predicates. All of them
635 Checks for access permission for all given predicates. All of them
636 have to be meet in order to fulfill the request
636 have to be meet in order to fulfill the request
637 """
637 """
638
638
639 def check_permissions(self):
639 def check_permissions(self):
640 if self.required_perms.issubset(self.user_perms.get('global')):
640 if self.required_perms.issubset(self.user_perms.get('global')):
641 return True
641 return True
642 return False
642 return False
643
643
644
644
645 class HasPermissionAnyDecorator(PermsDecorator):
645 class HasPermissionAnyDecorator(PermsDecorator):
646 """
646 """
647 Checks for access permission for any of given predicates. In order to
647 Checks for access permission for any of given predicates. In order to
648 fulfill the request any of predicates must be meet
648 fulfill the request any of predicates must be meet
649 """
649 """
650
650
651 def check_permissions(self):
651 def check_permissions(self):
652 if self.required_perms.intersection(self.user_perms.get('global')):
652 if self.required_perms.intersection(self.user_perms.get('global')):
653 return True
653 return True
654 return False
654 return False
655
655
656
656
657 class HasRepoPermissionAllDecorator(PermsDecorator):
657 class HasRepoPermissionAllDecorator(PermsDecorator):
658 """
658 """
659 Checks for access permission for all given predicates for specific
659 Checks for access permission for all given predicates for specific
660 repository. All of them have to be meet in order to fulfill the request
660 repository. All of them have to be meet in order to fulfill the request
661 """
661 """
662
662
663 def check_permissions(self):
663 def check_permissions(self):
664 repo_name = get_repo_slug(request)
664 repo_name = get_repo_slug(request)
665 try:
665 try:
666 user_perms = set([self.user_perms['repositories'][repo_name]])
666 user_perms = set([self.user_perms['repositories'][repo_name]])
667 except KeyError:
667 except KeyError:
668 return False
668 return False
669 if self.required_perms.issubset(user_perms):
669 if self.required_perms.issubset(user_perms):
670 return True
670 return True
671 return False
671 return False
672
672
673
673
674 class HasRepoPermissionAnyDecorator(PermsDecorator):
674 class HasRepoPermissionAnyDecorator(PermsDecorator):
675 """
675 """
676 Checks for access permission for any of given predicates for specific
676 Checks for access permission for any of given predicates for specific
677 repository. In order to fulfill the request any of predicates must be meet
677 repository. In order to fulfill the request any of predicates must be meet
678 """
678 """
679
679
680 def check_permissions(self):
680 def check_permissions(self):
681 repo_name = get_repo_slug(request)
681 repo_name = get_repo_slug(request)
682 try:
682 try:
683 user_perms = set([self.user_perms['repositories'][repo_name]])
683 user_perms = set([self.user_perms['repositories'][repo_name]])
684 except KeyError:
684 except KeyError:
685 return False
685 return False
686
686
687 if self.required_perms.intersection(user_perms):
687 if self.required_perms.intersection(user_perms):
688 return True
688 return True
689 return False
689 return False
690
690
691
691
692 class HasReposGroupPermissionAllDecorator(PermsDecorator):
692 class HasReposGroupPermissionAllDecorator(PermsDecorator):
693 """
693 """
694 Checks for access permission for all given predicates for specific
694 Checks for access permission for all given predicates for specific
695 repository. All of them have to be meet in order to fulfill the request
695 repository. All of them have to be meet in order to fulfill the request
696 """
696 """
697
697
698 def check_permissions(self):
698 def check_permissions(self):
699 group_name = get_repos_group_slug(request)
699 group_name = get_repos_group_slug(request)
700 try:
700 try:
701 user_perms = set([self.user_perms['repositories_groups'][group_name]])
701 user_perms = set([self.user_perms['repositories_groups'][group_name]])
702 except KeyError:
702 except KeyError:
703 return False
703 return False
704
704
705 if self.required_perms.issubset(user_perms):
705 if self.required_perms.issubset(user_perms):
706 return True
706 return True
707 return False
707 return False
708
708
709
709
710 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
710 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
711 """
711 """
712 Checks for access permission for any of given predicates for specific
712 Checks for access permission for any of given predicates for specific
713 repository. In order to fulfill the request any of predicates must be meet
713 repository. In order to fulfill the request any of predicates must be meet
714 """
714 """
715
715
716 def check_permissions(self):
716 def check_permissions(self):
717 group_name = get_repos_group_slug(request)
717 group_name = get_repos_group_slug(request)
718 try:
718 try:
719 user_perms = set([self.user_perms['repositories_groups'][group_name]])
719 user_perms = set([self.user_perms['repositories_groups'][group_name]])
720 except KeyError:
720 except KeyError:
721 return False
721 return False
722
722
723 if self.required_perms.intersection(user_perms):
723 if self.required_perms.intersection(user_perms):
724 return True
724 return True
725 return False
725 return False
726
726
727
727
728 #==============================================================================
728 #==============================================================================
729 # CHECK FUNCTIONS
729 # CHECK FUNCTIONS
730 #==============================================================================
730 #==============================================================================
731 class PermsFunction(object):
731 class PermsFunction(object):
732 """Base function for other check functions"""
732 """Base function for other check functions"""
733
733
734 def __init__(self, *perms):
734 def __init__(self, *perms):
735 available_perms = config['available_permissions']
735 available_perms = config['available_permissions']
736
736
737 for perm in perms:
737 for perm in perms:
738 if perm not in available_perms:
738 if perm not in available_perms:
739 raise Exception("'%s' permission is not defined" % perm)
739 raise Exception("'%s' permission is not defined" % perm)
740 self.required_perms = set(perms)
740 self.required_perms = set(perms)
741 self.user_perms = None
741 self.user_perms = None
742 self.repo_name = None
742 self.repo_name = None
743 self.group_name = None
743 self.group_name = None
744
744
745 def __call__(self, check_location=''):
745 def __call__(self, check_location=''):
746 #TODO: put user as attribute here
746 #TODO: put user as attribute here
747 user = request.user
747 user = request.user
748 cls_name = self.__class__.__name__
748 cls_name = self.__class__.__name__
749 check_scope = {
749 check_scope = {
750 'HasPermissionAll': '',
750 'HasPermissionAll': '',
751 'HasPermissionAny': '',
751 'HasPermissionAny': '',
752 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
752 'HasRepoPermissionAll': 'repo:%s' % self.repo_name,
753 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
753 'HasRepoPermissionAny': 'repo:%s' % self.repo_name,
754 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
754 'HasReposGroupPermissionAll': 'group:%s' % self.group_name,
755 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
755 'HasReposGroupPermissionAny': 'group:%s' % self.group_name,
756 }.get(cls_name, '?')
756 }.get(cls_name, '?')
757 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
757 log.debug('checking cls:%s %s usr:%s %s @ %s', cls_name,
758 self.required_perms, user, check_scope,
758 self.required_perms, user, check_scope,
759 check_location or 'unspecified location')
759 check_location or 'unspecified location')
760 if not user:
760 if not user:
761 log.debug('Empty request user')
761 log.debug('Empty request user')
762 return False
762 return False
763 self.user_perms = user.permissions
763 self.user_perms = user.permissions
764 if self.check_permissions():
764 if self.check_permissions():
765 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
765 log.debug('Permission to %s granted for user: %s @ %s', self.repo_name, user,
766 check_location or 'unspecified location')
766 check_location or 'unspecified location')
767 return True
767 return True
768
768
769 else:
769 else:
770 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
770 log.debug('Permission to %s denied for user: %s @ %s', self.repo_name, user,
771 check_location or 'unspecified location')
771 check_location or 'unspecified location')
772 return False
772 return False
773
773
774 def check_permissions(self):
774 def check_permissions(self):
775 """Dummy function for overriding"""
775 """Dummy function for overriding"""
776 raise Exception('You have to write this function in child class')
776 raise Exception('You have to write this function in child class')
777
777
778
778
779 class HasPermissionAll(PermsFunction):
779 class HasPermissionAll(PermsFunction):
780 def check_permissions(self):
780 def check_permissions(self):
781 if self.required_perms.issubset(self.user_perms.get('global')):
781 if self.required_perms.issubset(self.user_perms.get('global')):
782 return True
782 return True
783 return False
783 return False
784
784
785
785
786 class HasPermissionAny(PermsFunction):
786 class HasPermissionAny(PermsFunction):
787 def check_permissions(self):
787 def check_permissions(self):
788 if self.required_perms.intersection(self.user_perms.get('global')):
788 if self.required_perms.intersection(self.user_perms.get('global')):
789 return True
789 return True
790 return False
790 return False
791
791
792
792
793 class HasRepoPermissionAll(PermsFunction):
793 class HasRepoPermissionAll(PermsFunction):
794 def __call__(self, repo_name=None, check_location=''):
794 def __call__(self, repo_name=None, check_location=''):
795 self.repo_name = repo_name
795 self.repo_name = repo_name
796 return super(HasRepoPermissionAll, self).__call__(check_location)
796 return super(HasRepoPermissionAll, self).__call__(check_location)
797
797
798 def check_permissions(self):
798 def check_permissions(self):
799 if not self.repo_name:
799 if not self.repo_name:
800 self.repo_name = get_repo_slug(request)
800 self.repo_name = get_repo_slug(request)
801
801
802 try:
802 try:
803 self._user_perms = set(
803 self._user_perms = set(
804 [self.user_perms['repositories'][self.repo_name]]
804 [self.user_perms['repositories'][self.repo_name]]
805 )
805 )
806 except KeyError:
806 except KeyError:
807 return False
807 return False
808 if self.required_perms.issubset(self._user_perms):
808 if self.required_perms.issubset(self._user_perms):
809 return True
809 return True
810 return False
810 return False
811
811
812
812
813 class HasRepoPermissionAny(PermsFunction):
813 class HasRepoPermissionAny(PermsFunction):
814 def __call__(self, repo_name=None, check_location=''):
814 def __call__(self, repo_name=None, check_location=''):
815 self.repo_name = repo_name
815 self.repo_name = repo_name
816 return super(HasRepoPermissionAny, self).__call__(check_location)
816 return super(HasRepoPermissionAny, self).__call__(check_location)
817
817
818 def check_permissions(self):
818 def check_permissions(self):
819 if not self.repo_name:
819 if not self.repo_name:
820 self.repo_name = get_repo_slug(request)
820 self.repo_name = get_repo_slug(request)
821
821
822 try:
822 try:
823 self._user_perms = set(
823 self._user_perms = set(
824 [self.user_perms['repositories'][self.repo_name]]
824 [self.user_perms['repositories'][self.repo_name]]
825 )
825 )
826 except KeyError:
826 except KeyError:
827 return False
827 return False
828 if self.required_perms.intersection(self._user_perms):
828 if self.required_perms.intersection(self._user_perms):
829 return True
829 return True
830 return False
830 return False
831
831
832
832
833 class HasReposGroupPermissionAny(PermsFunction):
833 class HasReposGroupPermissionAny(PermsFunction):
834 def __call__(self, group_name=None, check_location=''):
834 def __call__(self, group_name=None, check_location=''):
835 self.group_name = group_name
835 self.group_name = group_name
836 return super(HasReposGroupPermissionAny, self).__call__(check_location)
836 return super(HasReposGroupPermissionAny, self).__call__(check_location)
837
837
838 def check_permissions(self):
838 def check_permissions(self):
839 try:
839 try:
840 self._user_perms = set(
840 self._user_perms = set(
841 [self.user_perms['repositories_groups'][self.group_name]]
841 [self.user_perms['repositories_groups'][self.group_name]]
842 )
842 )
843 except KeyError:
843 except KeyError:
844 return False
844 return False
845 if self.required_perms.intersection(self._user_perms):
845 if self.required_perms.intersection(self._user_perms):
846 return True
846 return True
847 return False
847 return False
848
848
849
849
850 class HasReposGroupPermissionAll(PermsFunction):
850 class HasReposGroupPermissionAll(PermsFunction):
851 def __call__(self, group_name=None, check_location=''):
851 def __call__(self, group_name=None, check_location=''):
852 self.group_name = group_name
852 self.group_name = group_name
853 return super(HasReposGroupPermissionAll, self).__call__(check_location)
853 return super(HasReposGroupPermissionAll, self).__call__(check_location)
854
854
855 def check_permissions(self):
855 def check_permissions(self):
856 try:
856 try:
857 self._user_perms = set(
857 self._user_perms = set(
858 [self.user_perms['repositories_groups'][self.group_name]]
858 [self.user_perms['repositories_groups'][self.group_name]]
859 )
859 )
860 except KeyError:
860 except KeyError:
861 return False
861 return False
862 if self.required_perms.issubset(self._user_perms):
862 if self.required_perms.issubset(self._user_perms):
863 return True
863 return True
864 return False
864 return False
865
865
866
866
867 #==============================================================================
867 #==============================================================================
868 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
868 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
869 #==============================================================================
869 #==============================================================================
870 class HasPermissionAnyMiddleware(object):
870 class HasPermissionAnyMiddleware(object):
871 def __init__(self, *perms):
871 def __init__(self, *perms):
872 self.required_perms = set(perms)
872 self.required_perms = set(perms)
873
873
874 def __call__(self, user, repo_name):
874 def __call__(self, user, repo_name):
875 # repo_name MUST be unicode, since we handle keys in permission
875 # repo_name MUST be unicode, since we handle keys in permission
876 # dict by unicode
876 # dict by unicode
877 repo_name = safe_unicode(repo_name)
877 repo_name = safe_unicode(repo_name)
878 usr = AuthUser(user.user_id)
878 usr = AuthUser(user.user_id)
879 try:
879 try:
880 self.user_perms = set([usr.permissions['repositories'][repo_name]])
880 self.user_perms = set([usr.permissions['repositories'][repo_name]])
881 except Exception:
881 except Exception:
882 log.error('Exception while accessing permissions %s' %
882 log.error('Exception while accessing permissions %s' %
883 traceback.format_exc())
883 traceback.format_exc())
884 self.user_perms = set()
884 self.user_perms = set()
885 self.username = user.username
885 self.username = user.username
886 self.repo_name = repo_name
886 self.repo_name = repo_name
887 return self.check_permissions()
887 return self.check_permissions()
888
888
889 def check_permissions(self):
889 def check_permissions(self):
890 log.debug('checking VCS protocol '
890 log.debug('checking VCS protocol '
891 'permissions %s for user:%s repository:%s', self.user_perms,
891 'permissions %s for user:%s repository:%s', self.user_perms,
892 self.username, self.repo_name)
892 self.username, self.repo_name)
893 if self.required_perms.intersection(self.user_perms):
893 if self.required_perms.intersection(self.user_perms):
894 log.debug('permission granted for user:%s on repo:%s' % (
894 log.debug('permission granted for user:%s on repo:%s' % (
895 self.username, self.repo_name
895 self.username, self.repo_name
896 )
896 )
897 )
897 )
898 return True
898 return True
899 log.debug('permission denied for user:%s on repo:%s' % (
899 log.debug('permission denied for user:%s on repo:%s' % (
900 self.username, self.repo_name
900 self.username, self.repo_name
901 )
901 )
902 )
902 )
903 return False
903 return False
904
904
905
905
906 #==============================================================================
906 #==============================================================================
907 # SPECIAL VERSION TO HANDLE API AUTH
907 # SPECIAL VERSION TO HANDLE API AUTH
908 #==============================================================================
908 #==============================================================================
909 class _BaseApiPerm(object):
909 class _BaseApiPerm(object):
910 def __init__(self, *perms):
910 def __init__(self, *perms):
911 self.required_perms = set(perms)
911 self.required_perms = set(perms)
912
912
913 def __call__(self, check_location='unspecified', user=None, repo_name=None):
913 def __call__(self, check_location='unspecified', user=None, repo_name=None):
914 cls_name = self.__class__.__name__
914 cls_name = self.__class__.__name__
915 check_scope = 'user:%s, repo:%s' % (user, repo_name)
915 check_scope = 'user:%s, repo:%s' % (user, repo_name)
916 log.debug('checking cls:%s %s %s @ %s', cls_name,
916 log.debug('checking cls:%s %s %s @ %s', cls_name,
917 self.required_perms, check_scope, check_location)
917 self.required_perms, check_scope, check_location)
918 if not user:
918 if not user:
919 log.debug('Empty User passed into arguments')
919 log.debug('Empty User passed into arguments')
920 return False
920 return False
921
921
922 ## process user
922 ## process user
923 if not isinstance(user, AuthUser):
923 if not isinstance(user, AuthUser):
924 user = AuthUser(user.user_id)
924 user = AuthUser(user.user_id)
925
925
926 if self.check_permissions(user.permissions, repo_name):
926 if self.check_permissions(user.permissions, repo_name):
927 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
927 log.debug('Permission to %s granted for user: %s @ %s', repo_name,
928 user, check_location)
928 user, check_location)
929 return True
929 return True
930
930
931 else:
931 else:
932 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
932 log.debug('Permission to %s denied for user: %s @ %s', repo_name,
933 user, check_location)
933 user, check_location)
934 return False
934 return False
935
935
936 def check_permissions(self, perm_defs, repo_name):
936 def check_permissions(self, perm_defs, repo_name):
937 """
937 """
938 implement in child class should return True if permissions are ok,
938 implement in child class should return True if permissions are ok,
939 False otherwise
939 False otherwise
940
940
941 :param perm_defs: dict with permission definitions
941 :param perm_defs: dict with permission definitions
942 :param repo_name: repo name
942 :param repo_name: repo name
943 """
943 """
944 raise NotImplementedError()
944 raise NotImplementedError()
945
945
946
946
947 class HasPermissionAllApi(_BaseApiPerm):
947 class HasPermissionAllApi(_BaseApiPerm):
948 def __call__(self, user, check_location=''):
948 def __call__(self, user, check_location=''):
949 return super(HasPermissionAllApi, self)\
949 return super(HasPermissionAllApi, self)\
950 .__call__(check_location=check_location, user=user)
950 .__call__(check_location=check_location, user=user)
951
951
952 def check_permissions(self, perm_defs, repo):
952 def check_permissions(self, perm_defs, repo):
953 if self.required_perms.issubset(perm_defs.get('global')):
953 if self.required_perms.issubset(perm_defs.get('global')):
954 return True
954 return True
955 return False
955 return False
956
956
957
957
958 class HasPermissionAnyApi(_BaseApiPerm):
958 class HasPermissionAnyApi(_BaseApiPerm):
959 def __call__(self, user, check_location=''):
959 def __call__(self, user, check_location=''):
960 return super(HasPermissionAnyApi, self)\
960 return super(HasPermissionAnyApi, self)\
961 .__call__(check_location=check_location, user=user)
961 .__call__(check_location=check_location, user=user)
962
962
963 def check_permissions(self, perm_defs, repo):
963 def check_permissions(self, perm_defs, repo):
964 if self.required_perms.intersection(perm_defs.get('global')):
964 if self.required_perms.intersection(perm_defs.get('global')):
965 return True
965 return True
966 return False
966 return False
967
967
968
968
969 class HasRepoPermissionAllApi(_BaseApiPerm):
969 class HasRepoPermissionAllApi(_BaseApiPerm):
970 def __call__(self, user, repo_name, check_location=''):
970 def __call__(self, user, repo_name, check_location=''):
971 return super(HasRepoPermissionAllApi, self)\
971 return super(HasRepoPermissionAllApi, self)\
972 .__call__(check_location=check_location, user=user,
972 .__call__(check_location=check_location, user=user,
973 repo_name=repo_name)
973 repo_name=repo_name)
974
974
975 def check_permissions(self, perm_defs, repo_name):
975 def check_permissions(self, perm_defs, repo_name):
976
976
977 try:
977 try:
978 self._user_perms = set(
978 self._user_perms = set(
979 [perm_defs['repositories'][repo_name]]
979 [perm_defs['repositories'][repo_name]]
980 )
980 )
981 except KeyError:
981 except KeyError:
982 log.warning(traceback.format_exc())
982 log.warning(traceback.format_exc())
983 return False
983 return False
984 if self.required_perms.issubset(self._user_perms):
984 if self.required_perms.issubset(self._user_perms):
985 return True
985 return True
986 return False
986 return False
987
987
988
988
989 class HasRepoPermissionAnyApi(_BaseApiPerm):
989 class HasRepoPermissionAnyApi(_BaseApiPerm):
990 def __call__(self, user, repo_name, check_location=''):
990 def __call__(self, user, repo_name, check_location=''):
991 return super(HasRepoPermissionAnyApi, self)\
991 return super(HasRepoPermissionAnyApi, self)\
992 .__call__(check_location=check_location, user=user,
992 .__call__(check_location=check_location, user=user,
993 repo_name=repo_name)
993 repo_name=repo_name)
994
994
995 def check_permissions(self, perm_defs, repo_name):
995 def check_permissions(self, perm_defs, repo_name):
996
996
997 try:
997 try:
998 _user_perms = set(
998 _user_perms = set(
999 [perm_defs['repositories'][repo_name]]
999 [perm_defs['repositories'][repo_name]]
1000 )
1000 )
1001 except KeyError:
1001 except KeyError:
1002 log.warning(traceback.format_exc())
1002 log.warning(traceback.format_exc())
1003 return False
1003 return False
1004 if self.required_perms.intersection(_user_perms):
1004 if self.required_perms.intersection(_user_perms):
1005 return True
1005 return True
1006 return False
1006 return False
1007
1007
1008
1008
1009 def check_ip_access(source_ip, allowed_ips=None):
1009 def check_ip_access(source_ip, allowed_ips=None):
1010 """
1010 """
1011 Checks if source_ip is a subnet of any of allowed_ips.
1011 Checks if source_ip is a subnet of any of allowed_ips.
1012
1012
1013 :param source_ip:
1013 :param source_ip:
1014 :param allowed_ips: list of allowed ips together with mask
1014 :param allowed_ips: list of allowed ips together with mask
1015 """
1015 """
1016 from rhodecode.lib import ipaddr
1016 from rhodecode.lib import ipaddr
1017 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1017 log.debug('checking if ip:%s is subnet of %s' % (source_ip, allowed_ips))
1018 if isinstance(allowed_ips, (tuple, list, set)):
1018 if isinstance(allowed_ips, (tuple, list, set)):
1019 for ip in allowed_ips:
1019 for ip in allowed_ips:
1020 try:
1020 try:
1021 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1021 if ipaddr.IPAddress(source_ip) in ipaddr.IPNetwork(ip):
1022 return True
1022 return True
1023 # for any case we cannot determine the IP, don't crash just
1023 # for any case we cannot determine the IP, don't crash just
1024 # skip it and log as error, we want to say forbidden still when
1024 # skip it and log as error, we want to say forbidden still when
1025 # sending bad IP
1025 # sending bad IP
1026 except Exception:
1026 except Exception:
1027 log.error(traceback.format_exc())
1027 log.error(traceback.format_exc())
1028 continue
1028 continue
1029 return False
1029 return False
@@ -1,1186 +1,1186 b''
1 """Helper functions
1 """Helper functions
2
2
3 Consists of functions to typically be used within templates, but also
3 Consists of functions to typically be used within templates, but also
4 available to Controllers. This module is available to both as 'h'.
4 available to Controllers. This module is available to both as 'h'.
5 """
5 """
6 import random
6 import random
7 import hashlib
7 import hashlib
8 import StringIO
8 import StringIO
9 import urllib
9 import urllib
10 import math
10 import math
11 import logging
11 import logging
12 import re
12 import re
13 import urlparse
13 import urlparse
14 import textwrap
14 import textwrap
15
15
16 from datetime import datetime
16 from datetime import datetime
17 from pygments.formatters.html import HtmlFormatter
17 from pygments.formatters.html import HtmlFormatter
18 from pygments import highlight as code_highlight
18 from pygments import highlight as code_highlight
19 from pylons import url, request, config
19 from pylons import url, request, config
20 from pylons.i18n.translation import _, ungettext
20 from pylons.i18n.translation import _, ungettext
21 from hashlib import md5
21 from hashlib import md5
22
22
23 from webhelpers.html import literal, HTML, escape
23 from webhelpers.html import literal, HTML, escape
24 from webhelpers.html.tools import *
24 from webhelpers.html.tools import *
25 from webhelpers.html.builder import make_tag
25 from webhelpers.html.builder import make_tag
26 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
26 from webhelpers.html.tags import auto_discovery_link, checkbox, css_classes, \
27 end_form, file, form, hidden, image, javascript_link, link_to, \
27 end_form, file, form, hidden, image, javascript_link, link_to, \
28 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
28 link_to_if, link_to_unless, ol, required_legend, select, stylesheet_link, \
29 submit, text, password, textarea, title, ul, xml_declaration, radio
29 submit, text, password, textarea, title, ul, xml_declaration, radio
30 from webhelpers.html.tools import auto_link, button_to, highlight, \
30 from webhelpers.html.tools import auto_link, button_to, highlight, \
31 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
31 js_obfuscate, mail_to, strip_links, strip_tags, tag_re
32 from webhelpers.number import format_byte_size, format_bit_size
32 from webhelpers.number import format_byte_size, format_bit_size
33 from webhelpers.pylonslib import Flash as _Flash
33 from webhelpers.pylonslib import Flash as _Flash
34 from webhelpers.pylonslib.secure_form import secure_form
34 from webhelpers.pylonslib.secure_form import secure_form
35 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
35 from webhelpers.text import chop_at, collapse, convert_accented_entities, \
36 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
36 convert_misc_entities, lchop, plural, rchop, remove_formatting, \
37 replace_whitespace, urlify, truncate, wrap_paragraphs
37 replace_whitespace, urlify, truncate, wrap_paragraphs
38 from webhelpers.date import time_ago_in_words
38 from webhelpers.date import time_ago_in_words
39 from webhelpers.paginate import Page
39 from webhelpers.paginate import Page
40 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
40 from webhelpers.html.tags import _set_input_attrs, _set_id_attr, \
41 convert_boolean_attrs, NotGiven, _make_safe_id_component
41 convert_boolean_attrs, NotGiven, _make_safe_id_component
42
42
43 from rhodecode.lib.annotate import annotate_highlight
43 from rhodecode.lib.annotate import annotate_highlight
44 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
44 from rhodecode.lib.utils import repo_name_slug, get_custom_lexer
45 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
45 from rhodecode.lib.utils2 import str2bool, safe_unicode, safe_str, \
46 get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict
46 get_changeset_safe, datetime_to_time, time_to_datetime, AttributeDict
47 from rhodecode.lib.markup_renderer import MarkupRenderer
47 from rhodecode.lib.markup_renderer import MarkupRenderer
48 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
48 from rhodecode.lib.vcs.exceptions import ChangesetDoesNotExistError
49 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
49 from rhodecode.lib.vcs.backends.base import BaseChangeset, EmptyChangeset
50 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
50 from rhodecode.config.conf import DATE_FORMAT, DATETIME_FORMAT
51 from rhodecode.model.changeset_status import ChangesetStatusModel
51 from rhodecode.model.changeset_status import ChangesetStatusModel
52 from rhodecode.model.db import URL_SEP, Permission
52 from rhodecode.model.db import URL_SEP, Permission
53
53
54 log = logging.getLogger(__name__)
54 log = logging.getLogger(__name__)
55
55
56
56
57 html_escape_table = {
57 html_escape_table = {
58 "&": "&amp;",
58 "&": "&amp;",
59 '"': "&quot;",
59 '"': "&quot;",
60 "'": "&apos;",
60 "'": "&apos;",
61 ">": "&gt;",
61 ">": "&gt;",
62 "<": "&lt;",
62 "<": "&lt;",
63 }
63 }
64
64
65
65
66 def html_escape(text):
66 def html_escape(text):
67 """Produce entities within text."""
67 """Produce entities within text."""
68 return "".join(html_escape_table.get(c, c) for c in text)
68 return "".join(html_escape_table.get(c, c) for c in text)
69
69
70
70
71 def shorter(text, size=20):
71 def shorter(text, size=20):
72 postfix = '...'
72 postfix = '...'
73 if len(text) > size:
73 if len(text) > size:
74 return text[:size - len(postfix)] + postfix
74 return text[:size - len(postfix)] + postfix
75 return text
75 return text
76
76
77
77
78 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
78 def _reset(name, value=None, id=NotGiven, type="reset", **attrs):
79 """
79 """
80 Reset button
80 Reset button
81 """
81 """
82 _set_input_attrs(attrs, type, name, value)
82 _set_input_attrs(attrs, type, name, value)
83 _set_id_attr(attrs, id, name)
83 _set_id_attr(attrs, id, name)
84 convert_boolean_attrs(attrs, ["disabled"])
84 convert_boolean_attrs(attrs, ["disabled"])
85 return HTML.input(**attrs)
85 return HTML.input(**attrs)
86
86
87 reset = _reset
87 reset = _reset
88 safeid = _make_safe_id_component
88 safeid = _make_safe_id_component
89
89
90
90
91 def FID(raw_id, path):
91 def FID(raw_id, path):
92 """
92 """
93 Creates a uniqe ID for filenode based on it's hash of path and revision
93 Creates a uniqe ID for filenode based on it's hash of path and revision
94 it's safe to use in urls
94 it's safe to use in urls
95
95
96 :param raw_id:
96 :param raw_id:
97 :param path:
97 :param path:
98 """
98 """
99
99
100 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
100 return 'C-%s-%s' % (short_id(raw_id), md5(safe_str(path)).hexdigest()[:12])
101
101
102
102
103 def get_token():
103 def get_token():
104 """Return the current authentication token, creating one if one doesn't
104 """Return the current authentication token, creating one if one doesn't
105 already exist.
105 already exist.
106 """
106 """
107 token_key = "_authentication_token"
107 token_key = "_authentication_token"
108 from pylons import session
108 from pylons import session
109 if not token_key in session:
109 if not token_key in session:
110 try:
110 try:
111 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
111 token = hashlib.sha1(str(random.getrandbits(128))).hexdigest()
112 except AttributeError: # Python < 2.4
112 except AttributeError: # Python < 2.4
113 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
113 token = hashlib.sha1(str(random.randrange(2 ** 128))).hexdigest()
114 session[token_key] = token
114 session[token_key] = token
115 if hasattr(session, 'save'):
115 if hasattr(session, 'save'):
116 session.save()
116 session.save()
117 return session[token_key]
117 return session[token_key]
118
118
119
119
120 class _GetError(object):
120 class _GetError(object):
121 """Get error from form_errors, and represent it as span wrapped error
121 """Get error from form_errors, and represent it as span wrapped error
122 message
122 message
123
123
124 :param field_name: field to fetch errors for
124 :param field_name: field to fetch errors for
125 :param form_errors: form errors dict
125 :param form_errors: form errors dict
126 """
126 """
127
127
128 def __call__(self, field_name, form_errors):
128 def __call__(self, field_name, form_errors):
129 tmpl = """<span class="error_msg">%s</span>"""
129 tmpl = """<span class="error_msg">%s</span>"""
130 if form_errors and field_name in form_errors:
130 if form_errors and field_name in form_errors:
131 return literal(tmpl % form_errors.get(field_name))
131 return literal(tmpl % form_errors.get(field_name))
132
132
133 get_error = _GetError()
133 get_error = _GetError()
134
134
135
135
136 class _ToolTip(object):
136 class _ToolTip(object):
137
137
138 def __call__(self, tooltip_title, trim_at=50):
138 def __call__(self, tooltip_title, trim_at=50):
139 """
139 """
140 Special function just to wrap our text into nice formatted
140 Special function just to wrap our text into nice formatted
141 autowrapped text
141 autowrapped text
142
142
143 :param tooltip_title:
143 :param tooltip_title:
144 """
144 """
145 tooltip_title = escape(tooltip_title)
145 tooltip_title = escape(tooltip_title)
146 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
146 tooltip_title = tooltip_title.replace('<', '&lt;').replace('>', '&gt;')
147 return tooltip_title
147 return tooltip_title
148 tooltip = _ToolTip()
148 tooltip = _ToolTip()
149
149
150
150
151 class _FilesBreadCrumbs(object):
151 class _FilesBreadCrumbs(object):
152
152
153 def __call__(self, repo_name, rev, paths):
153 def __call__(self, repo_name, rev, paths):
154 if isinstance(paths, str):
154 if isinstance(paths, str):
155 paths = safe_unicode(paths)
155 paths = safe_unicode(paths)
156 url_l = [link_to(repo_name, url('files_home',
156 url_l = [link_to(repo_name, url('files_home',
157 repo_name=repo_name,
157 repo_name=repo_name,
158 revision=rev, f_path=''),
158 revision=rev, f_path=''),
159 class_='ypjax-link')]
159 class_='ypjax-link')]
160 paths_l = paths.split('/')
160 paths_l = paths.split('/')
161 for cnt, p in enumerate(paths_l):
161 for cnt, p in enumerate(paths_l):
162 if p != '':
162 if p != '':
163 url_l.append(link_to(p,
163 url_l.append(link_to(p,
164 url('files_home',
164 url('files_home',
165 repo_name=repo_name,
165 repo_name=repo_name,
166 revision=rev,
166 revision=rev,
167 f_path='/'.join(paths_l[:cnt + 1])
167 f_path='/'.join(paths_l[:cnt + 1])
168 ),
168 ),
169 class_='ypjax-link'
169 class_='ypjax-link'
170 )
170 )
171 )
171 )
172
172
173 return literal('/'.join(url_l))
173 return literal('/'.join(url_l))
174
174
175 files_breadcrumbs = _FilesBreadCrumbs()
175 files_breadcrumbs = _FilesBreadCrumbs()
176
176
177
177
178 class CodeHtmlFormatter(HtmlFormatter):
178 class CodeHtmlFormatter(HtmlFormatter):
179 """
179 """
180 My code Html Formatter for source codes
180 My code Html Formatter for source codes
181 """
181 """
182
182
183 def wrap(self, source, outfile):
183 def wrap(self, source, outfile):
184 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
184 return self._wrap_div(self._wrap_pre(self._wrap_code(source)))
185
185
186 def _wrap_code(self, source):
186 def _wrap_code(self, source):
187 for cnt, it in enumerate(source):
187 for cnt, it in enumerate(source):
188 i, t = it
188 i, t = it
189 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
189 t = '<div id="L%s">%s</div>' % (cnt + 1, t)
190 yield i, t
190 yield i, t
191
191
192 def _wrap_tablelinenos(self, inner):
192 def _wrap_tablelinenos(self, inner):
193 dummyoutfile = StringIO.StringIO()
193 dummyoutfile = StringIO.StringIO()
194 lncount = 0
194 lncount = 0
195 for t, line in inner:
195 for t, line in inner:
196 if t:
196 if t:
197 lncount += 1
197 lncount += 1
198 dummyoutfile.write(line)
198 dummyoutfile.write(line)
199
199
200 fl = self.linenostart
200 fl = self.linenostart
201 mw = len(str(lncount + fl - 1))
201 mw = len(str(lncount + fl - 1))
202 sp = self.linenospecial
202 sp = self.linenospecial
203 st = self.linenostep
203 st = self.linenostep
204 la = self.lineanchors
204 la = self.lineanchors
205 aln = self.anchorlinenos
205 aln = self.anchorlinenos
206 nocls = self.noclasses
206 nocls = self.noclasses
207 if sp:
207 if sp:
208 lines = []
208 lines = []
209
209
210 for i in range(fl, fl + lncount):
210 for i in range(fl, fl + lncount):
211 if i % st == 0:
211 if i % st == 0:
212 if i % sp == 0:
212 if i % sp == 0:
213 if aln:
213 if aln:
214 lines.append('<a href="#%s%d" class="special">%*d</a>' %
214 lines.append('<a href="#%s%d" class="special">%*d</a>' %
215 (la, i, mw, i))
215 (la, i, mw, i))
216 else:
216 else:
217 lines.append('<span class="special">%*d</span>' % (mw, i))
217 lines.append('<span class="special">%*d</span>' % (mw, i))
218 else:
218 else:
219 if aln:
219 if aln:
220 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
220 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
221 else:
221 else:
222 lines.append('%*d' % (mw, i))
222 lines.append('%*d' % (mw, i))
223 else:
223 else:
224 lines.append('')
224 lines.append('')
225 ls = '\n'.join(lines)
225 ls = '\n'.join(lines)
226 else:
226 else:
227 lines = []
227 lines = []
228 for i in range(fl, fl + lncount):
228 for i in range(fl, fl + lncount):
229 if i % st == 0:
229 if i % st == 0:
230 if aln:
230 if aln:
231 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
231 lines.append('<a href="#%s%d">%*d</a>' % (la, i, mw, i))
232 else:
232 else:
233 lines.append('%*d' % (mw, i))
233 lines.append('%*d' % (mw, i))
234 else:
234 else:
235 lines.append('')
235 lines.append('')
236 ls = '\n'.join(lines)
236 ls = '\n'.join(lines)
237
237
238 # in case you wonder about the seemingly redundant <div> here: since the
238 # in case you wonder about the seemingly redundant <div> here: since the
239 # content in the other cell also is wrapped in a div, some browsers in
239 # content in the other cell also is wrapped in a div, some browsers in
240 # some configurations seem to mess up the formatting...
240 # some configurations seem to mess up the formatting...
241 if nocls:
241 if nocls:
242 yield 0, ('<table class="%stable">' % self.cssclass +
242 yield 0, ('<table class="%stable">' % self.cssclass +
243 '<tr><td><div class="linenodiv" '
243 '<tr><td><div class="linenodiv" '
244 'style="background-color: #f0f0f0; padding-right: 10px">'
244 'style="background-color: #f0f0f0; padding-right: 10px">'
245 '<pre style="line-height: 125%">' +
245 '<pre style="line-height: 125%">' +
246 ls + '</pre></div></td><td id="hlcode" class="code">')
246 ls + '</pre></div></td><td id="hlcode" class="code">')
247 else:
247 else:
248 yield 0, ('<table class="%stable">' % self.cssclass +
248 yield 0, ('<table class="%stable">' % self.cssclass +
249 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
249 '<tr><td class="linenos"><div class="linenodiv"><pre>' +
250 ls + '</pre></div></td><td id="hlcode" class="code">')
250 ls + '</pre></div></td><td id="hlcode" class="code">')
251 yield 0, dummyoutfile.getvalue()
251 yield 0, dummyoutfile.getvalue()
252 yield 0, '</td></tr></table>'
252 yield 0, '</td></tr></table>'
253
253
254
254
255 def pygmentize(filenode, **kwargs):
255 def pygmentize(filenode, **kwargs):
256 """
256 """
257 pygmentize function using pygments
257 pygmentize function using pygments
258
258
259 :param filenode:
259 :param filenode:
260 """
260 """
261 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
261 lexer = get_custom_lexer(filenode.extension) or filenode.lexer
262 return literal(code_highlight(filenode.content, lexer,
262 return literal(code_highlight(filenode.content, lexer,
263 CodeHtmlFormatter(**kwargs)))
263 CodeHtmlFormatter(**kwargs)))
264
264
265
265
266 def pygmentize_annotation(repo_name, filenode, **kwargs):
266 def pygmentize_annotation(repo_name, filenode, **kwargs):
267 """
267 """
268 pygmentize function for annotation
268 pygmentize function for annotation
269
269
270 :param filenode:
270 :param filenode:
271 """
271 """
272
272
273 color_dict = {}
273 color_dict = {}
274
274
275 def gen_color(n=10000):
275 def gen_color(n=10000):
276 """generator for getting n of evenly distributed colors using
276 """generator for getting n of evenly distributed colors using
277 hsv color and golden ratio. It always return same order of colors
277 hsv color and golden ratio. It always return same order of colors
278
278
279 :returns: RGB tuple
279 :returns: RGB tuple
280 """
280 """
281
281
282 def hsv_to_rgb(h, s, v):
282 def hsv_to_rgb(h, s, v):
283 if s == 0.0:
283 if s == 0.0:
284 return v, v, v
284 return v, v, v
285 i = int(h * 6.0) # XXX assume int() truncates!
285 i = int(h * 6.0) # XXX assume int() truncates!
286 f = (h * 6.0) - i
286 f = (h * 6.0) - i
287 p = v * (1.0 - s)
287 p = v * (1.0 - s)
288 q = v * (1.0 - s * f)
288 q = v * (1.0 - s * f)
289 t = v * (1.0 - s * (1.0 - f))
289 t = v * (1.0 - s * (1.0 - f))
290 i = i % 6
290 i = i % 6
291 if i == 0:
291 if i == 0:
292 return v, t, p
292 return v, t, p
293 if i == 1:
293 if i == 1:
294 return q, v, p
294 return q, v, p
295 if i == 2:
295 if i == 2:
296 return p, v, t
296 return p, v, t
297 if i == 3:
297 if i == 3:
298 return p, q, v
298 return p, q, v
299 if i == 4:
299 if i == 4:
300 return t, p, v
300 return t, p, v
301 if i == 5:
301 if i == 5:
302 return v, p, q
302 return v, p, q
303
303
304 golden_ratio = 0.618033988749895
304 golden_ratio = 0.618033988749895
305 h = 0.22717784590367374
305 h = 0.22717784590367374
306
306
307 for _ in xrange(n):
307 for _ in xrange(n):
308 h += golden_ratio
308 h += golden_ratio
309 h %= 1
309 h %= 1
310 HSV_tuple = [h, 0.95, 0.95]
310 HSV_tuple = [h, 0.95, 0.95]
311 RGB_tuple = hsv_to_rgb(*HSV_tuple)
311 RGB_tuple = hsv_to_rgb(*HSV_tuple)
312 yield map(lambda x: str(int(x * 256)), RGB_tuple)
312 yield map(lambda x: str(int(x * 256)), RGB_tuple)
313
313
314 cgenerator = gen_color()
314 cgenerator = gen_color()
315
315
316 def get_color_string(cs):
316 def get_color_string(cs):
317 if cs in color_dict:
317 if cs in color_dict:
318 col = color_dict[cs]
318 col = color_dict[cs]
319 else:
319 else:
320 col = color_dict[cs] = cgenerator.next()
320 col = color_dict[cs] = cgenerator.next()
321 return "color: rgb(%s)! important;" % (', '.join(col))
321 return "color: rgb(%s)! important;" % (', '.join(col))
322
322
323 def url_func(repo_name):
323 def url_func(repo_name):
324
324
325 def _url_func(changeset):
325 def _url_func(changeset):
326 author = changeset.author
326 author = changeset.author
327 date = changeset.date
327 date = changeset.date
328 message = tooltip(changeset.message)
328 message = tooltip(changeset.message)
329
329
330 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
330 tooltip_html = ("<div style='font-size:0.8em'><b>Author:</b>"
331 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
331 " %s<br/><b>Date:</b> %s</b><br/><b>Message:"
332 "</b> %s<br/></div>")
332 "</b> %s<br/></div>")
333
333
334 tooltip_html = tooltip_html % (author, date, message)
334 tooltip_html = tooltip_html % (author, date, message)
335 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
335 lnk_format = '%5s:%s' % ('r%s' % changeset.revision,
336 short_id(changeset.raw_id))
336 short_id(changeset.raw_id))
337 uri = link_to(
337 uri = link_to(
338 lnk_format,
338 lnk_format,
339 url('changeset_home', repo_name=repo_name,
339 url('changeset_home', repo_name=repo_name,
340 revision=changeset.raw_id),
340 revision=changeset.raw_id),
341 style=get_color_string(changeset.raw_id),
341 style=get_color_string(changeset.raw_id),
342 class_='tooltip',
342 class_='tooltip',
343 title=tooltip_html
343 title=tooltip_html
344 )
344 )
345
345
346 uri += '\n'
346 uri += '\n'
347 return uri
347 return uri
348 return _url_func
348 return _url_func
349
349
350 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
350 return literal(annotate_highlight(filenode, url_func(repo_name), **kwargs))
351
351
352
352
353 def is_following_repo(repo_name, user_id):
353 def is_following_repo(repo_name, user_id):
354 from rhodecode.model.scm import ScmModel
354 from rhodecode.model.scm import ScmModel
355 return ScmModel().is_following_repo(repo_name, user_id)
355 return ScmModel().is_following_repo(repo_name, user_id)
356
356
357 flash = _Flash()
357 flash = _Flash()
358
358
359 #==============================================================================
359 #==============================================================================
360 # SCM FILTERS available via h.
360 # SCM FILTERS available via h.
361 #==============================================================================
361 #==============================================================================
362 from rhodecode.lib.vcs.utils import author_name, author_email
362 from rhodecode.lib.vcs.utils import author_name, author_email
363 from rhodecode.lib.utils2 import credentials_filter, age as _age
363 from rhodecode.lib.utils2 import credentials_filter, age as _age
364 from rhodecode.model.db import User, ChangesetStatus
364 from rhodecode.model.db import User, ChangesetStatus
365
365
366 age = lambda x: _age(x)
366 age = lambda x: _age(x)
367 capitalize = lambda x: x.capitalize()
367 capitalize = lambda x: x.capitalize()
368 email = author_email
368 email = author_email
369 short_id = lambda x: x[:12]
369 short_id = lambda x: x[:12]
370 hide_credentials = lambda x: ''.join(credentials_filter(x))
370 hide_credentials = lambda x: ''.join(credentials_filter(x))
371
371
372
372
373 def fmt_date(date):
373 def fmt_date(date):
374 if date:
374 if date:
375 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
375 _fmt = _(u"%a, %d %b %Y %H:%M:%S").encode('utf8')
376 return date.strftime(_fmt).decode('utf8')
376 return date.strftime(_fmt).decode('utf8')
377
377
378 return ""
378 return ""
379
379
380
380
381 def is_git(repository):
381 def is_git(repository):
382 if hasattr(repository, 'alias'):
382 if hasattr(repository, 'alias'):
383 _type = repository.alias
383 _type = repository.alias
384 elif hasattr(repository, 'repo_type'):
384 elif hasattr(repository, 'repo_type'):
385 _type = repository.repo_type
385 _type = repository.repo_type
386 else:
386 else:
387 _type = repository
387 _type = repository
388 return _type == 'git'
388 return _type == 'git'
389
389
390
390
391 def is_hg(repository):
391 def is_hg(repository):
392 if hasattr(repository, 'alias'):
392 if hasattr(repository, 'alias'):
393 _type = repository.alias
393 _type = repository.alias
394 elif hasattr(repository, 'repo_type'):
394 elif hasattr(repository, 'repo_type'):
395 _type = repository.repo_type
395 _type = repository.repo_type
396 else:
396 else:
397 _type = repository
397 _type = repository
398 return _type == 'hg'
398 return _type == 'hg'
399
399
400
400
401 def email_or_none(author):
401 def email_or_none(author):
402 # extract email from the commit string
402 # extract email from the commit string
403 _email = email(author)
403 _email = email(author)
404 if _email != '':
404 if _email != '':
405 # check it against RhodeCode database, and use the MAIN email for this
405 # check it against RhodeCode database, and use the MAIN email for this
406 # user
406 # user
407 user = User.get_by_email(_email, case_insensitive=True, cache=True)
407 user = User.get_by_email(_email, case_insensitive=True, cache=True)
408 if user is not None:
408 if user is not None:
409 return user.email
409 return user.email
410 return _email
410 return _email
411
411
412 # See if it contains a username we can get an email from
412 # See if it contains a username we can get an email from
413 user = User.get_by_username(author_name(author), case_insensitive=True,
413 user = User.get_by_username(author_name(author), case_insensitive=True,
414 cache=True)
414 cache=True)
415 if user is not None:
415 if user is not None:
416 return user.email
416 return user.email
417
417
418 # No valid email, not a valid user in the system, none!
418 # No valid email, not a valid user in the system, none!
419 return None
419 return None
420
420
421
421
422 def person(author, show_attr="username_and_name"):
422 def person(author, show_attr="username_and_name"):
423 # attr to return from fetched user
423 # attr to return from fetched user
424 person_getter = lambda usr: getattr(usr, show_attr)
424 person_getter = lambda usr: getattr(usr, show_attr)
425
425
426 # Valid email in the attribute passed, see if they're in the system
426 # Valid email in the attribute passed, see if they're in the system
427 _email = email(author)
427 _email = email(author)
428 if _email != '':
428 if _email != '':
429 user = User.get_by_email(_email, case_insensitive=True, cache=True)
429 user = User.get_by_email(_email, case_insensitive=True, cache=True)
430 if user is not None:
430 if user is not None:
431 return person_getter(user)
431 return person_getter(user)
432 return _email
432 return _email
433
433
434 # Maybe it's a username?
434 # Maybe it's a username?
435 _author = author_name(author)
435 _author = author_name(author)
436 user = User.get_by_username(_author, case_insensitive=True,
436 user = User.get_by_username(_author, case_insensitive=True,
437 cache=True)
437 cache=True)
438 if user is not None:
438 if user is not None:
439 return person_getter(user)
439 return person_getter(user)
440
440
441 # Still nothing? Just pass back the author name then
441 # Still nothing? Just pass back the author name then
442 return _author
442 return _author
443
443
444
444
445 def person_by_id(id_, show_attr="username_and_name"):
445 def person_by_id(id_, show_attr="username_and_name"):
446 # attr to return from fetched user
446 # attr to return from fetched user
447 person_getter = lambda usr: getattr(usr, show_attr)
447 person_getter = lambda usr: getattr(usr, show_attr)
448
448
449 #maybe it's an ID ?
449 #maybe it's an ID ?
450 if str(id_).isdigit() or isinstance(id_, int):
450 if str(id_).isdigit() or isinstance(id_, int):
451 id_ = int(id_)
451 id_ = int(id_)
452 user = User.get(id_)
452 user = User.get(id_)
453 if user is not None:
453 if user is not None:
454 return person_getter(user)
454 return person_getter(user)
455 return id_
455 return id_
456
456
457
457
458 def desc_stylize(value):
458 def desc_stylize(value):
459 """
459 """
460 converts tags from value into html equivalent
460 converts tags from value into html equivalent
461
461
462 :param value:
462 :param value:
463 """
463 """
464 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
464 value = re.sub(r'\[see\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
465 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
465 '<div class="metatag" tag="see">see =&gt; \\1 </div>', value)
466 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
466 value = re.sub(r'\[license\ \=\>\ *([a-zA-Z0-9\/\=\?\&\ \:\/\.\-]*)\]',
467 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
467 '<div class="metatag" tag="license"><a href="http:\/\/www.opensource.org/licenses/\\1">\\1</a></div>', value)
468 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
468 value = re.sub(r'\[(requires|recommends|conflicts|base)\ \=\>\ *([a-zA-Z0-9\-\/]*)\]',
469 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
469 '<div class="metatag" tag="\\1">\\1 =&gt; <a href="/\\2">\\2</a></div>', value)
470 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
470 value = re.sub(r'\[(lang|language)\ \=\>\ *([a-zA-Z\-\/\#\+]*)\]',
471 '<div class="metatag" tag="lang">\\2</div>', value)
471 '<div class="metatag" tag="lang">\\2</div>', value)
472 value = re.sub(r'\[([a-z]+)\]',
472 value = re.sub(r'\[([a-z]+)\]',
473 '<div class="metatag" tag="\\1">\\1</div>', value)
473 '<div class="metatag" tag="\\1">\\1</div>', value)
474
474
475 return value
475 return value
476
476
477
477
478 def bool2icon(value):
478 def bool2icon(value):
479 """Returns True/False values represented as small html image of true/false
479 """Returns True/False values represented as small html image of true/false
480 icons
480 icons
481
481
482 :param value: bool value
482 :param value: bool value
483 """
483 """
484
484
485 if value is True:
485 if value is True:
486 return HTML.tag('img', src=url("/images/icons/accept.png"),
486 return HTML.tag('img', src=url("/images/icons/accept.png"),
487 alt=_('True'))
487 alt=_('True'))
488
488
489 if value is False:
489 if value is False:
490 return HTML.tag('img', src=url("/images/icons/cancel.png"),
490 return HTML.tag('img', src=url("/images/icons/cancel.png"),
491 alt=_('False'))
491 alt=_('False'))
492
492
493 return value
493 return value
494
494
495
495
496 def action_parser(user_log, feed=False, parse_cs=False):
496 def action_parser(user_log, feed=False, parse_cs=False):
497 """
497 """
498 This helper will action_map the specified string action into translated
498 This helper will action_map the specified string action into translated
499 fancy names with icons and links
499 fancy names with icons and links
500
500
501 :param user_log: user log instance
501 :param user_log: user log instance
502 :param feed: use output for feeds (no html and fancy icons)
502 :param feed: use output for feeds (no html and fancy icons)
503 :param parse_cs: parse Changesets into VCS instances
503 :param parse_cs: parse Changesets into VCS instances
504 """
504 """
505
505
506 action = user_log.action
506 action = user_log.action
507 action_params = ' '
507 action_params = ' '
508
508
509 x = action.split(':')
509 x = action.split(':')
510
510
511 if len(x) > 1:
511 if len(x) > 1:
512 action, action_params = x
512 action, action_params = x
513
513
514 def get_cs_links():
514 def get_cs_links():
515 revs_limit = 3 # display this amount always
515 revs_limit = 3 # display this amount always
516 revs_top_limit = 50 # show upto this amount of changesets hidden
516 revs_top_limit = 50 # show upto this amount of changesets hidden
517 revs_ids = action_params.split(',')
517 revs_ids = action_params.split(',')
518 deleted = user_log.repository is None
518 deleted = user_log.repository is None
519 if deleted:
519 if deleted:
520 return ','.join(revs_ids)
520 return ','.join(revs_ids)
521
521
522 repo_name = user_log.repository.repo_name
522 repo_name = user_log.repository.repo_name
523
523
524 def lnk(rev, repo_name):
524 def lnk(rev, repo_name):
525 if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict):
525 if isinstance(rev, BaseChangeset) or isinstance(rev, AttributeDict):
526 lazy_cs = True
526 lazy_cs = True
527 if getattr(rev, 'op', None) and getattr(rev, 'ref_name', None):
527 if getattr(rev, 'op', None) and getattr(rev, 'ref_name', None):
528 lazy_cs = False
528 lazy_cs = False
529 lbl = '?'
529 lbl = '?'
530 if rev.op == 'delete_branch':
530 if rev.op == 'delete_branch':
531 lbl = '%s' % _('Deleted branch: %s') % rev.ref_name
531 lbl = '%s' % _('Deleted branch: %s') % rev.ref_name
532 title = ''
532 title = ''
533 elif rev.op == 'tag':
533 elif rev.op == 'tag':
534 lbl = '%s' % _('Created tag: %s') % rev.ref_name
534 lbl = '%s' % _('Created tag: %s') % rev.ref_name
535 title = ''
535 title = ''
536 _url = '#'
536 _url = '#'
537
537
538 else:
538 else:
539 lbl = '%s' % (rev.short_id[:8])
539 lbl = '%s' % (rev.short_id[:8])
540 _url = url('changeset_home', repo_name=repo_name,
540 _url = url('changeset_home', repo_name=repo_name,
541 revision=rev.raw_id)
541 revision=rev.raw_id)
542 title = tooltip(rev.message)
542 title = tooltip(rev.message)
543 else:
543 else:
544 ## changeset cannot be found/striped/removed etc.
544 ## changeset cannot be found/striped/removed etc.
545 lbl = ('%s' % rev)[:12]
545 lbl = ('%s' % rev)[:12]
546 _url = '#'
546 _url = '#'
547 title = _('Changeset not found')
547 title = _('Changeset not found')
548 if parse_cs:
548 if parse_cs:
549 return link_to(lbl, _url, title=title, class_='tooltip')
549 return link_to(lbl, _url, title=title, class_='tooltip')
550 return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name,
550 return link_to(lbl, _url, raw_id=rev.raw_id, repo_name=repo_name,
551 class_='lazy-cs' if lazy_cs else '')
551 class_='lazy-cs' if lazy_cs else '')
552
552
553 revs = []
553 revs = []
554 if len(filter(lambda v: v != '', revs_ids)) > 0:
554 if len(filter(lambda v: v != '', revs_ids)) > 0:
555 repo = None
555 repo = None
556 for rev in revs_ids[:revs_top_limit]:
556 for rev in revs_ids[:revs_top_limit]:
557 _op = _name = None
557 _op = _name = None
558 if len(rev.split('=>')) == 2:
558 if len(rev.split('=>')) == 2:
559 _op, _name = rev.split('=>')
559 _op, _name = rev.split('=>')
560
560
561 # we want parsed changesets, or new log store format is bad
561 # we want parsed changesets, or new log store format is bad
562 if parse_cs:
562 if parse_cs:
563 try:
563 try:
564 if repo is None:
564 if repo is None:
565 repo = user_log.repository.scm_instance
565 repo = user_log.repository.scm_instance
566 _rev = repo.get_changeset(rev)
566 _rev = repo.get_changeset(rev)
567 revs.append(_rev)
567 revs.append(_rev)
568 except ChangesetDoesNotExistError:
568 except ChangesetDoesNotExistError:
569 log.error('cannot find revision %s in this repo' % rev)
569 log.error('cannot find revision %s in this repo' % rev)
570 revs.append(rev)
570 revs.append(rev)
571 continue
571 continue
572 else:
572 else:
573 _rev = AttributeDict({
573 _rev = AttributeDict({
574 'short_id': rev[:12],
574 'short_id': rev[:12],
575 'raw_id': rev,
575 'raw_id': rev,
576 'message': '',
576 'message': '',
577 'op': _op,
577 'op': _op,
578 'ref_name': _name
578 'ref_name': _name
579 })
579 })
580 revs.append(_rev)
580 revs.append(_rev)
581 cs_links = []
581 cs_links = []
582 cs_links.append(" " + ', '.join(
582 cs_links.append(" " + ', '.join(
583 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
583 [lnk(rev, repo_name) for rev in revs[:revs_limit]]
584 )
584 )
585 )
585 )
586
586
587 compare_view = (
587 compare_view = (
588 ' <div class="compare_view tooltip" title="%s">'
588 ' <div class="compare_view tooltip" title="%s">'
589 '<a href="%s">%s</a> </div>' % (
589 '<a href="%s">%s</a> </div>' % (
590 _('Show all combined changesets %s->%s') % (
590 _('Show all combined changesets %s->%s') % (
591 revs_ids[0][:12], revs_ids[-1][:12]
591 revs_ids[0][:12], revs_ids[-1][:12]
592 ),
592 ),
593 url('changeset_home', repo_name=repo_name,
593 url('changeset_home', repo_name=repo_name,
594 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
594 revision='%s...%s' % (revs_ids[0], revs_ids[-1])
595 ),
595 ),
596 _('compare view')
596 _('compare view')
597 )
597 )
598 )
598 )
599
599
600 # if we have exactly one more than normally displayed
600 # if we have exactly one more than normally displayed
601 # just display it, takes less space than displaying
601 # just display it, takes less space than displaying
602 # "and 1 more revisions"
602 # "and 1 more revisions"
603 if len(revs_ids) == revs_limit + 1:
603 if len(revs_ids) == revs_limit + 1:
604 rev = revs[revs_limit]
604 rev = revs[revs_limit]
605 cs_links.append(", " + lnk(rev, repo_name))
605 cs_links.append(", " + lnk(rev, repo_name))
606
606
607 # hidden-by-default ones
607 # hidden-by-default ones
608 if len(revs_ids) > revs_limit + 1:
608 if len(revs_ids) > revs_limit + 1:
609 uniq_id = revs_ids[0]
609 uniq_id = revs_ids[0]
610 html_tmpl = (
610 html_tmpl = (
611 '<span> %s <a class="show_more" id="_%s" '
611 '<span> %s <a class="show_more" id="_%s" '
612 'href="#more">%s</a> %s</span>'
612 'href="#more">%s</a> %s</span>'
613 )
613 )
614 if not feed:
614 if not feed:
615 cs_links.append(html_tmpl % (
615 cs_links.append(html_tmpl % (
616 _('and'),
616 _('and'),
617 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
617 uniq_id, _('%s more') % (len(revs_ids) - revs_limit),
618 _('revisions')
618 _('revisions')
619 )
619 )
620 )
620 )
621
621
622 if not feed:
622 if not feed:
623 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
623 html_tmpl = '<span id="%s" style="display:none">, %s </span>'
624 else:
624 else:
625 html_tmpl = '<span id="%s"> %s </span>'
625 html_tmpl = '<span id="%s"> %s </span>'
626
626
627 morelinks = ', '.join(
627 morelinks = ', '.join(
628 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
628 [lnk(rev, repo_name) for rev in revs[revs_limit:]]
629 )
629 )
630
630
631 if len(revs_ids) > revs_top_limit:
631 if len(revs_ids) > revs_top_limit:
632 morelinks += ', ...'
632 morelinks += ', ...'
633
633
634 cs_links.append(html_tmpl % (uniq_id, morelinks))
634 cs_links.append(html_tmpl % (uniq_id, morelinks))
635 if len(revs) > 1:
635 if len(revs) > 1:
636 cs_links.append(compare_view)
636 cs_links.append(compare_view)
637 return ''.join(cs_links)
637 return ''.join(cs_links)
638
638
639 def get_fork_name():
639 def get_fork_name():
640 repo_name = action_params
640 repo_name = action_params
641 _url = url('summary_home', repo_name=repo_name)
641 _url = url('summary_home', repo_name=repo_name)
642 return _('fork name %s') % link_to(action_params, _url)
642 return _('fork name %s') % link_to(action_params, _url)
643
643
644 def get_user_name():
644 def get_user_name():
645 user_name = action_params
645 user_name = action_params
646 return user_name
646 return user_name
647
647
648 def get_users_group():
648 def get_users_group():
649 group_name = action_params
649 group_name = action_params
650 return group_name
650 return group_name
651
651
652 def get_pull_request():
652 def get_pull_request():
653 pull_request_id = action_params
653 pull_request_id = action_params
654 deleted = user_log.repository is None
654 deleted = user_log.repository is None
655 if deleted:
655 if deleted:
656 repo_name = user_log.repository_name
656 repo_name = user_log.repository_name
657 else:
657 else:
658 repo_name = user_log.repository.repo_name
658 repo_name = user_log.repository.repo_name
659 return link_to(_('Pull request #%s') % pull_request_id,
659 return link_to(_('Pull request #%s') % pull_request_id,
660 url('pullrequest_show', repo_name=repo_name,
660 url('pullrequest_show', repo_name=repo_name,
661 pull_request_id=pull_request_id))
661 pull_request_id=pull_request_id))
662
662
663 # action : translated str, callback(extractor), icon
663 # action : translated str, callback(extractor), icon
664 action_map = {
664 action_map = {
665 'user_deleted_repo': (_('[deleted] repository'),
665 'user_deleted_repo': (_('[deleted] repository'),
666 None, 'database_delete.png'),
666 None, 'database_delete.png'),
667 'user_created_repo': (_('[created] repository'),
667 'user_created_repo': (_('[created] repository'),
668 None, 'database_add.png'),
668 None, 'database_add.png'),
669 'user_created_fork': (_('[created] repository as fork'),
669 'user_created_fork': (_('[created] repository as fork'),
670 None, 'arrow_divide.png'),
670 None, 'arrow_divide.png'),
671 'user_forked_repo': (_('[forked] repository'),
671 'user_forked_repo': (_('[forked] repository'),
672 get_fork_name, 'arrow_divide.png'),
672 get_fork_name, 'arrow_divide.png'),
673 'user_updated_repo': (_('[updated] repository'),
673 'user_updated_repo': (_('[updated] repository'),
674 None, 'database_edit.png'),
674 None, 'database_edit.png'),
675 'admin_deleted_repo': (_('[delete] repository'),
675 'admin_deleted_repo': (_('[delete] repository'),
676 None, 'database_delete.png'),
676 None, 'database_delete.png'),
677 'admin_created_repo': (_('[created] repository'),
677 'admin_created_repo': (_('[created] repository'),
678 None, 'database_add.png'),
678 None, 'database_add.png'),
679 'admin_forked_repo': (_('[forked] repository'),
679 'admin_forked_repo': (_('[forked] repository'),
680 None, 'arrow_divide.png'),
680 None, 'arrow_divide.png'),
681 'admin_updated_repo': (_('[updated] repository'),
681 'admin_updated_repo': (_('[updated] repository'),
682 None, 'database_edit.png'),
682 None, 'database_edit.png'),
683 'admin_created_user': (_('[created] user'),
683 'admin_created_user': (_('[created] user'),
684 get_user_name, 'user_add.png'),
684 get_user_name, 'user_add.png'),
685 'admin_updated_user': (_('[updated] user'),
685 'admin_updated_user': (_('[updated] user'),
686 get_user_name, 'user_edit.png'),
686 get_user_name, 'user_edit.png'),
687 'admin_created_users_group': (_('[created] users group'),
687 'admin_created_users_group': (_('[created] user group'),
688 get_users_group, 'group_add.png'),
688 get_users_group, 'group_add.png'),
689 'admin_updated_users_group': (_('[updated] users group'),
689 'admin_updated_users_group': (_('[updated] user group'),
690 get_users_group, 'group_edit.png'),
690 get_users_group, 'group_edit.png'),
691 'user_commented_revision': (_('[commented] on revision in repository'),
691 'user_commented_revision': (_('[commented] on revision in repository'),
692 get_cs_links, 'comment_add.png'),
692 get_cs_links, 'comment_add.png'),
693 'user_commented_pull_request': (_('[commented] on pull request for'),
693 'user_commented_pull_request': (_('[commented] on pull request for'),
694 get_pull_request, 'comment_add.png'),
694 get_pull_request, 'comment_add.png'),
695 'user_closed_pull_request': (_('[closed] pull request for'),
695 'user_closed_pull_request': (_('[closed] pull request for'),
696 get_pull_request, 'tick.png'),
696 get_pull_request, 'tick.png'),
697 'push': (_('[pushed] into'),
697 'push': (_('[pushed] into'),
698 get_cs_links, 'script_add.png'),
698 get_cs_links, 'script_add.png'),
699 'push_local': (_('[committed via RhodeCode] into repository'),
699 'push_local': (_('[committed via RhodeCode] into repository'),
700 get_cs_links, 'script_edit.png'),
700 get_cs_links, 'script_edit.png'),
701 'push_remote': (_('[pulled from remote] into repository'),
701 'push_remote': (_('[pulled from remote] into repository'),
702 get_cs_links, 'connect.png'),
702 get_cs_links, 'connect.png'),
703 'pull': (_('[pulled] from'),
703 'pull': (_('[pulled] from'),
704 None, 'down_16.png'),
704 None, 'down_16.png'),
705 'started_following_repo': (_('[started following] repository'),
705 'started_following_repo': (_('[started following] repository'),
706 None, 'heart_add.png'),
706 None, 'heart_add.png'),
707 'stopped_following_repo': (_('[stopped following] repository'),
707 'stopped_following_repo': (_('[stopped following] repository'),
708 None, 'heart_delete.png'),
708 None, 'heart_delete.png'),
709 }
709 }
710
710
711 action_str = action_map.get(action, action)
711 action_str = action_map.get(action, action)
712 if feed:
712 if feed:
713 action = action_str[0].replace('[', '').replace(']', '')
713 action = action_str[0].replace('[', '').replace(']', '')
714 else:
714 else:
715 action = action_str[0]\
715 action = action_str[0]\
716 .replace('[', '<span class="journal_highlight">')\
716 .replace('[', '<span class="journal_highlight">')\
717 .replace(']', '</span>')
717 .replace(']', '</span>')
718
718
719 action_params_func = lambda: ""
719 action_params_func = lambda: ""
720
720
721 if callable(action_str[1]):
721 if callable(action_str[1]):
722 action_params_func = action_str[1]
722 action_params_func = action_str[1]
723
723
724 def action_parser_icon():
724 def action_parser_icon():
725 action = user_log.action
725 action = user_log.action
726 action_params = None
726 action_params = None
727 x = action.split(':')
727 x = action.split(':')
728
728
729 if len(x) > 1:
729 if len(x) > 1:
730 action, action_params = x
730 action, action_params = x
731
731
732 tmpl = """<img src="%s%s" alt="%s"/>"""
732 tmpl = """<img src="%s%s" alt="%s"/>"""
733 ico = action_map.get(action, ['', '', ''])[2]
733 ico = action_map.get(action, ['', '', ''])[2]
734 return literal(tmpl % ((url('/images/icons/')), ico, action))
734 return literal(tmpl % ((url('/images/icons/')), ico, action))
735
735
736 # returned callbacks we need to call to get
736 # returned callbacks we need to call to get
737 return [lambda: literal(action), action_params_func, action_parser_icon]
737 return [lambda: literal(action), action_params_func, action_parser_icon]
738
738
739
739
740
740
741 #==============================================================================
741 #==============================================================================
742 # PERMS
742 # PERMS
743 #==============================================================================
743 #==============================================================================
744 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
744 from rhodecode.lib.auth import HasPermissionAny, HasPermissionAll, \
745 HasRepoPermissionAny, HasRepoPermissionAll, HasReposGroupPermissionAll, \
745 HasRepoPermissionAny, HasRepoPermissionAll, HasReposGroupPermissionAll, \
746 HasReposGroupPermissionAny
746 HasReposGroupPermissionAny
747
747
748
748
749 #==============================================================================
749 #==============================================================================
750 # GRAVATAR URL
750 # GRAVATAR URL
751 #==============================================================================
751 #==============================================================================
752
752
753 def gravatar_url(email_address, size=30):
753 def gravatar_url(email_address, size=30):
754 from pylons import url # doh, we need to re-import url to mock it later
754 from pylons import url # doh, we need to re-import url to mock it later
755 _def = 'anonymous@rhodecode.org'
755 _def = 'anonymous@rhodecode.org'
756 use_gravatar = str2bool(config['app_conf'].get('use_gravatar'))
756 use_gravatar = str2bool(config['app_conf'].get('use_gravatar'))
757 email_address = email_address or _def
757 email_address = email_address or _def
758 if (not use_gravatar or not email_address or email_address == _def):
758 if (not use_gravatar or not email_address or email_address == _def):
759 f = lambda a, l: min(l, key=lambda x: abs(x - a))
759 f = lambda a, l: min(l, key=lambda x: abs(x - a))
760 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
760 return url("/images/user%s.png" % f(size, [14, 16, 20, 24, 30]))
761
761
762 if use_gravatar and config['app_conf'].get('alternative_gravatar_url'):
762 if use_gravatar and config['app_conf'].get('alternative_gravatar_url'):
763 tmpl = config['app_conf'].get('alternative_gravatar_url', '')
763 tmpl = config['app_conf'].get('alternative_gravatar_url', '')
764 parsed_url = urlparse.urlparse(url.current(qualified=True))
764 parsed_url = urlparse.urlparse(url.current(qualified=True))
765 tmpl = tmpl.replace('{email}', email_address)\
765 tmpl = tmpl.replace('{email}', email_address)\
766 .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest()) \
766 .replace('{md5email}', hashlib.md5(email_address.lower()).hexdigest()) \
767 .replace('{netloc}', parsed_url.netloc)\
767 .replace('{netloc}', parsed_url.netloc)\
768 .replace('{scheme}', parsed_url.scheme)\
768 .replace('{scheme}', parsed_url.scheme)\
769 .replace('{size}', str(size))
769 .replace('{size}', str(size))
770 return tmpl
770 return tmpl
771
771
772 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
772 ssl_enabled = 'https' == request.environ.get('wsgi.url_scheme')
773 default = 'identicon'
773 default = 'identicon'
774 baseurl_nossl = "http://www.gravatar.com/avatar/"
774 baseurl_nossl = "http://www.gravatar.com/avatar/"
775 baseurl_ssl = "https://secure.gravatar.com/avatar/"
775 baseurl_ssl = "https://secure.gravatar.com/avatar/"
776 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
776 baseurl = baseurl_ssl if ssl_enabled else baseurl_nossl
777
777
778 if isinstance(email_address, unicode):
778 if isinstance(email_address, unicode):
779 #hashlib crashes on unicode items
779 #hashlib crashes on unicode items
780 email_address = safe_str(email_address)
780 email_address = safe_str(email_address)
781 # construct the url
781 # construct the url
782 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
782 gravatar_url = baseurl + hashlib.md5(email_address.lower()).hexdigest() + "?"
783 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
783 gravatar_url += urllib.urlencode({'d': default, 's': str(size)})
784
784
785 return gravatar_url
785 return gravatar_url
786
786
787
787
788 #==============================================================================
788 #==============================================================================
789 # REPO PAGER, PAGER FOR REPOSITORY
789 # REPO PAGER, PAGER FOR REPOSITORY
790 #==============================================================================
790 #==============================================================================
791 class RepoPage(Page):
791 class RepoPage(Page):
792
792
793 def __init__(self, collection, page=1, items_per_page=20,
793 def __init__(self, collection, page=1, items_per_page=20,
794 item_count=None, url=None, **kwargs):
794 item_count=None, url=None, **kwargs):
795
795
796 """Create a "RepoPage" instance. special pager for paging
796 """Create a "RepoPage" instance. special pager for paging
797 repository
797 repository
798 """
798 """
799 self._url_generator = url
799 self._url_generator = url
800
800
801 # Safe the kwargs class-wide so they can be used in the pager() method
801 # Safe the kwargs class-wide so they can be used in the pager() method
802 self.kwargs = kwargs
802 self.kwargs = kwargs
803
803
804 # Save a reference to the collection
804 # Save a reference to the collection
805 self.original_collection = collection
805 self.original_collection = collection
806
806
807 self.collection = collection
807 self.collection = collection
808
808
809 # The self.page is the number of the current page.
809 # The self.page is the number of the current page.
810 # The first page has the number 1!
810 # The first page has the number 1!
811 try:
811 try:
812 self.page = int(page) # make it int() if we get it as a string
812 self.page = int(page) # make it int() if we get it as a string
813 except (ValueError, TypeError):
813 except (ValueError, TypeError):
814 self.page = 1
814 self.page = 1
815
815
816 self.items_per_page = items_per_page
816 self.items_per_page = items_per_page
817
817
818 # Unless the user tells us how many items the collections has
818 # Unless the user tells us how many items the collections has
819 # we calculate that ourselves.
819 # we calculate that ourselves.
820 if item_count is not None:
820 if item_count is not None:
821 self.item_count = item_count
821 self.item_count = item_count
822 else:
822 else:
823 self.item_count = len(self.collection)
823 self.item_count = len(self.collection)
824
824
825 # Compute the number of the first and last available page
825 # Compute the number of the first and last available page
826 if self.item_count > 0:
826 if self.item_count > 0:
827 self.first_page = 1
827 self.first_page = 1
828 self.page_count = int(math.ceil(float(self.item_count) /
828 self.page_count = int(math.ceil(float(self.item_count) /
829 self.items_per_page))
829 self.items_per_page))
830 self.last_page = self.first_page + self.page_count - 1
830 self.last_page = self.first_page + self.page_count - 1
831
831
832 # Make sure that the requested page number is the range of
832 # Make sure that the requested page number is the range of
833 # valid pages
833 # valid pages
834 if self.page > self.last_page:
834 if self.page > self.last_page:
835 self.page = self.last_page
835 self.page = self.last_page
836 elif self.page < self.first_page:
836 elif self.page < self.first_page:
837 self.page = self.first_page
837 self.page = self.first_page
838
838
839 # Note: the number of items on this page can be less than
839 # Note: the number of items on this page can be less than
840 # items_per_page if the last page is not full
840 # items_per_page if the last page is not full
841 self.first_item = max(0, (self.item_count) - (self.page *
841 self.first_item = max(0, (self.item_count) - (self.page *
842 items_per_page))
842 items_per_page))
843 self.last_item = ((self.item_count - 1) - items_per_page *
843 self.last_item = ((self.item_count - 1) - items_per_page *
844 (self.page - 1))
844 (self.page - 1))
845
845
846 self.items = list(self.collection[self.first_item:self.last_item + 1])
846 self.items = list(self.collection[self.first_item:self.last_item + 1])
847
847
848 # Links to previous and next page
848 # Links to previous and next page
849 if self.page > self.first_page:
849 if self.page > self.first_page:
850 self.previous_page = self.page - 1
850 self.previous_page = self.page - 1
851 else:
851 else:
852 self.previous_page = None
852 self.previous_page = None
853
853
854 if self.page < self.last_page:
854 if self.page < self.last_page:
855 self.next_page = self.page + 1
855 self.next_page = self.page + 1
856 else:
856 else:
857 self.next_page = None
857 self.next_page = None
858
858
859 # No items available
859 # No items available
860 else:
860 else:
861 self.first_page = None
861 self.first_page = None
862 self.page_count = 0
862 self.page_count = 0
863 self.last_page = None
863 self.last_page = None
864 self.first_item = None
864 self.first_item = None
865 self.last_item = None
865 self.last_item = None
866 self.previous_page = None
866 self.previous_page = None
867 self.next_page = None
867 self.next_page = None
868 self.items = []
868 self.items = []
869
869
870 # This is a subclass of the 'list' type. Initialise the list now.
870 # This is a subclass of the 'list' type. Initialise the list now.
871 list.__init__(self, reversed(self.items))
871 list.__init__(self, reversed(self.items))
872
872
873
873
874 def changed_tooltip(nodes):
874 def changed_tooltip(nodes):
875 """
875 """
876 Generates a html string for changed nodes in changeset page.
876 Generates a html string for changed nodes in changeset page.
877 It limits the output to 30 entries
877 It limits the output to 30 entries
878
878
879 :param nodes: LazyNodesGenerator
879 :param nodes: LazyNodesGenerator
880 """
880 """
881 if nodes:
881 if nodes:
882 pref = ': <br/> '
882 pref = ': <br/> '
883 suf = ''
883 suf = ''
884 if len(nodes) > 30:
884 if len(nodes) > 30:
885 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
885 suf = '<br/>' + _(' and %s more') % (len(nodes) - 30)
886 return literal(pref + '<br/> '.join([safe_unicode(x.path)
886 return literal(pref + '<br/> '.join([safe_unicode(x.path)
887 for x in nodes[:30]]) + suf)
887 for x in nodes[:30]]) + suf)
888 else:
888 else:
889 return ': ' + _('No Files')
889 return ': ' + _('No Files')
890
890
891
891
892 def repo_link(groups_and_repos, last_url=None):
892 def repo_link(groups_and_repos, last_url=None):
893 """
893 """
894 Makes a breadcrumbs link to repo within a group
894 Makes a breadcrumbs link to repo within a group
895 joins &raquo; on each group to create a fancy link
895 joins &raquo; on each group to create a fancy link
896
896
897 ex::
897 ex::
898 group >> subgroup >> repo
898 group >> subgroup >> repo
899
899
900 :param groups_and_repos:
900 :param groups_and_repos:
901 :param last_url:
901 :param last_url:
902 """
902 """
903 groups, repo_name = groups_and_repos
903 groups, repo_name = groups_and_repos
904 last_link = link_to(repo_name, last_url) if last_url else repo_name
904 last_link = link_to(repo_name, last_url) if last_url else repo_name
905
905
906 if not groups:
906 if not groups:
907 if last_url:
907 if last_url:
908 return last_link
908 return last_link
909 return repo_name
909 return repo_name
910 else:
910 else:
911 def make_link(group):
911 def make_link(group):
912 return link_to(group.name,
912 return link_to(group.name,
913 url('repos_group_home', group_name=group.group_name))
913 url('repos_group_home', group_name=group.group_name))
914 return literal(' &raquo; '.join(map(make_link, groups) + [last_link]))
914 return literal(' &raquo; '.join(map(make_link, groups) + [last_link]))
915
915
916
916
917 def fancy_file_stats(stats):
917 def fancy_file_stats(stats):
918 """
918 """
919 Displays a fancy two colored bar for number of added/deleted
919 Displays a fancy two colored bar for number of added/deleted
920 lines of code on file
920 lines of code on file
921
921
922 :param stats: two element list of added/deleted lines of code
922 :param stats: two element list of added/deleted lines of code
923 """
923 """
924 def cgen(l_type, a_v, d_v):
924 def cgen(l_type, a_v, d_v):
925 mapping = {'tr': 'top-right-rounded-corner-mid',
925 mapping = {'tr': 'top-right-rounded-corner-mid',
926 'tl': 'top-left-rounded-corner-mid',
926 'tl': 'top-left-rounded-corner-mid',
927 'br': 'bottom-right-rounded-corner-mid',
927 'br': 'bottom-right-rounded-corner-mid',
928 'bl': 'bottom-left-rounded-corner-mid'}
928 'bl': 'bottom-left-rounded-corner-mid'}
929 map_getter = lambda x: mapping[x]
929 map_getter = lambda x: mapping[x]
930
930
931 if l_type == 'a' and d_v:
931 if l_type == 'a' and d_v:
932 #case when added and deleted are present
932 #case when added and deleted are present
933 return ' '.join(map(map_getter, ['tl', 'bl']))
933 return ' '.join(map(map_getter, ['tl', 'bl']))
934
934
935 if l_type == 'a' and not d_v:
935 if l_type == 'a' and not d_v:
936 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
936 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
937
937
938 if l_type == 'd' and a_v:
938 if l_type == 'd' and a_v:
939 return ' '.join(map(map_getter, ['tr', 'br']))
939 return ' '.join(map(map_getter, ['tr', 'br']))
940
940
941 if l_type == 'd' and not a_v:
941 if l_type == 'd' and not a_v:
942 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
942 return ' '.join(map(map_getter, ['tr', 'br', 'tl', 'bl']))
943
943
944 a, d = stats[0], stats[1]
944 a, d = stats[0], stats[1]
945 width = 100
945 width = 100
946
946
947 if a == 'b':
947 if a == 'b':
948 #binary mode
948 #binary mode
949 b_d = '<div class="bin%s %s" style="width:100%%">%s</div>' % (d, cgen('a', a_v='', d_v=0), 'bin')
949 b_d = '<div class="bin%s %s" style="width:100%%">%s</div>' % (d, cgen('a', a_v='', d_v=0), 'bin')
950 b_a = '<div class="bin1" style="width:0%%">%s</div>' % ('bin')
950 b_a = '<div class="bin1" style="width:0%%">%s</div>' % ('bin')
951 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
951 return literal('<div style="width:%spx">%s%s</div>' % (width, b_a, b_d))
952
952
953 t = stats[0] + stats[1]
953 t = stats[0] + stats[1]
954 unit = float(width) / (t or 1)
954 unit = float(width) / (t or 1)
955
955
956 # needs > 9% of width to be visible or 0 to be hidden
956 # needs > 9% of width to be visible or 0 to be hidden
957 a_p = max(9, unit * a) if a > 0 else 0
957 a_p = max(9, unit * a) if a > 0 else 0
958 d_p = max(9, unit * d) if d > 0 else 0
958 d_p = max(9, unit * d) if d > 0 else 0
959 p_sum = a_p + d_p
959 p_sum = a_p + d_p
960
960
961 if p_sum > width:
961 if p_sum > width:
962 #adjust the percentage to be == 100% since we adjusted to 9
962 #adjust the percentage to be == 100% since we adjusted to 9
963 if a_p > d_p:
963 if a_p > d_p:
964 a_p = a_p - (p_sum - width)
964 a_p = a_p - (p_sum - width)
965 else:
965 else:
966 d_p = d_p - (p_sum - width)
966 d_p = d_p - (p_sum - width)
967
967
968 a_v = a if a > 0 else ''
968 a_v = a if a > 0 else ''
969 d_v = d if d > 0 else ''
969 d_v = d if d > 0 else ''
970
970
971 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
971 d_a = '<div class="added %s" style="width:%s%%">%s</div>' % (
972 cgen('a', a_v, d_v), a_p, a_v
972 cgen('a', a_v, d_v), a_p, a_v
973 )
973 )
974 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
974 d_d = '<div class="deleted %s" style="width:%s%%">%s</div>' % (
975 cgen('d', a_v, d_v), d_p, d_v
975 cgen('d', a_v, d_v), d_p, d_v
976 )
976 )
977 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
977 return literal('<div style="width:%spx">%s%s</div>' % (width, d_a, d_d))
978
978
979
979
980 def urlify_text(text_, safe=True):
980 def urlify_text(text_, safe=True):
981 """
981 """
982 Extrac urls from text and make html links out of them
982 Extrac urls from text and make html links out of them
983
983
984 :param text_:
984 :param text_:
985 """
985 """
986
986
987 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
987 url_pat = re.compile(r'''(http[s]?://(?:[a-zA-Z]|[0-9]|[$-_@.&+]'''
988 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
988 '''|[!*\(\),]|(?:%[0-9a-fA-F][0-9a-fA-F]))+)''')
989
989
990 def url_func(match_obj):
990 def url_func(match_obj):
991 url_full = match_obj.groups()[0]
991 url_full = match_obj.groups()[0]
992 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
992 return '<a href="%(url)s">%(url)s</a>' % ({'url': url_full})
993 _newtext = url_pat.sub(url_func, text_)
993 _newtext = url_pat.sub(url_func, text_)
994 if safe:
994 if safe:
995 return literal(_newtext)
995 return literal(_newtext)
996 return _newtext
996 return _newtext
997
997
998
998
999 def urlify_changesets(text_, repository):
999 def urlify_changesets(text_, repository):
1000 """
1000 """
1001 Extract revision ids from changeset and make link from them
1001 Extract revision ids from changeset and make link from them
1002
1002
1003 :param text_:
1003 :param text_:
1004 :param repository: repo name to build the URL with
1004 :param repository: repo name to build the URL with
1005 """
1005 """
1006 from pylons import url # doh, we need to re-import url to mock it later
1006 from pylons import url # doh, we need to re-import url to mock it later
1007 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1007 URL_PAT = re.compile(r'(^|\s)([0-9a-fA-F]{12,40})($|\s)')
1008
1008
1009 def url_func(match_obj):
1009 def url_func(match_obj):
1010 rev = match_obj.groups()[1]
1010 rev = match_obj.groups()[1]
1011 pref = match_obj.groups()[0]
1011 pref = match_obj.groups()[0]
1012 suf = match_obj.groups()[2]
1012 suf = match_obj.groups()[2]
1013
1013
1014 tmpl = (
1014 tmpl = (
1015 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1015 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1016 '%(rev)s</a>%(suf)s'
1016 '%(rev)s</a>%(suf)s'
1017 )
1017 )
1018 return tmpl % {
1018 return tmpl % {
1019 'pref': pref,
1019 'pref': pref,
1020 'cls': 'revision-link',
1020 'cls': 'revision-link',
1021 'url': url('changeset_home', repo_name=repository, revision=rev),
1021 'url': url('changeset_home', repo_name=repository, revision=rev),
1022 'rev': rev,
1022 'rev': rev,
1023 'suf': suf
1023 'suf': suf
1024 }
1024 }
1025
1025
1026 newtext = URL_PAT.sub(url_func, text_)
1026 newtext = URL_PAT.sub(url_func, text_)
1027
1027
1028 return newtext
1028 return newtext
1029
1029
1030
1030
1031 def urlify_commit(text_, repository=None, link_=None):
1031 def urlify_commit(text_, repository=None, link_=None):
1032 """
1032 """
1033 Parses given text message and makes proper links.
1033 Parses given text message and makes proper links.
1034 issues are linked to given issue-server, and rest is a changeset link
1034 issues are linked to given issue-server, and rest is a changeset link
1035 if link_ is given, in other case it's a plain text
1035 if link_ is given, in other case it's a plain text
1036
1036
1037 :param text_:
1037 :param text_:
1038 :param repository:
1038 :param repository:
1039 :param link_: changeset link
1039 :param link_: changeset link
1040 """
1040 """
1041 import traceback
1041 import traceback
1042 from pylons import url # doh, we need to re-import url to mock it later
1042 from pylons import url # doh, we need to re-import url to mock it later
1043
1043
1044 def escaper(string):
1044 def escaper(string):
1045 return string.replace('<', '&lt;').replace('>', '&gt;')
1045 return string.replace('<', '&lt;').replace('>', '&gt;')
1046
1046
1047 def linkify_others(t, l):
1047 def linkify_others(t, l):
1048 urls = re.compile(r'(\<a.*?\<\/a\>)',)
1048 urls = re.compile(r'(\<a.*?\<\/a\>)',)
1049 links = []
1049 links = []
1050 for e in urls.split(t):
1050 for e in urls.split(t):
1051 if not urls.match(e):
1051 if not urls.match(e):
1052 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
1052 links.append('<a class="message-link" href="%s">%s</a>' % (l, e))
1053 else:
1053 else:
1054 links.append(e)
1054 links.append(e)
1055
1055
1056 return ''.join(links)
1056 return ''.join(links)
1057
1057
1058 # urlify changesets - extrac revisions and make link out of them
1058 # urlify changesets - extrac revisions and make link out of them
1059 newtext = urlify_changesets(escaper(text_), repository)
1059 newtext = urlify_changesets(escaper(text_), repository)
1060
1060
1061 # extract http/https links and make them real urls
1061 # extract http/https links and make them real urls
1062 newtext = urlify_text(newtext, safe=False)
1062 newtext = urlify_text(newtext, safe=False)
1063
1063
1064 try:
1064 try:
1065 from rhodecode import CONFIG
1065 from rhodecode import CONFIG
1066 conf = CONFIG
1066 conf = CONFIG
1067
1067
1068 # allow multiple issue servers to be used
1068 # allow multiple issue servers to be used
1069 valid_indices = [
1069 valid_indices = [
1070 x.group(1)
1070 x.group(1)
1071 for x in map(lambda x: re.match(r'issue_pat(.*)', x), conf.keys())
1071 for x in map(lambda x: re.match(r'issue_pat(.*)', x), conf.keys())
1072 if x and 'issue_server_link%s' % x.group(1) in conf
1072 if x and 'issue_server_link%s' % x.group(1) in conf
1073 and 'issue_prefix%s' % x.group(1) in conf
1073 and 'issue_prefix%s' % x.group(1) in conf
1074 ]
1074 ]
1075
1075
1076 log.debug('found issue server suffixes `%s` during valuation of: %s'
1076 log.debug('found issue server suffixes `%s` during valuation of: %s'
1077 % (','.join(valid_indices), newtext))
1077 % (','.join(valid_indices), newtext))
1078
1078
1079 for pattern_index in valid_indices:
1079 for pattern_index in valid_indices:
1080 ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index)
1080 ISSUE_PATTERN = conf.get('issue_pat%s' % pattern_index)
1081 ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index)
1081 ISSUE_SERVER_LNK = conf.get('issue_server_link%s' % pattern_index)
1082 ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index)
1082 ISSUE_PREFIX = conf.get('issue_prefix%s' % pattern_index)
1083
1083
1084 log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s'
1084 log.debug('pattern suffix `%s` PAT:%s SERVER_LINK:%s PREFIX:%s'
1085 % (pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK,
1085 % (pattern_index, ISSUE_PATTERN, ISSUE_SERVER_LNK,
1086 ISSUE_PREFIX))
1086 ISSUE_PREFIX))
1087
1087
1088 URL_PAT = re.compile(r'%s' % ISSUE_PATTERN)
1088 URL_PAT = re.compile(r'%s' % ISSUE_PATTERN)
1089
1089
1090 def url_func(match_obj):
1090 def url_func(match_obj):
1091 pref = ''
1091 pref = ''
1092 if match_obj.group().startswith(' '):
1092 if match_obj.group().startswith(' '):
1093 pref = ' '
1093 pref = ' '
1094
1094
1095 issue_id = ''.join(match_obj.groups())
1095 issue_id = ''.join(match_obj.groups())
1096 tmpl = (
1096 tmpl = (
1097 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1097 '%(pref)s<a class="%(cls)s" href="%(url)s">'
1098 '%(issue-prefix)s%(id-repr)s'
1098 '%(issue-prefix)s%(id-repr)s'
1099 '</a>'
1099 '</a>'
1100 )
1100 )
1101 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
1101 url = ISSUE_SERVER_LNK.replace('{id}', issue_id)
1102 if repository:
1102 if repository:
1103 url = url.replace('{repo}', repository)
1103 url = url.replace('{repo}', repository)
1104 repo_name = repository.split(URL_SEP)[-1]
1104 repo_name = repository.split(URL_SEP)[-1]
1105 url = url.replace('{repo_name}', repo_name)
1105 url = url.replace('{repo_name}', repo_name)
1106
1106
1107 return tmpl % {
1107 return tmpl % {
1108 'pref': pref,
1108 'pref': pref,
1109 'cls': 'issue-tracker-link',
1109 'cls': 'issue-tracker-link',
1110 'url': url,
1110 'url': url,
1111 'id-repr': issue_id,
1111 'id-repr': issue_id,
1112 'issue-prefix': ISSUE_PREFIX,
1112 'issue-prefix': ISSUE_PREFIX,
1113 'serv': ISSUE_SERVER_LNK,
1113 'serv': ISSUE_SERVER_LNK,
1114 }
1114 }
1115 newtext = URL_PAT.sub(url_func, newtext)
1115 newtext = URL_PAT.sub(url_func, newtext)
1116 log.debug('processed prefix:`%s` => %s' % (pattern_index, newtext))
1116 log.debug('processed prefix:`%s` => %s' % (pattern_index, newtext))
1117
1117
1118 # if we actually did something above
1118 # if we actually did something above
1119 if link_:
1119 if link_:
1120 # wrap not links into final link => link_
1120 # wrap not links into final link => link_
1121 newtext = linkify_others(newtext, link_)
1121 newtext = linkify_others(newtext, link_)
1122 except:
1122 except:
1123 log.error(traceback.format_exc())
1123 log.error(traceback.format_exc())
1124 pass
1124 pass
1125
1125
1126 return literal(newtext)
1126 return literal(newtext)
1127
1127
1128
1128
1129 def rst(source):
1129 def rst(source):
1130 return literal('<div class="rst-block">%s</div>' %
1130 return literal('<div class="rst-block">%s</div>' %
1131 MarkupRenderer.rst(source))
1131 MarkupRenderer.rst(source))
1132
1132
1133
1133
1134 def rst_w_mentions(source):
1134 def rst_w_mentions(source):
1135 """
1135 """
1136 Wrapped rst renderer with @mention highlighting
1136 Wrapped rst renderer with @mention highlighting
1137
1137
1138 :param source:
1138 :param source:
1139 """
1139 """
1140 return literal('<div class="rst-block">%s</div>' %
1140 return literal('<div class="rst-block">%s</div>' %
1141 MarkupRenderer.rst_with_mentions(source))
1141 MarkupRenderer.rst_with_mentions(source))
1142
1142
1143
1143
1144 def changeset_status(repo, revision):
1144 def changeset_status(repo, revision):
1145 return ChangesetStatusModel().get_status(repo, revision)
1145 return ChangesetStatusModel().get_status(repo, revision)
1146
1146
1147
1147
1148 def changeset_status_lbl(changeset_status):
1148 def changeset_status_lbl(changeset_status):
1149 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1149 return dict(ChangesetStatus.STATUSES).get(changeset_status)
1150
1150
1151
1151
1152 def get_permission_name(key):
1152 def get_permission_name(key):
1153 return dict(Permission.PERMS).get(key)
1153 return dict(Permission.PERMS).get(key)
1154
1154
1155
1155
1156 def journal_filter_help():
1156 def journal_filter_help():
1157 return _(textwrap.dedent('''
1157 return _(textwrap.dedent('''
1158 Example filter terms:
1158 Example filter terms:
1159 repository:vcs
1159 repository:vcs
1160 username:marcin
1160 username:marcin
1161 action:*push*
1161 action:*push*
1162 ip:127.0.0.1
1162 ip:127.0.0.1
1163 date:20120101
1163 date:20120101
1164 date:[20120101100000 TO 20120102]
1164 date:[20120101100000 TO 20120102]
1165
1165
1166 Generate wildcards using '*' character:
1166 Generate wildcards using '*' character:
1167 "repositroy:vcs*" - search everything starting with 'vcs'
1167 "repositroy:vcs*" - search everything starting with 'vcs'
1168 "repository:*vcs*" - search for repository containing 'vcs'
1168 "repository:*vcs*" - search for repository containing 'vcs'
1169
1169
1170 Optional AND / OR operators in queries
1170 Optional AND / OR operators in queries
1171 "repository:vcs OR repository:test"
1171 "repository:vcs OR repository:test"
1172 "username:test AND repository:test*"
1172 "username:test AND repository:test*"
1173 '''))
1173 '''))
1174
1174
1175
1175
1176 def not_mapped_error(repo_name):
1176 def not_mapped_error(repo_name):
1177 flash(_('%s repository is not mapped to db perhaps'
1177 flash(_('%s repository is not mapped to db perhaps'
1178 ' it was created or renamed from the filesystem'
1178 ' it was created or renamed from the filesystem'
1179 ' please run the application again'
1179 ' please run the application again'
1180 ' in order to rescan repositories') % repo_name, category='error')
1180 ' in order to rescan repositories') % repo_name, category='error')
1181
1181
1182
1182
1183 def ip_range(ip_addr):
1183 def ip_range(ip_addr):
1184 from rhodecode.model.db import UserIpMap
1184 from rhodecode.model.db import UserIpMap
1185 s, e = UserIpMap._get_ip_range(ip_addr)
1185 s, e = UserIpMap._get_ip_range(ip_addr)
1186 return '%s - %s' % (s, e)
1186 return '%s - %s' % (s, e)
@@ -1,705 +1,705 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.repo
3 rhodecode.model.repo
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 Repository model for rhodecode
6 Repository model for rhodecode
7
7
8 :created_on: Jun 5, 2010
8 :created_on: Jun 5, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 from __future__ import with_statement
25 from __future__ import with_statement
26 import os
26 import os
27 import shutil
27 import shutil
28 import logging
28 import logging
29 import traceback
29 import traceback
30 from datetime import datetime
30 from datetime import datetime
31
31
32 from rhodecode.lib.vcs.backends import get_backend
32 from rhodecode.lib.vcs.backends import get_backend
33 from rhodecode.lib.compat import json
33 from rhodecode.lib.compat import json
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
34 from rhodecode.lib.utils2 import LazyProperty, safe_str, safe_unicode,\
35 remove_prefix
35 remove_prefix
36 from rhodecode.lib.caching_query import FromCache
36 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
37 from rhodecode.lib.hooks import log_create_repository, log_delete_repository
38
38
39 from rhodecode.model import BaseModel
39 from rhodecode.model import BaseModel
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
40 from rhodecode.model.db import Repository, UserRepoToPerm, User, Permission, \
41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
41 Statistics, UsersGroup, UsersGroupRepoToPerm, RhodeCodeUi, RepoGroup,\
42 RhodeCodeSetting, RepositoryField
42 RhodeCodeSetting, RepositoryField
43 from rhodecode.lib import helpers as h
43 from rhodecode.lib import helpers as h
44 from rhodecode.lib.auth import HasRepoPermissionAny
44 from rhodecode.lib.auth import HasRepoPermissionAny
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
45 from rhodecode.lib.vcs.backends.base import EmptyChangeset
46
46
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50
50
51 class RepoModel(BaseModel):
51 class RepoModel(BaseModel):
52
52
53 cls = Repository
53 cls = Repository
54 URL_SEPARATOR = Repository.url_sep()
54 URL_SEPARATOR = Repository.url_sep()
55
55
56 def __get_users_group(self, users_group):
56 def __get_users_group(self, users_group):
57 return self._get_instance(UsersGroup, users_group,
57 return self._get_instance(UsersGroup, users_group,
58 callback=UsersGroup.get_by_group_name)
58 callback=UsersGroup.get_by_group_name)
59
59
60 def _get_repos_group(self, repos_group):
60 def _get_repos_group(self, repos_group):
61 return self._get_instance(RepoGroup, repos_group,
61 return self._get_instance(RepoGroup, repos_group,
62 callback=RepoGroup.get_by_group_name)
62 callback=RepoGroup.get_by_group_name)
63
63
64 @LazyProperty
64 @LazyProperty
65 def repos_path(self):
65 def repos_path(self):
66 """
66 """
67 Get's the repositories root path from database
67 Get's the repositories root path from database
68 """
68 """
69
69
70 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
70 q = self.sa.query(RhodeCodeUi).filter(RhodeCodeUi.ui_key == '/').one()
71 return q.ui_value
71 return q.ui_value
72
72
73 def get(self, repo_id, cache=False):
73 def get(self, repo_id, cache=False):
74 repo = self.sa.query(Repository)\
74 repo = self.sa.query(Repository)\
75 .filter(Repository.repo_id == repo_id)
75 .filter(Repository.repo_id == repo_id)
76
76
77 if cache:
77 if cache:
78 repo = repo.options(FromCache("sql_cache_short",
78 repo = repo.options(FromCache("sql_cache_short",
79 "get_repo_%s" % repo_id))
79 "get_repo_%s" % repo_id))
80 return repo.scalar()
80 return repo.scalar()
81
81
82 def get_repo(self, repository):
82 def get_repo(self, repository):
83 return self._get_repo(repository)
83 return self._get_repo(repository)
84
84
85 def get_by_repo_name(self, repo_name, cache=False):
85 def get_by_repo_name(self, repo_name, cache=False):
86 repo = self.sa.query(Repository)\
86 repo = self.sa.query(Repository)\
87 .filter(Repository.repo_name == repo_name)
87 .filter(Repository.repo_name == repo_name)
88
88
89 if cache:
89 if cache:
90 repo = repo.options(FromCache("sql_cache_short",
90 repo = repo.options(FromCache("sql_cache_short",
91 "get_repo_%s" % repo_name))
91 "get_repo_%s" % repo_name))
92 return repo.scalar()
92 return repo.scalar()
93
93
94 def get_all_user_repos(self, user):
94 def get_all_user_repos(self, user):
95 """
95 """
96 Get's all repositories that user have at least read access
96 Get's all repositories that user have at least read access
97
97
98 :param user:
98 :param user:
99 :type user:
99 :type user:
100 """
100 """
101 from rhodecode.lib.auth import AuthUser
101 from rhodecode.lib.auth import AuthUser
102 user = self._get_user(user)
102 user = self._get_user(user)
103 repos = AuthUser(user_id=user.user_id).permissions['repositories']
103 repos = AuthUser(user_id=user.user_id).permissions['repositories']
104 access_check = lambda r: r[1] in ['repository.read',
104 access_check = lambda r: r[1] in ['repository.read',
105 'repository.write',
105 'repository.write',
106 'repository.admin']
106 'repository.admin']
107 repos = [x[0] for x in filter(access_check, repos.items())]
107 repos = [x[0] for x in filter(access_check, repos.items())]
108 return Repository.query().filter(Repository.repo_name.in_(repos))
108 return Repository.query().filter(Repository.repo_name.in_(repos))
109
109
110 def get_users_js(self):
110 def get_users_js(self):
111 users = self.sa.query(User).filter(User.active == True).all()
111 users = self.sa.query(User).filter(User.active == True).all()
112 return json.dumps([
112 return json.dumps([
113 {
113 {
114 'id': u.user_id,
114 'id': u.user_id,
115 'fname': u.name,
115 'fname': u.name,
116 'lname': u.lastname,
116 'lname': u.lastname,
117 'nname': u.username,
117 'nname': u.username,
118 'gravatar_lnk': h.gravatar_url(u.email, 14)
118 'gravatar_lnk': h.gravatar_url(u.email, 14)
119 } for u in users]
119 } for u in users]
120 )
120 )
121
121
122 def get_users_groups_js(self):
122 def get_users_groups_js(self):
123 users_groups = self.sa.query(UsersGroup)\
123 users_groups = self.sa.query(UsersGroup)\
124 .filter(UsersGroup.users_group_active == True).all()
124 .filter(UsersGroup.users_group_active == True).all()
125
125
126 return json.dumps([
126 return json.dumps([
127 {
127 {
128 'id': gr.users_group_id,
128 'id': gr.users_group_id,
129 'grname': gr.users_group_name,
129 'grname': gr.users_group_name,
130 'grmembers': len(gr.members),
130 'grmembers': len(gr.members),
131 } for gr in users_groups]
131 } for gr in users_groups]
132 )
132 )
133
133
134 @classmethod
134 @classmethod
135 def _render_datatable(cls, tmpl, *args, **kwargs):
135 def _render_datatable(cls, tmpl, *args, **kwargs):
136 import rhodecode
136 import rhodecode
137 from pylons import tmpl_context as c
137 from pylons import tmpl_context as c
138 from pylons.i18n.translation import _
138 from pylons.i18n.translation import _
139
139
140 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
140 _tmpl_lookup = rhodecode.CONFIG['pylons.app_globals'].mako_lookup
141 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
141 template = _tmpl_lookup.get_template('data_table/_dt_elements.html')
142
142
143 tmpl = template.get_def(tmpl)
143 tmpl = template.get_def(tmpl)
144 kwargs.update(dict(_=_, h=h, c=c))
144 kwargs.update(dict(_=_, h=h, c=c))
145 return tmpl.render(*args, **kwargs)
145 return tmpl.render(*args, **kwargs)
146
146
147 @classmethod
147 @classmethod
148 def update_repoinfo(cls, repositories=None):
148 def update_repoinfo(cls, repositories=None):
149 if not repositories:
149 if not repositories:
150 repositories = Repository.getAll()
150 repositories = Repository.getAll()
151 for repo in repositories:
151 for repo in repositories:
152 scm_repo = repo.scm_instance_no_cache
152 scm_repo = repo.scm_instance_no_cache
153 last_cs = EmptyChangeset()
153 last_cs = EmptyChangeset()
154 if scm_repo:
154 if scm_repo:
155 last_cs = scm_repo.get_changeset()
155 last_cs = scm_repo.get_changeset()
156 repo.update_changeset_cache(last_cs)
156 repo.update_changeset_cache(last_cs)
157
157
158 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
158 def get_repos_as_dict(self, repos_list=None, admin=False, perm_check=True,
159 super_user_actions=False):
159 super_user_actions=False):
160 _render = self._render_datatable
160 _render = self._render_datatable
161
161
162 def quick_menu(repo_name):
162 def quick_menu(repo_name):
163 return _render('quick_menu', repo_name)
163 return _render('quick_menu', repo_name)
164
164
165 def repo_lnk(name, rtype, private, fork_of):
165 def repo_lnk(name, rtype, private, fork_of):
166 return _render('repo_name', name, rtype, private, fork_of,
166 return _render('repo_name', name, rtype, private, fork_of,
167 short_name=not admin, admin=False)
167 short_name=not admin, admin=False)
168
168
169 def last_change(last_change):
169 def last_change(last_change):
170 return _render("last_change", last_change)
170 return _render("last_change", last_change)
171
171
172 def rss_lnk(repo_name):
172 def rss_lnk(repo_name):
173 return _render("rss", repo_name)
173 return _render("rss", repo_name)
174
174
175 def atom_lnk(repo_name):
175 def atom_lnk(repo_name):
176 return _render("atom", repo_name)
176 return _render("atom", repo_name)
177
177
178 def last_rev(repo_name, cs_cache):
178 def last_rev(repo_name, cs_cache):
179 return _render('revision', repo_name, cs_cache.get('revision'),
179 return _render('revision', repo_name, cs_cache.get('revision'),
180 cs_cache.get('raw_id'), cs_cache.get('author'),
180 cs_cache.get('raw_id'), cs_cache.get('author'),
181 cs_cache.get('message'))
181 cs_cache.get('message'))
182
182
183 def desc(desc):
183 def desc(desc):
184 from pylons import tmpl_context as c
184 from pylons import tmpl_context as c
185 if c.visual.stylify_metatags:
185 if c.visual.stylify_metatags:
186 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
186 return h.urlify_text(h.desc_stylize(h.truncate(desc, 60)))
187 else:
187 else:
188 return h.urlify_text(h.truncate(desc, 60))
188 return h.urlify_text(h.truncate(desc, 60))
189
189
190 def repo_actions(repo_name):
190 def repo_actions(repo_name):
191 return _render('repo_actions', repo_name, super_user_actions)
191 return _render('repo_actions', repo_name, super_user_actions)
192
192
193 def owner_actions(user_id, username):
193 def owner_actions(user_id, username):
194 return _render('user_name', user_id, username)
194 return _render('user_name', user_id, username)
195
195
196 repos_data = []
196 repos_data = []
197 for repo in repos_list:
197 for repo in repos_list:
198 if perm_check:
198 if perm_check:
199 # check permission at this level
199 # check permission at this level
200 if not HasRepoPermissionAny(
200 if not HasRepoPermissionAny(
201 'repository.read', 'repository.write', 'repository.admin'
201 'repository.read', 'repository.write', 'repository.admin'
202 )(repo.repo_name, 'get_repos_as_dict check'):
202 )(repo.repo_name, 'get_repos_as_dict check'):
203 continue
203 continue
204 cs_cache = repo.changeset_cache
204 cs_cache = repo.changeset_cache
205 row = {
205 row = {
206 "menu": quick_menu(repo.repo_name),
206 "menu": quick_menu(repo.repo_name),
207 "raw_name": repo.repo_name.lower(),
207 "raw_name": repo.repo_name.lower(),
208 "name": repo_lnk(repo.repo_name, repo.repo_type,
208 "name": repo_lnk(repo.repo_name, repo.repo_type,
209 repo.private, repo.fork),
209 repo.private, repo.fork),
210 "last_change": last_change(repo.last_db_change),
210 "last_change": last_change(repo.last_db_change),
211 "last_changeset": last_rev(repo.repo_name, cs_cache),
211 "last_changeset": last_rev(repo.repo_name, cs_cache),
212 "raw_tip": cs_cache.get('revision'),
212 "raw_tip": cs_cache.get('revision'),
213 "desc": desc(repo.description),
213 "desc": desc(repo.description),
214 "owner": h.person(repo.user.username),
214 "owner": h.person(repo.user.username),
215 "rss": rss_lnk(repo.repo_name),
215 "rss": rss_lnk(repo.repo_name),
216 "atom": atom_lnk(repo.repo_name),
216 "atom": atom_lnk(repo.repo_name),
217
217
218 }
218 }
219 if admin:
219 if admin:
220 row.update({
220 row.update({
221 "action": repo_actions(repo.repo_name),
221 "action": repo_actions(repo.repo_name),
222 "owner": owner_actions(repo.user.user_id,
222 "owner": owner_actions(repo.user.user_id,
223 h.person(repo.user.username))
223 h.person(repo.user.username))
224 })
224 })
225 repos_data.append(row)
225 repos_data.append(row)
226
226
227 return {
227 return {
228 "totalRecords": len(repos_list),
228 "totalRecords": len(repos_list),
229 "startIndex": 0,
229 "startIndex": 0,
230 "sort": "name",
230 "sort": "name",
231 "dir": "asc",
231 "dir": "asc",
232 "records": repos_data
232 "records": repos_data
233 }
233 }
234
234
235 def _get_defaults(self, repo_name):
235 def _get_defaults(self, repo_name):
236 """
236 """
237 Get's information about repository, and returns a dict for
237 Get's information about repository, and returns a dict for
238 usage in forms
238 usage in forms
239
239
240 :param repo_name:
240 :param repo_name:
241 """
241 """
242
242
243 repo_info = Repository.get_by_repo_name(repo_name)
243 repo_info = Repository.get_by_repo_name(repo_name)
244
244
245 if repo_info is None:
245 if repo_info is None:
246 return None
246 return None
247
247
248 defaults = repo_info.get_dict()
248 defaults = repo_info.get_dict()
249 group, repo_name = repo_info.groups_and_repo
249 group, repo_name = repo_info.groups_and_repo
250 defaults['repo_name'] = repo_name
250 defaults['repo_name'] = repo_name
251 defaults['repo_group'] = getattr(group[-1] if group else None,
251 defaults['repo_group'] = getattr(group[-1] if group else None,
252 'group_id', None)
252 'group_id', None)
253
253
254 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
254 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
255 (1, 'repo_description'), (1, 'repo_enable_locking'),
255 (1, 'repo_description'), (1, 'repo_enable_locking'),
256 (1, 'repo_landing_rev'), (0, 'clone_uri'),
256 (1, 'repo_landing_rev'), (0, 'clone_uri'),
257 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
257 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
258 attr = k
258 attr = k
259 if strip:
259 if strip:
260 attr = remove_prefix(k, 'repo_')
260 attr = remove_prefix(k, 'repo_')
261
261
262 defaults[k] = defaults[attr]
262 defaults[k] = defaults[attr]
263
263
264 # fill owner
264 # fill owner
265 if repo_info.user:
265 if repo_info.user:
266 defaults.update({'user': repo_info.user.username})
266 defaults.update({'user': repo_info.user.username})
267 else:
267 else:
268 replacement_user = User.query().filter(User.admin ==
268 replacement_user = User.query().filter(User.admin ==
269 True).first().username
269 True).first().username
270 defaults.update({'user': replacement_user})
270 defaults.update({'user': replacement_user})
271
271
272 # fill repository users
272 # fill repository users
273 for p in repo_info.repo_to_perm:
273 for p in repo_info.repo_to_perm:
274 defaults.update({'u_perm_%s' % p.user.username:
274 defaults.update({'u_perm_%s' % p.user.username:
275 p.permission.permission_name})
275 p.permission.permission_name})
276
276
277 # fill repository groups
277 # fill repository groups
278 for p in repo_info.users_group_to_perm:
278 for p in repo_info.users_group_to_perm:
279 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
279 defaults.update({'g_perm_%s' % p.users_group.users_group_name:
280 p.permission.permission_name})
280 p.permission.permission_name})
281
281
282 return defaults
282 return defaults
283
283
284 def update(self, org_repo_name, **kwargs):
284 def update(self, org_repo_name, **kwargs):
285 try:
285 try:
286 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
286 cur_repo = self.get_by_repo_name(org_repo_name, cache=False)
287
287
288 # update permissions
288 # update permissions
289 for member, perm, member_type in kwargs['perms_updates']:
289 for member, perm, member_type in kwargs['perms_updates']:
290 if member_type == 'user':
290 if member_type == 'user':
291 # this updates existing one
291 # this updates existing one
292 RepoModel().grant_user_permission(
292 RepoModel().grant_user_permission(
293 repo=cur_repo, user=member, perm=perm
293 repo=cur_repo, user=member, perm=perm
294 )
294 )
295 else:
295 else:
296 RepoModel().grant_users_group_permission(
296 RepoModel().grant_users_group_permission(
297 repo=cur_repo, group_name=member, perm=perm
297 repo=cur_repo, group_name=member, perm=perm
298 )
298 )
299 # set new permissions
299 # set new permissions
300 for member, perm, member_type in kwargs['perms_new']:
300 for member, perm, member_type in kwargs['perms_new']:
301 if member_type == 'user':
301 if member_type == 'user':
302 RepoModel().grant_user_permission(
302 RepoModel().grant_user_permission(
303 repo=cur_repo, user=member, perm=perm
303 repo=cur_repo, user=member, perm=perm
304 )
304 )
305 else:
305 else:
306 RepoModel().grant_users_group_permission(
306 RepoModel().grant_users_group_permission(
307 repo=cur_repo, group_name=member, perm=perm
307 repo=cur_repo, group_name=member, perm=perm
308 )
308 )
309
309
310 if 'user' in kwargs:
310 if 'user' in kwargs:
311 cur_repo.user = User.get_by_username(kwargs['user'])
311 cur_repo.user = User.get_by_username(kwargs['user'])
312
312
313 if 'repo_group' in kwargs:
313 if 'repo_group' in kwargs:
314 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
314 cur_repo.group = RepoGroup.get(kwargs['repo_group'])
315
315
316 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
316 for strip, k in [(0, 'repo_type'), (1, 'repo_enable_downloads'),
317 (1, 'repo_description'), (1, 'repo_enable_locking'),
317 (1, 'repo_description'), (1, 'repo_enable_locking'),
318 (1, 'repo_landing_rev'), (0, 'clone_uri'),
318 (1, 'repo_landing_rev'), (0, 'clone_uri'),
319 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
319 (1, 'repo_private'), (1, 'repo_enable_statistics')]:
320 if k in kwargs:
320 if k in kwargs:
321 val = kwargs[k]
321 val = kwargs[k]
322 if strip:
322 if strip:
323 k = remove_prefix(k, 'repo_')
323 k = remove_prefix(k, 'repo_')
324 setattr(cur_repo, k, val)
324 setattr(cur_repo, k, val)
325
325
326 new_name = cur_repo.get_new_name(kwargs['repo_name'])
326 new_name = cur_repo.get_new_name(kwargs['repo_name'])
327 cur_repo.repo_name = new_name
327 cur_repo.repo_name = new_name
328
328
329 #handle extra fields
329 #handle extra fields
330 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
330 for field in filter(lambda k: k.startswith(RepositoryField.PREFIX), kwargs):
331 k = RepositoryField.un_prefix_key(field)
331 k = RepositoryField.un_prefix_key(field)
332 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
332 ex_field = RepositoryField.get_by_key_name(key=k, repo=cur_repo)
333 if ex_field:
333 if ex_field:
334 ex_field.field_value = kwargs[field]
334 ex_field.field_value = kwargs[field]
335 self.sa.add(ex_field)
335 self.sa.add(ex_field)
336 self.sa.add(cur_repo)
336 self.sa.add(cur_repo)
337
337
338 if org_repo_name != new_name:
338 if org_repo_name != new_name:
339 # rename repository
339 # rename repository
340 self.__rename_repo(old=org_repo_name, new=new_name)
340 self.__rename_repo(old=org_repo_name, new=new_name)
341
341
342 return cur_repo
342 return cur_repo
343 except:
343 except:
344 log.error(traceback.format_exc())
344 log.error(traceback.format_exc())
345 raise
345 raise
346
346
347 def create_repo(self, repo_name, repo_type, description, owner,
347 def create_repo(self, repo_name, repo_type, description, owner,
348 private=False, clone_uri=None, repos_group=None,
348 private=False, clone_uri=None, repos_group=None,
349 landing_rev='tip', just_db=False, fork_of=None,
349 landing_rev='tip', just_db=False, fork_of=None,
350 copy_fork_permissions=False, enable_statistics=False,
350 copy_fork_permissions=False, enable_statistics=False,
351 enable_locking=False, enable_downloads=False):
351 enable_locking=False, enable_downloads=False):
352 """
352 """
353 Create repository
353 Create repository
354
354
355 """
355 """
356 from rhodecode.model.scm import ScmModel
356 from rhodecode.model.scm import ScmModel
357
357
358 owner = self._get_user(owner)
358 owner = self._get_user(owner)
359 fork_of = self._get_repo(fork_of)
359 fork_of = self._get_repo(fork_of)
360 repos_group = self._get_repos_group(repos_group)
360 repos_group = self._get_repos_group(repos_group)
361 try:
361 try:
362
362
363 # repo name is just a name of repository
363 # repo name is just a name of repository
364 # while repo_name_full is a full qualified name that is combined
364 # while repo_name_full is a full qualified name that is combined
365 # with name and path of group
365 # with name and path of group
366 repo_name_full = repo_name
366 repo_name_full = repo_name
367 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
367 repo_name = repo_name.split(self.URL_SEPARATOR)[-1]
368
368
369 new_repo = Repository()
369 new_repo = Repository()
370 new_repo.enable_statistics = False
370 new_repo.enable_statistics = False
371 new_repo.repo_name = repo_name_full
371 new_repo.repo_name = repo_name_full
372 new_repo.repo_type = repo_type
372 new_repo.repo_type = repo_type
373 new_repo.user = owner
373 new_repo.user = owner
374 new_repo.group = repos_group
374 new_repo.group = repos_group
375 new_repo.description = description or repo_name
375 new_repo.description = description or repo_name
376 new_repo.private = private
376 new_repo.private = private
377 new_repo.clone_uri = clone_uri
377 new_repo.clone_uri = clone_uri
378 new_repo.landing_rev = landing_rev
378 new_repo.landing_rev = landing_rev
379
379
380 new_repo.enable_statistics = enable_statistics
380 new_repo.enable_statistics = enable_statistics
381 new_repo.enable_locking = enable_locking
381 new_repo.enable_locking = enable_locking
382 new_repo.enable_downloads = enable_downloads
382 new_repo.enable_downloads = enable_downloads
383
383
384 if repos_group:
384 if repos_group:
385 new_repo.enable_locking = repos_group.enable_locking
385 new_repo.enable_locking = repos_group.enable_locking
386
386
387 if fork_of:
387 if fork_of:
388 parent_repo = fork_of
388 parent_repo = fork_of
389 new_repo.fork = parent_repo
389 new_repo.fork = parent_repo
390
390
391 self.sa.add(new_repo)
391 self.sa.add(new_repo)
392
392
393 def _create_default_perms():
393 def _create_default_perms():
394 # create default permission
394 # create default permission
395 repo_to_perm = UserRepoToPerm()
395 repo_to_perm = UserRepoToPerm()
396 default = 'repository.read'
396 default = 'repository.read'
397 for p in User.get_by_username('default').user_perms:
397 for p in User.get_by_username('default').user_perms:
398 if p.permission.permission_name.startswith('repository.'):
398 if p.permission.permission_name.startswith('repository.'):
399 default = p.permission.permission_name
399 default = p.permission.permission_name
400 break
400 break
401
401
402 default_perm = 'repository.none' if private else default
402 default_perm = 'repository.none' if private else default
403
403
404 repo_to_perm.permission_id = self.sa.query(Permission)\
404 repo_to_perm.permission_id = self.sa.query(Permission)\
405 .filter(Permission.permission_name == default_perm)\
405 .filter(Permission.permission_name == default_perm)\
406 .one().permission_id
406 .one().permission_id
407
407
408 repo_to_perm.repository = new_repo
408 repo_to_perm.repository = new_repo
409 repo_to_perm.user_id = User.get_by_username('default').user_id
409 repo_to_perm.user_id = User.get_by_username('default').user_id
410
410
411 self.sa.add(repo_to_perm)
411 self.sa.add(repo_to_perm)
412
412
413 if fork_of:
413 if fork_of:
414 if copy_fork_permissions:
414 if copy_fork_permissions:
415 repo = fork_of
415 repo = fork_of
416 user_perms = UserRepoToPerm.query()\
416 user_perms = UserRepoToPerm.query()\
417 .filter(UserRepoToPerm.repository == repo).all()
417 .filter(UserRepoToPerm.repository == repo).all()
418 group_perms = UsersGroupRepoToPerm.query()\
418 group_perms = UsersGroupRepoToPerm.query()\
419 .filter(UsersGroupRepoToPerm.repository == repo).all()
419 .filter(UsersGroupRepoToPerm.repository == repo).all()
420
420
421 for perm in user_perms:
421 for perm in user_perms:
422 UserRepoToPerm.create(perm.user, new_repo,
422 UserRepoToPerm.create(perm.user, new_repo,
423 perm.permission)
423 perm.permission)
424
424
425 for perm in group_perms:
425 for perm in group_perms:
426 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
426 UsersGroupRepoToPerm.create(perm.users_group, new_repo,
427 perm.permission)
427 perm.permission)
428 else:
428 else:
429 _create_default_perms()
429 _create_default_perms()
430 else:
430 else:
431 _create_default_perms()
431 _create_default_perms()
432
432
433 if not just_db:
433 if not just_db:
434 self.__create_repo(repo_name, repo_type,
434 self.__create_repo(repo_name, repo_type,
435 repos_group,
435 repos_group,
436 clone_uri)
436 clone_uri)
437 log_create_repository(new_repo.get_dict(),
437 log_create_repository(new_repo.get_dict(),
438 created_by=owner.username)
438 created_by=owner.username)
439
439
440 # now automatically start following this repository as owner
440 # now automatically start following this repository as owner
441 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
441 ScmModel(self.sa).toggle_following_repo(new_repo.repo_id,
442 owner.user_id)
442 owner.user_id)
443 return new_repo
443 return new_repo
444 except:
444 except:
445 log.error(traceback.format_exc())
445 log.error(traceback.format_exc())
446 raise
446 raise
447
447
448 def create(self, form_data, cur_user, just_db=False, fork=None):
448 def create(self, form_data, cur_user, just_db=False, fork=None):
449 """
449 """
450 Backward compatibility function, just a wrapper on top of create_repo
450 Backward compatibility function, just a wrapper on top of create_repo
451
451
452 :param form_data:
452 :param form_data:
453 :param cur_user:
453 :param cur_user:
454 :param just_db:
454 :param just_db:
455 :param fork:
455 :param fork:
456 """
456 """
457 owner = cur_user
457 owner = cur_user
458 repo_name = form_data['repo_name_full']
458 repo_name = form_data['repo_name_full']
459 repo_type = form_data['repo_type']
459 repo_type = form_data['repo_type']
460 description = form_data['repo_description']
460 description = form_data['repo_description']
461 private = form_data['repo_private']
461 private = form_data['repo_private']
462 clone_uri = form_data.get('clone_uri')
462 clone_uri = form_data.get('clone_uri')
463 repos_group = form_data['repo_group']
463 repos_group = form_data['repo_group']
464 landing_rev = form_data['repo_landing_rev']
464 landing_rev = form_data['repo_landing_rev']
465 copy_fork_permissions = form_data.get('copy_permissions')
465 copy_fork_permissions = form_data.get('copy_permissions')
466 fork_of = form_data.get('fork_parent_id')
466 fork_of = form_data.get('fork_parent_id')
467
467
468 ## repo creation defaults, private and repo_type are filled in form
468 ## repo creation defaults, private and repo_type are filled in form
469 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
469 defs = RhodeCodeSetting.get_default_repo_settings(strip_prefix=True)
470 enable_statistics = defs.get('repo_enable_statistics')
470 enable_statistics = defs.get('repo_enable_statistics')
471 enable_locking = defs.get('repo_enable_locking')
471 enable_locking = defs.get('repo_enable_locking')
472 enable_downloads = defs.get('repo_enable_downloads')
472 enable_downloads = defs.get('repo_enable_downloads')
473
473
474 return self.create_repo(
474 return self.create_repo(
475 repo_name, repo_type, description, owner, private, clone_uri,
475 repo_name, repo_type, description, owner, private, clone_uri,
476 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
476 repos_group, landing_rev, just_db, fork_of, copy_fork_permissions,
477 enable_statistics, enable_locking, enable_downloads
477 enable_statistics, enable_locking, enable_downloads
478 )
478 )
479
479
480 def create_fork(self, form_data, cur_user):
480 def create_fork(self, form_data, cur_user):
481 """
481 """
482 Simple wrapper into executing celery task for fork creation
482 Simple wrapper into executing celery task for fork creation
483
483
484 :param form_data:
484 :param form_data:
485 :param cur_user:
485 :param cur_user:
486 """
486 """
487 from rhodecode.lib.celerylib import tasks, run_task
487 from rhodecode.lib.celerylib import tasks, run_task
488 run_task(tasks.create_repo_fork, form_data, cur_user)
488 run_task(tasks.create_repo_fork, form_data, cur_user)
489
489
490 def delete(self, repo):
490 def delete(self, repo):
491 repo = self._get_repo(repo)
491 repo = self._get_repo(repo)
492 if repo:
492 if repo:
493 old_repo_dict = repo.get_dict()
493 old_repo_dict = repo.get_dict()
494 owner = repo.user
494 owner = repo.user
495 try:
495 try:
496 self.sa.delete(repo)
496 self.sa.delete(repo)
497 self.__delete_repo(repo)
497 self.__delete_repo(repo)
498 log_delete_repository(old_repo_dict,
498 log_delete_repository(old_repo_dict,
499 deleted_by=owner.username)
499 deleted_by=owner.username)
500 except:
500 except:
501 log.error(traceback.format_exc())
501 log.error(traceback.format_exc())
502 raise
502 raise
503
503
504 def grant_user_permission(self, repo, user, perm):
504 def grant_user_permission(self, repo, user, perm):
505 """
505 """
506 Grant permission for user on given repository, or update existing one
506 Grant permission for user on given repository, or update existing one
507 if found
507 if found
508
508
509 :param repo: Instance of Repository, repository_id, or repository name
509 :param repo: Instance of Repository, repository_id, or repository name
510 :param user: Instance of User, user_id or username
510 :param user: Instance of User, user_id or username
511 :param perm: Instance of Permission, or permission_name
511 :param perm: Instance of Permission, or permission_name
512 """
512 """
513 user = self._get_user(user)
513 user = self._get_user(user)
514 repo = self._get_repo(repo)
514 repo = self._get_repo(repo)
515 permission = self._get_perm(perm)
515 permission = self._get_perm(perm)
516
516
517 # check if we have that permission already
517 # check if we have that permission already
518 obj = self.sa.query(UserRepoToPerm)\
518 obj = self.sa.query(UserRepoToPerm)\
519 .filter(UserRepoToPerm.user == user)\
519 .filter(UserRepoToPerm.user == user)\
520 .filter(UserRepoToPerm.repository == repo)\
520 .filter(UserRepoToPerm.repository == repo)\
521 .scalar()
521 .scalar()
522 if obj is None:
522 if obj is None:
523 # create new !
523 # create new !
524 obj = UserRepoToPerm()
524 obj = UserRepoToPerm()
525 obj.repository = repo
525 obj.repository = repo
526 obj.user = user
526 obj.user = user
527 obj.permission = permission
527 obj.permission = permission
528 self.sa.add(obj)
528 self.sa.add(obj)
529 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
529 log.debug('Granted perm %s to %s on %s' % (perm, user, repo))
530
530
531 def revoke_user_permission(self, repo, user):
531 def revoke_user_permission(self, repo, user):
532 """
532 """
533 Revoke permission for user on given repository
533 Revoke permission for user on given repository
534
534
535 :param repo: Instance of Repository, repository_id, or repository name
535 :param repo: Instance of Repository, repository_id, or repository name
536 :param user: Instance of User, user_id or username
536 :param user: Instance of User, user_id or username
537 """
537 """
538
538
539 user = self._get_user(user)
539 user = self._get_user(user)
540 repo = self._get_repo(repo)
540 repo = self._get_repo(repo)
541
541
542 obj = self.sa.query(UserRepoToPerm)\
542 obj = self.sa.query(UserRepoToPerm)\
543 .filter(UserRepoToPerm.repository == repo)\
543 .filter(UserRepoToPerm.repository == repo)\
544 .filter(UserRepoToPerm.user == user)\
544 .filter(UserRepoToPerm.user == user)\
545 .scalar()
545 .scalar()
546 if obj:
546 if obj:
547 self.sa.delete(obj)
547 self.sa.delete(obj)
548 log.debug('Revoked perm on %s on %s' % (repo, user))
548 log.debug('Revoked perm on %s on %s' % (repo, user))
549
549
550 def grant_users_group_permission(self, repo, group_name, perm):
550 def grant_users_group_permission(self, repo, group_name, perm):
551 """
551 """
552 Grant permission for users group on given repository, or update
552 Grant permission for user group on given repository, or update
553 existing one if found
553 existing one if found
554
554
555 :param repo: Instance of Repository, repository_id, or repository name
555 :param repo: Instance of Repository, repository_id, or repository name
556 :param group_name: Instance of UserGroup, users_group_id,
556 :param group_name: Instance of UserGroup, users_group_id,
557 or users group name
557 or user group name
558 :param perm: Instance of Permission, or permission_name
558 :param perm: Instance of Permission, or permission_name
559 """
559 """
560 repo = self._get_repo(repo)
560 repo = self._get_repo(repo)
561 group_name = self.__get_users_group(group_name)
561 group_name = self.__get_users_group(group_name)
562 permission = self._get_perm(perm)
562 permission = self._get_perm(perm)
563
563
564 # check if we have that permission already
564 # check if we have that permission already
565 obj = self.sa.query(UsersGroupRepoToPerm)\
565 obj = self.sa.query(UsersGroupRepoToPerm)\
566 .filter(UsersGroupRepoToPerm.users_group == group_name)\
566 .filter(UsersGroupRepoToPerm.users_group == group_name)\
567 .filter(UsersGroupRepoToPerm.repository == repo)\
567 .filter(UsersGroupRepoToPerm.repository == repo)\
568 .scalar()
568 .scalar()
569
569
570 if obj is None:
570 if obj is None:
571 # create new
571 # create new
572 obj = UsersGroupRepoToPerm()
572 obj = UsersGroupRepoToPerm()
573
573
574 obj.repository = repo
574 obj.repository = repo
575 obj.users_group = group_name
575 obj.users_group = group_name
576 obj.permission = permission
576 obj.permission = permission
577 self.sa.add(obj)
577 self.sa.add(obj)
578 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
578 log.debug('Granted perm %s to %s on %s' % (perm, group_name, repo))
579
579
580 def revoke_users_group_permission(self, repo, group_name):
580 def revoke_users_group_permission(self, repo, group_name):
581 """
581 """
582 Revoke permission for users group on given repository
582 Revoke permission for user group on given repository
583
583
584 :param repo: Instance of Repository, repository_id, or repository name
584 :param repo: Instance of Repository, repository_id, or repository name
585 :param group_name: Instance of UserGroup, users_group_id,
585 :param group_name: Instance of UserGroup, users_group_id,
586 or users group name
586 or user group name
587 """
587 """
588 repo = self._get_repo(repo)
588 repo = self._get_repo(repo)
589 group_name = self.__get_users_group(group_name)
589 group_name = self.__get_users_group(group_name)
590
590
591 obj = self.sa.query(UsersGroupRepoToPerm)\
591 obj = self.sa.query(UsersGroupRepoToPerm)\
592 .filter(UsersGroupRepoToPerm.repository == repo)\
592 .filter(UsersGroupRepoToPerm.repository == repo)\
593 .filter(UsersGroupRepoToPerm.users_group == group_name)\
593 .filter(UsersGroupRepoToPerm.users_group == group_name)\
594 .scalar()
594 .scalar()
595 if obj:
595 if obj:
596 self.sa.delete(obj)
596 self.sa.delete(obj)
597 log.debug('Revoked perm to %s on %s' % (repo, group_name))
597 log.debug('Revoked perm to %s on %s' % (repo, group_name))
598
598
599 def delete_stats(self, repo_name):
599 def delete_stats(self, repo_name):
600 """
600 """
601 removes stats for given repo
601 removes stats for given repo
602
602
603 :param repo_name:
603 :param repo_name:
604 """
604 """
605 repo = self._get_repo(repo_name)
605 repo = self._get_repo(repo_name)
606 try:
606 try:
607 obj = self.sa.query(Statistics)\
607 obj = self.sa.query(Statistics)\
608 .filter(Statistics.repository == repo).scalar()
608 .filter(Statistics.repository == repo).scalar()
609 if obj:
609 if obj:
610 self.sa.delete(obj)
610 self.sa.delete(obj)
611 except:
611 except:
612 log.error(traceback.format_exc())
612 log.error(traceback.format_exc())
613 raise
613 raise
614
614
615 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
615 def __create_repo(self, repo_name, alias, parent, clone_uri=False):
616 """
616 """
617 makes repository on filesystem. It's group aware means it'll create
617 makes repository on filesystem. It's group aware means it'll create
618 a repository within a group, and alter the paths accordingly of
618 a repository within a group, and alter the paths accordingly of
619 group location
619 group location
620
620
621 :param repo_name:
621 :param repo_name:
622 :param alias:
622 :param alias:
623 :param parent_id:
623 :param parent_id:
624 :param clone_uri:
624 :param clone_uri:
625 """
625 """
626 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
626 from rhodecode.lib.utils import is_valid_repo, is_valid_repos_group
627 from rhodecode.model.scm import ScmModel
627 from rhodecode.model.scm import ScmModel
628
628
629 if parent:
629 if parent:
630 new_parent_path = os.sep.join(parent.full_path_splitted)
630 new_parent_path = os.sep.join(parent.full_path_splitted)
631 else:
631 else:
632 new_parent_path = ''
632 new_parent_path = ''
633
633
634 # we need to make it str for mercurial
634 # we need to make it str for mercurial
635 repo_path = os.path.join(*map(lambda x: safe_str(x),
635 repo_path = os.path.join(*map(lambda x: safe_str(x),
636 [self.repos_path, new_parent_path, repo_name]))
636 [self.repos_path, new_parent_path, repo_name]))
637
637
638 # check if this path is not a repository
638 # check if this path is not a repository
639 if is_valid_repo(repo_path, self.repos_path):
639 if is_valid_repo(repo_path, self.repos_path):
640 raise Exception('This path %s is a valid repository' % repo_path)
640 raise Exception('This path %s is a valid repository' % repo_path)
641
641
642 # check if this path is a group
642 # check if this path is a group
643 if is_valid_repos_group(repo_path, self.repos_path):
643 if is_valid_repos_group(repo_path, self.repos_path):
644 raise Exception('This path %s is a valid group' % repo_path)
644 raise Exception('This path %s is a valid group' % repo_path)
645
645
646 log.info('creating repo %s in %s @ %s' % (
646 log.info('creating repo %s in %s @ %s' % (
647 repo_name, safe_unicode(repo_path), clone_uri
647 repo_name, safe_unicode(repo_path), clone_uri
648 )
648 )
649 )
649 )
650 backend = get_backend(alias)
650 backend = get_backend(alias)
651 if alias == 'hg':
651 if alias == 'hg':
652 backend(repo_path, create=True, src_url=clone_uri)
652 backend(repo_path, create=True, src_url=clone_uri)
653 elif alias == 'git':
653 elif alias == 'git':
654 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
654 r = backend(repo_path, create=True, src_url=clone_uri, bare=True)
655 # add rhodecode hook into this repo
655 # add rhodecode hook into this repo
656 ScmModel().install_git_hook(repo=r)
656 ScmModel().install_git_hook(repo=r)
657 else:
657 else:
658 raise Exception('Undefined alias %s' % alias)
658 raise Exception('Undefined alias %s' % alias)
659
659
660 def __rename_repo(self, old, new):
660 def __rename_repo(self, old, new):
661 """
661 """
662 renames repository on filesystem
662 renames repository on filesystem
663
663
664 :param old: old name
664 :param old: old name
665 :param new: new name
665 :param new: new name
666 """
666 """
667 log.info('renaming repo from %s to %s' % (old, new))
667 log.info('renaming repo from %s to %s' % (old, new))
668
668
669 old_path = os.path.join(self.repos_path, old)
669 old_path = os.path.join(self.repos_path, old)
670 new_path = os.path.join(self.repos_path, new)
670 new_path = os.path.join(self.repos_path, new)
671 if os.path.isdir(new_path):
671 if os.path.isdir(new_path):
672 raise Exception(
672 raise Exception(
673 'Was trying to rename to already existing dir %s' % new_path
673 'Was trying to rename to already existing dir %s' % new_path
674 )
674 )
675 shutil.move(old_path, new_path)
675 shutil.move(old_path, new_path)
676
676
677 def __delete_repo(self, repo):
677 def __delete_repo(self, repo):
678 """
678 """
679 removes repo from filesystem, the removal is acctually made by
679 removes repo from filesystem, the removal is acctually made by
680 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
680 added rm__ prefix into dir, and rename internat .hg/.git dirs so this
681 repository is no longer valid for rhodecode, can be undeleted later on
681 repository is no longer valid for rhodecode, can be undeleted later on
682 by reverting the renames on this repository
682 by reverting the renames on this repository
683
683
684 :param repo: repo object
684 :param repo: repo object
685 """
685 """
686 rm_path = os.path.join(self.repos_path, repo.repo_name)
686 rm_path = os.path.join(self.repos_path, repo.repo_name)
687 log.info("Removing %s" % (rm_path))
687 log.info("Removing %s" % (rm_path))
688 # disable hg/git internal that it doesn't get detected as repo
688 # disable hg/git internal that it doesn't get detected as repo
689 alias = repo.repo_type
689 alias = repo.repo_type
690
690
691 bare = getattr(repo.scm_instance, 'bare', False)
691 bare = getattr(repo.scm_instance, 'bare', False)
692
692
693 if not bare:
693 if not bare:
694 # skip this for bare git repos
694 # skip this for bare git repos
695 shutil.move(os.path.join(rm_path, '.%s' % alias),
695 shutil.move(os.path.join(rm_path, '.%s' % alias),
696 os.path.join(rm_path, 'rm__.%s' % alias))
696 os.path.join(rm_path, 'rm__.%s' % alias))
697 # disable repo
697 # disable repo
698 _now = datetime.now()
698 _now = datetime.now()
699 _ms = str(_now.microsecond).rjust(6, '0')
699 _ms = str(_now.microsecond).rjust(6, '0')
700 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
700 _d = 'rm__%s__%s' % (_now.strftime('%Y%m%d_%H%M%S_' + _ms),
701 repo.just_name)
701 repo.just_name)
702 if repo.group:
702 if repo.group:
703 args = repo.group.full_path_splitted + [_d]
703 args = repo.group.full_path_splitted + [_d]
704 _d = os.path.join(*args)
704 _d = os.path.join(*args)
705 shutil.move(rm_path, os.path.join(self.repos_path, _d))
705 shutil.move(rm_path, os.path.join(self.repos_path, _d))
@@ -1,763 +1,763 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.model.user
3 rhodecode.model.user
4 ~~~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~~~
5
5
6 users model for RhodeCode
6 users model for RhodeCode
7
7
8 :created_on: Apr 9, 2010
8 :created_on: Apr 9, 2010
9 :author: marcink
9 :author: marcink
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
10 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :license: GPLv3, see COPYING for more details.
11 :license: GPLv3, see COPYING for more details.
12 """
12 """
13 # This program is free software: you can redistribute it and/or modify
13 # This program is free software: you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation, either version 3 of the License, or
15 # the Free Software Foundation, either version 3 of the License, or
16 # (at your option) any later version.
16 # (at your option) any later version.
17 #
17 #
18 # This program is distributed in the hope that it will be useful,
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # GNU General Public License for more details.
21 # GNU General Public License for more details.
22 #
22 #
23 # You should have received a copy of the GNU General Public License
23 # You should have received a copy of the GNU General Public License
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25
25
26 import logging
26 import logging
27 import traceback
27 import traceback
28 import itertools
28 import itertools
29 import collections
29 import collections
30 from pylons import url
30 from pylons import url
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 from sqlalchemy.exc import DatabaseError
33 from sqlalchemy.exc import DatabaseError
34 from sqlalchemy.orm import joinedload
34 from sqlalchemy.orm import joinedload
35
35
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
36 from rhodecode.lib.utils2 import safe_unicode, generate_api_key
37 from rhodecode.lib.caching_query import FromCache
37 from rhodecode.lib.caching_query import FromCache
38 from rhodecode.model import BaseModel
38 from rhodecode.model import BaseModel
39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
39 from rhodecode.model.db import User, UserRepoToPerm, Repository, Permission, \
40 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
40 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember, \
41 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
41 Notification, RepoGroup, UserRepoGroupToPerm, UsersGroupRepoGroupToPerm, \
42 UserEmailMap, UserIpMap
42 UserEmailMap, UserIpMap
43 from rhodecode.lib.exceptions import DefaultUserException, \
43 from rhodecode.lib.exceptions import DefaultUserException, \
44 UserOwnsReposException
44 UserOwnsReposException
45 from rhodecode.model.meta import Session
45 from rhodecode.model.meta import Session
46
46
47
47
48 log = logging.getLogger(__name__)
48 log = logging.getLogger(__name__)
49
49
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
50 PERM_WEIGHTS = Permission.PERM_WEIGHTS
51
51
52
52
53 class UserModel(BaseModel):
53 class UserModel(BaseModel):
54 cls = User
54 cls = User
55
55
56 def get(self, user_id, cache=False):
56 def get(self, user_id, cache=False):
57 user = self.sa.query(User)
57 user = self.sa.query(User)
58 if cache:
58 if cache:
59 user = user.options(FromCache("sql_cache_short",
59 user = user.options(FromCache("sql_cache_short",
60 "get_user_%s" % user_id))
60 "get_user_%s" % user_id))
61 return user.get(user_id)
61 return user.get(user_id)
62
62
63 def get_user(self, user):
63 def get_user(self, user):
64 return self._get_user(user)
64 return self._get_user(user)
65
65
66 def get_by_username(self, username, cache=False, case_insensitive=False):
66 def get_by_username(self, username, cache=False, case_insensitive=False):
67
67
68 if case_insensitive:
68 if case_insensitive:
69 user = self.sa.query(User).filter(User.username.ilike(username))
69 user = self.sa.query(User).filter(User.username.ilike(username))
70 else:
70 else:
71 user = self.sa.query(User)\
71 user = self.sa.query(User)\
72 .filter(User.username == username)
72 .filter(User.username == username)
73 if cache:
73 if cache:
74 user = user.options(FromCache("sql_cache_short",
74 user = user.options(FromCache("sql_cache_short",
75 "get_user_%s" % username))
75 "get_user_%s" % username))
76 return user.scalar()
76 return user.scalar()
77
77
78 def get_by_email(self, email, cache=False, case_insensitive=False):
78 def get_by_email(self, email, cache=False, case_insensitive=False):
79 return User.get_by_email(email, case_insensitive, cache)
79 return User.get_by_email(email, case_insensitive, cache)
80
80
81 def get_by_api_key(self, api_key, cache=False):
81 def get_by_api_key(self, api_key, cache=False):
82 return User.get_by_api_key(api_key, cache)
82 return User.get_by_api_key(api_key, cache)
83
83
84 def create(self, form_data):
84 def create(self, form_data):
85 from rhodecode.lib.auth import get_crypt_password
85 from rhodecode.lib.auth import get_crypt_password
86 try:
86 try:
87 new_user = User()
87 new_user = User()
88 for k, v in form_data.items():
88 for k, v in form_data.items():
89 if k == 'password':
89 if k == 'password':
90 v = get_crypt_password(v)
90 v = get_crypt_password(v)
91 if k == 'firstname':
91 if k == 'firstname':
92 k = 'name'
92 k = 'name'
93 setattr(new_user, k, v)
93 setattr(new_user, k, v)
94
94
95 new_user.api_key = generate_api_key(form_data['username'])
95 new_user.api_key = generate_api_key(form_data['username'])
96 self.sa.add(new_user)
96 self.sa.add(new_user)
97 return new_user
97 return new_user
98 except:
98 except:
99 log.error(traceback.format_exc())
99 log.error(traceback.format_exc())
100 raise
100 raise
101
101
102 def create_or_update(self, username, password, email, firstname='',
102 def create_or_update(self, username, password, email, firstname='',
103 lastname='', active=True, admin=False, ldap_dn=None):
103 lastname='', active=True, admin=False, ldap_dn=None):
104 """
104 """
105 Creates a new instance if not found, or updates current one
105 Creates a new instance if not found, or updates current one
106
106
107 :param username:
107 :param username:
108 :param password:
108 :param password:
109 :param email:
109 :param email:
110 :param active:
110 :param active:
111 :param firstname:
111 :param firstname:
112 :param lastname:
112 :param lastname:
113 :param active:
113 :param active:
114 :param admin:
114 :param admin:
115 :param ldap_dn:
115 :param ldap_dn:
116 """
116 """
117
117
118 from rhodecode.lib.auth import get_crypt_password
118 from rhodecode.lib.auth import get_crypt_password
119
119
120 log.debug('Checking for %s account in RhodeCode database' % username)
120 log.debug('Checking for %s account in RhodeCode database' % username)
121 user = User.get_by_username(username, case_insensitive=True)
121 user = User.get_by_username(username, case_insensitive=True)
122 if user is None:
122 if user is None:
123 log.debug('creating new user %s' % username)
123 log.debug('creating new user %s' % username)
124 new_user = User()
124 new_user = User()
125 edit = False
125 edit = False
126 else:
126 else:
127 log.debug('updating user %s' % username)
127 log.debug('updating user %s' % username)
128 new_user = user
128 new_user = user
129 edit = True
129 edit = True
130
130
131 try:
131 try:
132 new_user.username = username
132 new_user.username = username
133 new_user.admin = admin
133 new_user.admin = admin
134 # set password only if creating an user or password is changed
134 # set password only if creating an user or password is changed
135 if edit is False or user.password != password:
135 if edit is False or user.password != password:
136 new_user.password = get_crypt_password(password)
136 new_user.password = get_crypt_password(password)
137 new_user.api_key = generate_api_key(username)
137 new_user.api_key = generate_api_key(username)
138 new_user.email = email
138 new_user.email = email
139 new_user.active = active
139 new_user.active = active
140 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
140 new_user.ldap_dn = safe_unicode(ldap_dn) if ldap_dn else None
141 new_user.name = firstname
141 new_user.name = firstname
142 new_user.lastname = lastname
142 new_user.lastname = lastname
143 self.sa.add(new_user)
143 self.sa.add(new_user)
144 return new_user
144 return new_user
145 except (DatabaseError,):
145 except (DatabaseError,):
146 log.error(traceback.format_exc())
146 log.error(traceback.format_exc())
147 raise
147 raise
148
148
149 def create_for_container_auth(self, username, attrs):
149 def create_for_container_auth(self, username, attrs):
150 """
150 """
151 Creates the given user if it's not already in the database
151 Creates the given user if it's not already in the database
152
152
153 :param username:
153 :param username:
154 :param attrs:
154 :param attrs:
155 """
155 """
156 if self.get_by_username(username, case_insensitive=True) is None:
156 if self.get_by_username(username, case_insensitive=True) is None:
157
157
158 # autogenerate email for container account without one
158 # autogenerate email for container account without one
159 generate_email = lambda usr: '%s@container_auth.account' % usr
159 generate_email = lambda usr: '%s@container_auth.account' % usr
160
160
161 try:
161 try:
162 new_user = User()
162 new_user = User()
163 new_user.username = username
163 new_user.username = username
164 new_user.password = None
164 new_user.password = None
165 new_user.api_key = generate_api_key(username)
165 new_user.api_key = generate_api_key(username)
166 new_user.email = attrs['email']
166 new_user.email = attrs['email']
167 new_user.active = attrs.get('active', True)
167 new_user.active = attrs.get('active', True)
168 new_user.name = attrs['name'] or generate_email(username)
168 new_user.name = attrs['name'] or generate_email(username)
169 new_user.lastname = attrs['lastname']
169 new_user.lastname = attrs['lastname']
170
170
171 self.sa.add(new_user)
171 self.sa.add(new_user)
172 return new_user
172 return new_user
173 except (DatabaseError,):
173 except (DatabaseError,):
174 log.error(traceback.format_exc())
174 log.error(traceback.format_exc())
175 self.sa.rollback()
175 self.sa.rollback()
176 raise
176 raise
177 log.debug('User %s already exists. Skipping creation of account'
177 log.debug('User %s already exists. Skipping creation of account'
178 ' for container auth.', username)
178 ' for container auth.', username)
179 return None
179 return None
180
180
181 def create_ldap(self, username, password, user_dn, attrs):
181 def create_ldap(self, username, password, user_dn, attrs):
182 """
182 """
183 Checks if user is in database, if not creates this user marked
183 Checks if user is in database, if not creates this user marked
184 as ldap user
184 as ldap user
185
185
186 :param username:
186 :param username:
187 :param password:
187 :param password:
188 :param user_dn:
188 :param user_dn:
189 :param attrs:
189 :param attrs:
190 """
190 """
191 from rhodecode.lib.auth import get_crypt_password
191 from rhodecode.lib.auth import get_crypt_password
192 log.debug('Checking for such ldap account in RhodeCode database')
192 log.debug('Checking for such ldap account in RhodeCode database')
193 if self.get_by_username(username, case_insensitive=True) is None:
193 if self.get_by_username(username, case_insensitive=True) is None:
194
194
195 # autogenerate email for ldap account without one
195 # autogenerate email for ldap account without one
196 generate_email = lambda usr: '%s@ldap.account' % usr
196 generate_email = lambda usr: '%s@ldap.account' % usr
197
197
198 try:
198 try:
199 new_user = User()
199 new_user = User()
200 username = username.lower()
200 username = username.lower()
201 # add ldap account always lowercase
201 # add ldap account always lowercase
202 new_user.username = username
202 new_user.username = username
203 new_user.password = get_crypt_password(password)
203 new_user.password = get_crypt_password(password)
204 new_user.api_key = generate_api_key(username)
204 new_user.api_key = generate_api_key(username)
205 new_user.email = attrs['email'] or generate_email(username)
205 new_user.email = attrs['email'] or generate_email(username)
206 new_user.active = attrs.get('active', True)
206 new_user.active = attrs.get('active', True)
207 new_user.ldap_dn = safe_unicode(user_dn)
207 new_user.ldap_dn = safe_unicode(user_dn)
208 new_user.name = attrs['name']
208 new_user.name = attrs['name']
209 new_user.lastname = attrs['lastname']
209 new_user.lastname = attrs['lastname']
210
210
211 self.sa.add(new_user)
211 self.sa.add(new_user)
212 return new_user
212 return new_user
213 except (DatabaseError,):
213 except (DatabaseError,):
214 log.error(traceback.format_exc())
214 log.error(traceback.format_exc())
215 self.sa.rollback()
215 self.sa.rollback()
216 raise
216 raise
217 log.debug('this %s user exists skipping creation of ldap account',
217 log.debug('this %s user exists skipping creation of ldap account',
218 username)
218 username)
219 return None
219 return None
220
220
221 def create_registration(self, form_data):
221 def create_registration(self, form_data):
222 from rhodecode.model.notification import NotificationModel
222 from rhodecode.model.notification import NotificationModel
223
223
224 try:
224 try:
225 form_data['admin'] = False
225 form_data['admin'] = False
226 new_user = self.create(form_data)
226 new_user = self.create(form_data)
227
227
228 self.sa.add(new_user)
228 self.sa.add(new_user)
229 self.sa.flush()
229 self.sa.flush()
230
230
231 # notification to admins
231 # notification to admins
232 subject = _('new user registration')
232 subject = _('new user registration')
233 body = ('New user registration\n'
233 body = ('New user registration\n'
234 '---------------------\n'
234 '---------------------\n'
235 '- Username: %s\n'
235 '- Username: %s\n'
236 '- Full Name: %s\n'
236 '- Full Name: %s\n'
237 '- Email: %s\n')
237 '- Email: %s\n')
238 body = body % (new_user.username, new_user.full_name,
238 body = body % (new_user.username, new_user.full_name,
239 new_user.email)
239 new_user.email)
240 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
240 edit_url = url('edit_user', id=new_user.user_id, qualified=True)
241 kw = {'registered_user_url': edit_url}
241 kw = {'registered_user_url': edit_url}
242 NotificationModel().create(created_by=new_user, subject=subject,
242 NotificationModel().create(created_by=new_user, subject=subject,
243 body=body, recipients=None,
243 body=body, recipients=None,
244 type_=Notification.TYPE_REGISTRATION,
244 type_=Notification.TYPE_REGISTRATION,
245 email_kwargs=kw)
245 email_kwargs=kw)
246
246
247 except:
247 except:
248 log.error(traceback.format_exc())
248 log.error(traceback.format_exc())
249 raise
249 raise
250
250
251 def update(self, user_id, form_data, skip_attrs=[]):
251 def update(self, user_id, form_data, skip_attrs=[]):
252 from rhodecode.lib.auth import get_crypt_password
252 from rhodecode.lib.auth import get_crypt_password
253 try:
253 try:
254 user = self.get(user_id, cache=False)
254 user = self.get(user_id, cache=False)
255 if user.username == 'default':
255 if user.username == 'default':
256 raise DefaultUserException(
256 raise DefaultUserException(
257 _("You can't Edit this user since it's"
257 _("You can't Edit this user since it's"
258 " crucial for entire application"))
258 " crucial for entire application"))
259
259
260 for k, v in form_data.items():
260 for k, v in form_data.items():
261 if k in skip_attrs:
261 if k in skip_attrs:
262 continue
262 continue
263 if k == 'new_password' and v:
263 if k == 'new_password' and v:
264 user.password = get_crypt_password(v)
264 user.password = get_crypt_password(v)
265 user.api_key = generate_api_key(user.username)
265 user.api_key = generate_api_key(user.username)
266 else:
266 else:
267 if k == 'firstname':
267 if k == 'firstname':
268 k = 'name'
268 k = 'name'
269 setattr(user, k, v)
269 setattr(user, k, v)
270 self.sa.add(user)
270 self.sa.add(user)
271 except:
271 except:
272 log.error(traceback.format_exc())
272 log.error(traceback.format_exc())
273 raise
273 raise
274
274
275 def update_user(self, user, **kwargs):
275 def update_user(self, user, **kwargs):
276 from rhodecode.lib.auth import get_crypt_password
276 from rhodecode.lib.auth import get_crypt_password
277 try:
277 try:
278 user = self._get_user(user)
278 user = self._get_user(user)
279 if user.username == 'default':
279 if user.username == 'default':
280 raise DefaultUserException(
280 raise DefaultUserException(
281 _("You can't Edit this user since it's"
281 _("You can't Edit this user since it's"
282 " crucial for entire application")
282 " crucial for entire application")
283 )
283 )
284
284
285 for k, v in kwargs.items():
285 for k, v in kwargs.items():
286 if k == 'password' and v:
286 if k == 'password' and v:
287 v = get_crypt_password(v)
287 v = get_crypt_password(v)
288 user.api_key = generate_api_key(user.username)
288 user.api_key = generate_api_key(user.username)
289
289
290 setattr(user, k, v)
290 setattr(user, k, v)
291 self.sa.add(user)
291 self.sa.add(user)
292 return user
292 return user
293 except:
293 except:
294 log.error(traceback.format_exc())
294 log.error(traceback.format_exc())
295 raise
295 raise
296
296
297 def delete(self, user):
297 def delete(self, user):
298 user = self._get_user(user)
298 user = self._get_user(user)
299
299
300 try:
300 try:
301 if user.username == 'default':
301 if user.username == 'default':
302 raise DefaultUserException(
302 raise DefaultUserException(
303 _(u"You can't remove this user since it's"
303 _(u"You can't remove this user since it's"
304 " crucial for entire application")
304 " crucial for entire application")
305 )
305 )
306 if user.repositories:
306 if user.repositories:
307 repos = [x.repo_name for x in user.repositories]
307 repos = [x.repo_name for x in user.repositories]
308 raise UserOwnsReposException(
308 raise UserOwnsReposException(
309 _(u'user "%s" still owns %s repositories and cannot be '
309 _(u'user "%s" still owns %s repositories and cannot be '
310 'removed. Switch owners or remove those repositories. %s')
310 'removed. Switch owners or remove those repositories. %s')
311 % (user.username, len(repos), ', '.join(repos))
311 % (user.username, len(repos), ', '.join(repos))
312 )
312 )
313 self.sa.delete(user)
313 self.sa.delete(user)
314 except:
314 except:
315 log.error(traceback.format_exc())
315 log.error(traceback.format_exc())
316 raise
316 raise
317
317
318 def reset_password_link(self, data):
318 def reset_password_link(self, data):
319 from rhodecode.lib.celerylib import tasks, run_task
319 from rhodecode.lib.celerylib import tasks, run_task
320 from rhodecode.model.notification import EmailNotificationModel
320 from rhodecode.model.notification import EmailNotificationModel
321 user_email = data['email']
321 user_email = data['email']
322 try:
322 try:
323 user = User.get_by_email(user_email)
323 user = User.get_by_email(user_email)
324 if user:
324 if user:
325 log.debug('password reset user found %s' % user)
325 log.debug('password reset user found %s' % user)
326 link = url('reset_password_confirmation', key=user.api_key,
326 link = url('reset_password_confirmation', key=user.api_key,
327 qualified=True)
327 qualified=True)
328 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
328 reg_type = EmailNotificationModel.TYPE_PASSWORD_RESET
329 body = EmailNotificationModel().get_email_tmpl(reg_type,
329 body = EmailNotificationModel().get_email_tmpl(reg_type,
330 **{'user': user.short_contact,
330 **{'user': user.short_contact,
331 'reset_url': link})
331 'reset_url': link})
332 log.debug('sending email')
332 log.debug('sending email')
333 run_task(tasks.send_email, user_email,
333 run_task(tasks.send_email, user_email,
334 _("password reset link"), body, body)
334 _("password reset link"), body, body)
335 log.info('send new password mail to %s' % user_email)
335 log.info('send new password mail to %s' % user_email)
336 else:
336 else:
337 log.debug("password reset email %s not found" % user_email)
337 log.debug("password reset email %s not found" % user_email)
338 except:
338 except:
339 log.error(traceback.format_exc())
339 log.error(traceback.format_exc())
340 return False
340 return False
341
341
342 return True
342 return True
343
343
344 def reset_password(self, data):
344 def reset_password(self, data):
345 from rhodecode.lib.celerylib import tasks, run_task
345 from rhodecode.lib.celerylib import tasks, run_task
346 from rhodecode.lib import auth
346 from rhodecode.lib import auth
347 user_email = data['email']
347 user_email = data['email']
348 try:
348 try:
349 try:
349 try:
350 user = User.get_by_email(user_email)
350 user = User.get_by_email(user_email)
351 new_passwd = auth.PasswordGenerator().gen_password(8,
351 new_passwd = auth.PasswordGenerator().gen_password(8,
352 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
352 auth.PasswordGenerator.ALPHABETS_BIG_SMALL)
353 if user:
353 if user:
354 user.password = auth.get_crypt_password(new_passwd)
354 user.password = auth.get_crypt_password(new_passwd)
355 user.api_key = auth.generate_api_key(user.username)
355 user.api_key = auth.generate_api_key(user.username)
356 Session().add(user)
356 Session().add(user)
357 Session().commit()
357 Session().commit()
358 log.info('change password for %s' % user_email)
358 log.info('change password for %s' % user_email)
359 if new_passwd is None:
359 if new_passwd is None:
360 raise Exception('unable to generate new password')
360 raise Exception('unable to generate new password')
361 except:
361 except:
362 log.error(traceback.format_exc())
362 log.error(traceback.format_exc())
363 Session().rollback()
363 Session().rollback()
364
364
365 run_task(tasks.send_email, user_email,
365 run_task(tasks.send_email, user_email,
366 _('Your new password'),
366 _('Your new password'),
367 _('Your new RhodeCode password:%s') % (new_passwd))
367 _('Your new RhodeCode password:%s') % (new_passwd))
368 log.info('send new password mail to %s' % user_email)
368 log.info('send new password mail to %s' % user_email)
369
369
370 except:
370 except:
371 log.error('Failed to update user password')
371 log.error('Failed to update user password')
372 log.error(traceback.format_exc())
372 log.error(traceback.format_exc())
373
373
374 return True
374 return True
375
375
376 def fill_data(self, auth_user, user_id=None, api_key=None):
376 def fill_data(self, auth_user, user_id=None, api_key=None):
377 """
377 """
378 Fetches auth_user by user_id,or api_key if present.
378 Fetches auth_user by user_id,or api_key if present.
379 Fills auth_user attributes with those taken from database.
379 Fills auth_user attributes with those taken from database.
380 Additionally set's is_authenitated if lookup fails
380 Additionally set's is_authenitated if lookup fails
381 present in database
381 present in database
382
382
383 :param auth_user: instance of user to set attributes
383 :param auth_user: instance of user to set attributes
384 :param user_id: user id to fetch by
384 :param user_id: user id to fetch by
385 :param api_key: api key to fetch by
385 :param api_key: api key to fetch by
386 """
386 """
387 if user_id is None and api_key is None:
387 if user_id is None and api_key is None:
388 raise Exception('You need to pass user_id or api_key')
388 raise Exception('You need to pass user_id or api_key')
389
389
390 try:
390 try:
391 if api_key:
391 if api_key:
392 dbuser = self.get_by_api_key(api_key)
392 dbuser = self.get_by_api_key(api_key)
393 else:
393 else:
394 dbuser = self.get(user_id)
394 dbuser = self.get(user_id)
395
395
396 if dbuser is not None and dbuser.active:
396 if dbuser is not None and dbuser.active:
397 log.debug('filling %s data' % dbuser)
397 log.debug('filling %s data' % dbuser)
398 for k, v in dbuser.get_dict().items():
398 for k, v in dbuser.get_dict().items():
399 setattr(auth_user, k, v)
399 setattr(auth_user, k, v)
400 else:
400 else:
401 return False
401 return False
402
402
403 except:
403 except:
404 log.error(traceback.format_exc())
404 log.error(traceback.format_exc())
405 auth_user.is_authenticated = False
405 auth_user.is_authenticated = False
406 return False
406 return False
407
407
408 return True
408 return True
409
409
410 def fill_perms(self, user, explicit=True, algo='higherwin'):
410 def fill_perms(self, user, explicit=True, algo='higherwin'):
411 """
411 """
412 Fills user permission attribute with permissions taken from database
412 Fills user permission attribute with permissions taken from database
413 works for permissions given for repositories, and for permissions that
413 works for permissions given for repositories, and for permissions that
414 are granted to groups
414 are granted to groups
415
415
416 :param user: user instance to fill his perms
416 :param user: user instance to fill his perms
417 :param explicit: In case there are permissions both for user and a group
417 :param explicit: In case there are permissions both for user and a group
418 that user is part of, explicit flag will defiine if user will
418 that user is part of, explicit flag will defiine if user will
419 explicitly override permissions from group, if it's False it will
419 explicitly override permissions from group, if it's False it will
420 make decision based on the algo
420 make decision based on the algo
421 :param algo: algorithm to decide what permission should be choose if
421 :param algo: algorithm to decide what permission should be choose if
422 it's multiple defined, eg user in two different groups. It also
422 it's multiple defined, eg user in two different groups. It also
423 decides if explicit flag is turned off how to specify the permission
423 decides if explicit flag is turned off how to specify the permission
424 for case when user is in a group + have defined separate permission
424 for case when user is in a group + have defined separate permission
425 """
425 """
426 RK = 'repositories'
426 RK = 'repositories'
427 GK = 'repositories_groups'
427 GK = 'repositories_groups'
428 GLOBAL = 'global'
428 GLOBAL = 'global'
429 user.permissions[RK] = {}
429 user.permissions[RK] = {}
430 user.permissions[GK] = {}
430 user.permissions[GK] = {}
431 user.permissions[GLOBAL] = set()
431 user.permissions[GLOBAL] = set()
432
432
433 def _choose_perm(new_perm, cur_perm):
433 def _choose_perm(new_perm, cur_perm):
434 new_perm_val = PERM_WEIGHTS[new_perm]
434 new_perm_val = PERM_WEIGHTS[new_perm]
435 cur_perm_val = PERM_WEIGHTS[cur_perm]
435 cur_perm_val = PERM_WEIGHTS[cur_perm]
436 if algo == 'higherwin':
436 if algo == 'higherwin':
437 if new_perm_val > cur_perm_val:
437 if new_perm_val > cur_perm_val:
438 return new_perm
438 return new_perm
439 return cur_perm
439 return cur_perm
440 elif algo == 'lowerwin':
440 elif algo == 'lowerwin':
441 if new_perm_val < cur_perm_val:
441 if new_perm_val < cur_perm_val:
442 return new_perm
442 return new_perm
443 return cur_perm
443 return cur_perm
444
444
445 #======================================================================
445 #======================================================================
446 # fetch default permissions
446 # fetch default permissions
447 #======================================================================
447 #======================================================================
448 default_user = User.get_by_username('default', cache=True)
448 default_user = User.get_by_username('default', cache=True)
449 default_user_id = default_user.user_id
449 default_user_id = default_user.user_id
450
450
451 default_repo_perms = Permission.get_default_perms(default_user_id)
451 default_repo_perms = Permission.get_default_perms(default_user_id)
452 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
452 default_repo_groups_perms = Permission.get_default_group_perms(default_user_id)
453
453
454 if user.is_admin:
454 if user.is_admin:
455 #==================================================================
455 #==================================================================
456 # admin user have all default rights for repositories
456 # admin user have all default rights for repositories
457 # and groups set to admin
457 # and groups set to admin
458 #==================================================================
458 #==================================================================
459 user.permissions[GLOBAL].add('hg.admin')
459 user.permissions[GLOBAL].add('hg.admin')
460
460
461 # repositories
461 # repositories
462 for perm in default_repo_perms:
462 for perm in default_repo_perms:
463 r_k = perm.UserRepoToPerm.repository.repo_name
463 r_k = perm.UserRepoToPerm.repository.repo_name
464 p = 'repository.admin'
464 p = 'repository.admin'
465 user.permissions[RK][r_k] = p
465 user.permissions[RK][r_k] = p
466
466
467 # repository groups
467 # repository groups
468 for perm in default_repo_groups_perms:
468 for perm in default_repo_groups_perms:
469 rg_k = perm.UserRepoGroupToPerm.group.group_name
469 rg_k = perm.UserRepoGroupToPerm.group.group_name
470 p = 'group.admin'
470 p = 'group.admin'
471 user.permissions[GK][rg_k] = p
471 user.permissions[GK][rg_k] = p
472 return user
472 return user
473
473
474 #==================================================================
474 #==================================================================
475 # SET DEFAULTS GLOBAL, REPOS, REPOS GROUPS
475 # SET DEFAULTS GLOBAL, REPOS, REPOS GROUPS
476 #==================================================================
476 #==================================================================
477 uid = user.user_id
477 uid = user.user_id
478
478
479 # default global permissions taken fron the default user
479 # default global permissions taken fron the default user
480 default_global_perms = self.sa.query(UserToPerm)\
480 default_global_perms = self.sa.query(UserToPerm)\
481 .filter(UserToPerm.user_id == default_user_id)
481 .filter(UserToPerm.user_id == default_user_id)
482
482
483 for perm in default_global_perms:
483 for perm in default_global_perms:
484 user.permissions[GLOBAL].add(perm.permission.permission_name)
484 user.permissions[GLOBAL].add(perm.permission.permission_name)
485
485
486 # defaults for repositories, taken from default user
486 # defaults for repositories, taken from default user
487 for perm in default_repo_perms:
487 for perm in default_repo_perms:
488 r_k = perm.UserRepoToPerm.repository.repo_name
488 r_k = perm.UserRepoToPerm.repository.repo_name
489 if perm.Repository.private and not (perm.Repository.user_id == uid):
489 if perm.Repository.private and not (perm.Repository.user_id == uid):
490 # disable defaults for private repos,
490 # disable defaults for private repos,
491 p = 'repository.none'
491 p = 'repository.none'
492 elif perm.Repository.user_id == uid:
492 elif perm.Repository.user_id == uid:
493 # set admin if owner
493 # set admin if owner
494 p = 'repository.admin'
494 p = 'repository.admin'
495 else:
495 else:
496 p = perm.Permission.permission_name
496 p = perm.Permission.permission_name
497
497
498 user.permissions[RK][r_k] = p
498 user.permissions[RK][r_k] = p
499
499
500 # defaults for repository groups taken from default user permission
500 # defaults for repository groups taken from default user permission
501 # on given group
501 # on given group
502 for perm in default_repo_groups_perms:
502 for perm in default_repo_groups_perms:
503 rg_k = perm.UserRepoGroupToPerm.group.group_name
503 rg_k = perm.UserRepoGroupToPerm.group.group_name
504 p = perm.Permission.permission_name
504 p = perm.Permission.permission_name
505 user.permissions[GK][rg_k] = p
505 user.permissions[GK][rg_k] = p
506
506
507 #======================================================================
507 #======================================================================
508 # !! OVERRIDE GLOBALS !! with user permissions if any found
508 # !! OVERRIDE GLOBALS !! with user permissions if any found
509 #======================================================================
509 #======================================================================
510 # those can be configured from groups or users explicitly
510 # those can be configured from groups or users explicitly
511 _configurable = set(['hg.fork.none', 'hg.fork.repository',
511 _configurable = set(['hg.fork.none', 'hg.fork.repository',
512 'hg.create.none', 'hg.create.repository'])
512 'hg.create.none', 'hg.create.repository'])
513
513
514 # USER GROUPS comes first
514 # USER GROUPS comes first
515 # users group global permissions
515 # user group global permissions
516 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
516 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
517 .options(joinedload(UsersGroupToPerm.permission))\
517 .options(joinedload(UsersGroupToPerm.permission))\
518 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
518 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
519 UsersGroupMember.users_group_id))\
519 UsersGroupMember.users_group_id))\
520 .filter(UsersGroupMember.user_id == uid)\
520 .filter(UsersGroupMember.user_id == uid)\
521 .order_by(UsersGroupToPerm.users_group_id)\
521 .order_by(UsersGroupToPerm.users_group_id)\
522 .all()
522 .all()
523 #need to group here by groups since user can be in more than one group
523 #need to group here by groups since user can be in more than one group
524 _grouped = [[x, list(y)] for x, y in
524 _grouped = [[x, list(y)] for x, y in
525 itertools.groupby(user_perms_from_users_groups,
525 itertools.groupby(user_perms_from_users_groups,
526 lambda x:x.users_group)]
526 lambda x:x.users_group)]
527 for gr, perms in _grouped:
527 for gr, perms in _grouped:
528 # since user can be in multiple groups iterate over them and
528 # since user can be in multiple groups iterate over them and
529 # select the lowest permissions first (more explicit)
529 # select the lowest permissions first (more explicit)
530 ##TODO: do this^^
530 ##TODO: do this^^
531 if not gr.inherit_default_permissions:
531 if not gr.inherit_default_permissions:
532 # NEED TO IGNORE all configurable permissions and
532 # NEED TO IGNORE all configurable permissions and
533 # replace them with explicitly set
533 # replace them with explicitly set
534 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
534 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
535 .difference(_configurable)
535 .difference(_configurable)
536 for perm in perms:
536 for perm in perms:
537 user.permissions[GLOBAL].add(perm.permission.permission_name)
537 user.permissions[GLOBAL].add(perm.permission.permission_name)
538
538
539 # user specific global permissions
539 # user specific global permissions
540 user_perms = self.sa.query(UserToPerm)\
540 user_perms = self.sa.query(UserToPerm)\
541 .options(joinedload(UserToPerm.permission))\
541 .options(joinedload(UserToPerm.permission))\
542 .filter(UserToPerm.user_id == uid).all()
542 .filter(UserToPerm.user_id == uid).all()
543
543
544 if not user.inherit_default_permissions:
544 if not user.inherit_default_permissions:
545 # NEED TO IGNORE all configurable permissions and
545 # NEED TO IGNORE all configurable permissions and
546 # replace them with explicitly set
546 # replace them with explicitly set
547 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
547 user.permissions[GLOBAL] = user.permissions[GLOBAL]\
548 .difference(_configurable)
548 .difference(_configurable)
549
549
550 for perm in user_perms:
550 for perm in user_perms:
551 user.permissions[GLOBAL].add(perm.permission.permission_name)
551 user.permissions[GLOBAL].add(perm.permission.permission_name)
552
552
553 #======================================================================
553 #======================================================================
554 # !! PERMISSIONS FOR REPOSITORIES !!
554 # !! PERMISSIONS FOR REPOSITORIES !!
555 #======================================================================
555 #======================================================================
556 #======================================================================
556 #======================================================================
557 # check if user is part of user groups for this repository and
557 # check if user is part of user groups for this repository and
558 # fill in his permission from it. _choose_perm decides of which
558 # fill in his permission from it. _choose_perm decides of which
559 # permission should be selected based on selected method
559 # permission should be selected based on selected method
560 #======================================================================
560 #======================================================================
561
561
562 # users group for repositories permissions
562 # user group for repositories permissions
563 user_repo_perms_from_users_groups = \
563 user_repo_perms_from_users_groups = \
564 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
564 self.sa.query(UsersGroupRepoToPerm, Permission, Repository,)\
565 .join((Repository, UsersGroupRepoToPerm.repository_id ==
565 .join((Repository, UsersGroupRepoToPerm.repository_id ==
566 Repository.repo_id))\
566 Repository.repo_id))\
567 .join((Permission, UsersGroupRepoToPerm.permission_id ==
567 .join((Permission, UsersGroupRepoToPerm.permission_id ==
568 Permission.permission_id))\
568 Permission.permission_id))\
569 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
569 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
570 UsersGroupMember.users_group_id))\
570 UsersGroupMember.users_group_id))\
571 .filter(UsersGroupMember.user_id == uid)\
571 .filter(UsersGroupMember.user_id == uid)\
572 .all()
572 .all()
573
573
574 multiple_counter = collections.defaultdict(int)
574 multiple_counter = collections.defaultdict(int)
575 for perm in user_repo_perms_from_users_groups:
575 for perm in user_repo_perms_from_users_groups:
576 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
576 r_k = perm.UsersGroupRepoToPerm.repository.repo_name
577 multiple_counter[r_k] += 1
577 multiple_counter[r_k] += 1
578 p = perm.Permission.permission_name
578 p = perm.Permission.permission_name
579 cur_perm = user.permissions[RK][r_k]
579 cur_perm = user.permissions[RK][r_k]
580
580
581 if perm.Repository.user_id == uid:
581 if perm.Repository.user_id == uid:
582 # set admin if owner
582 # set admin if owner
583 p = 'repository.admin'
583 p = 'repository.admin'
584 else:
584 else:
585 if multiple_counter[r_k] > 1:
585 if multiple_counter[r_k] > 1:
586 p = _choose_perm(p, cur_perm)
586 p = _choose_perm(p, cur_perm)
587 user.permissions[RK][r_k] = p
587 user.permissions[RK][r_k] = p
588
588
589 # user explicit permissions for repositories, overrides any specified
589 # user explicit permissions for repositories, overrides any specified
590 # by the group permission
590 # by the group permission
591 user_repo_perms = \
591 user_repo_perms = \
592 self.sa.query(UserRepoToPerm, Permission, Repository)\
592 self.sa.query(UserRepoToPerm, Permission, Repository)\
593 .join((Repository, UserRepoToPerm.repository_id ==
593 .join((Repository, UserRepoToPerm.repository_id ==
594 Repository.repo_id))\
594 Repository.repo_id))\
595 .join((Permission, UserRepoToPerm.permission_id ==
595 .join((Permission, UserRepoToPerm.permission_id ==
596 Permission.permission_id))\
596 Permission.permission_id))\
597 .filter(UserRepoToPerm.user_id == uid)\
597 .filter(UserRepoToPerm.user_id == uid)\
598 .all()
598 .all()
599
599
600 for perm in user_repo_perms:
600 for perm in user_repo_perms:
601 r_k = perm.UserRepoToPerm.repository.repo_name
601 r_k = perm.UserRepoToPerm.repository.repo_name
602 cur_perm = user.permissions[RK][r_k]
602 cur_perm = user.permissions[RK][r_k]
603 # set admin if owner
603 # set admin if owner
604 if perm.Repository.user_id == uid:
604 if perm.Repository.user_id == uid:
605 p = 'repository.admin'
605 p = 'repository.admin'
606 else:
606 else:
607 p = perm.Permission.permission_name
607 p = perm.Permission.permission_name
608 if not explicit:
608 if not explicit:
609 p = _choose_perm(p, cur_perm)
609 p = _choose_perm(p, cur_perm)
610 user.permissions[RK][r_k] = p
610 user.permissions[RK][r_k] = p
611
611
612 #======================================================================
612 #======================================================================
613 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
613 # !! PERMISSIONS FOR REPOSITORY GROUPS !!
614 #======================================================================
614 #======================================================================
615 #======================================================================
615 #======================================================================
616 # check if user is part of user groups for this repository groups and
616 # check if user is part of user groups for this repository groups and
617 # fill in his permission from it. _choose_perm decides of which
617 # fill in his permission from it. _choose_perm decides of which
618 # permission should be selected based on selected method
618 # permission should be selected based on selected method
619 #======================================================================
619 #======================================================================
620 # users group for repo groups permissions
620 # user group for repo groups permissions
621 user_repo_group_perms_from_users_groups = \
621 user_repo_group_perms_from_users_groups = \
622 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
622 self.sa.query(UsersGroupRepoGroupToPerm, Permission, RepoGroup)\
623 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
623 .join((RepoGroup, UsersGroupRepoGroupToPerm.group_id == RepoGroup.group_id))\
624 .join((Permission, UsersGroupRepoGroupToPerm.permission_id
624 .join((Permission, UsersGroupRepoGroupToPerm.permission_id
625 == Permission.permission_id))\
625 == Permission.permission_id))\
626 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id
626 .join((UsersGroupMember, UsersGroupRepoGroupToPerm.users_group_id
627 == UsersGroupMember.users_group_id))\
627 == UsersGroupMember.users_group_id))\
628 .filter(UsersGroupMember.user_id == uid)\
628 .filter(UsersGroupMember.user_id == uid)\
629 .all()
629 .all()
630
630
631 multiple_counter = collections.defaultdict(int)
631 multiple_counter = collections.defaultdict(int)
632 for perm in user_repo_group_perms_from_users_groups:
632 for perm in user_repo_group_perms_from_users_groups:
633 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
633 g_k = perm.UsersGroupRepoGroupToPerm.group.group_name
634 multiple_counter[g_k] += 1
634 multiple_counter[g_k] += 1
635 p = perm.Permission.permission_name
635 p = perm.Permission.permission_name
636 cur_perm = user.permissions[GK][g_k]
636 cur_perm = user.permissions[GK][g_k]
637 if multiple_counter[g_k] > 1:
637 if multiple_counter[g_k] > 1:
638 p = _choose_perm(p, cur_perm)
638 p = _choose_perm(p, cur_perm)
639 user.permissions[GK][g_k] = p
639 user.permissions[GK][g_k] = p
640
640
641 # user explicit permissions for repository groups
641 # user explicit permissions for repository groups
642 user_repo_groups_perms = \
642 user_repo_groups_perms = \
643 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
643 self.sa.query(UserRepoGroupToPerm, Permission, RepoGroup)\
644 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
644 .join((RepoGroup, UserRepoGroupToPerm.group_id == RepoGroup.group_id))\
645 .join((Permission, UserRepoGroupToPerm.permission_id
645 .join((Permission, UserRepoGroupToPerm.permission_id
646 == Permission.permission_id))\
646 == Permission.permission_id))\
647 .filter(UserRepoGroupToPerm.user_id == uid)\
647 .filter(UserRepoGroupToPerm.user_id == uid)\
648 .all()
648 .all()
649
649
650 for perm in user_repo_groups_perms:
650 for perm in user_repo_groups_perms:
651 rg_k = perm.UserRepoGroupToPerm.group.group_name
651 rg_k = perm.UserRepoGroupToPerm.group.group_name
652 p = perm.Permission.permission_name
652 p = perm.Permission.permission_name
653 cur_perm = user.permissions[GK][rg_k]
653 cur_perm = user.permissions[GK][rg_k]
654 if not explicit:
654 if not explicit:
655 p = _choose_perm(p, cur_perm)
655 p = _choose_perm(p, cur_perm)
656 user.permissions[GK][rg_k] = p
656 user.permissions[GK][rg_k] = p
657
657
658 return user
658 return user
659
659
660 def has_perm(self, user, perm):
660 def has_perm(self, user, perm):
661 perm = self._get_perm(perm)
661 perm = self._get_perm(perm)
662 user = self._get_user(user)
662 user = self._get_user(user)
663
663
664 return UserToPerm.query().filter(UserToPerm.user == user)\
664 return UserToPerm.query().filter(UserToPerm.user == user)\
665 .filter(UserToPerm.permission == perm).scalar() is not None
665 .filter(UserToPerm.permission == perm).scalar() is not None
666
666
667 def grant_perm(self, user, perm):
667 def grant_perm(self, user, perm):
668 """
668 """
669 Grant user global permissions
669 Grant user global permissions
670
670
671 :param user:
671 :param user:
672 :param perm:
672 :param perm:
673 """
673 """
674 user = self._get_user(user)
674 user = self._get_user(user)
675 perm = self._get_perm(perm)
675 perm = self._get_perm(perm)
676 # if this permission is already granted skip it
676 # if this permission is already granted skip it
677 _perm = UserToPerm.query()\
677 _perm = UserToPerm.query()\
678 .filter(UserToPerm.user == user)\
678 .filter(UserToPerm.user == user)\
679 .filter(UserToPerm.permission == perm)\
679 .filter(UserToPerm.permission == perm)\
680 .scalar()
680 .scalar()
681 if _perm:
681 if _perm:
682 return
682 return
683 new = UserToPerm()
683 new = UserToPerm()
684 new.user = user
684 new.user = user
685 new.permission = perm
685 new.permission = perm
686 self.sa.add(new)
686 self.sa.add(new)
687
687
688 def revoke_perm(self, user, perm):
688 def revoke_perm(self, user, perm):
689 """
689 """
690 Revoke users global permissions
690 Revoke users global permissions
691
691
692 :param user:
692 :param user:
693 :param perm:
693 :param perm:
694 """
694 """
695 user = self._get_user(user)
695 user = self._get_user(user)
696 perm = self._get_perm(perm)
696 perm = self._get_perm(perm)
697
697
698 obj = UserToPerm.query()\
698 obj = UserToPerm.query()\
699 .filter(UserToPerm.user == user)\
699 .filter(UserToPerm.user == user)\
700 .filter(UserToPerm.permission == perm)\
700 .filter(UserToPerm.permission == perm)\
701 .scalar()
701 .scalar()
702 if obj:
702 if obj:
703 self.sa.delete(obj)
703 self.sa.delete(obj)
704
704
705 def add_extra_email(self, user, email):
705 def add_extra_email(self, user, email):
706 """
706 """
707 Adds email address to UserEmailMap
707 Adds email address to UserEmailMap
708
708
709 :param user:
709 :param user:
710 :param email:
710 :param email:
711 """
711 """
712 from rhodecode.model import forms
712 from rhodecode.model import forms
713 form = forms.UserExtraEmailForm()()
713 form = forms.UserExtraEmailForm()()
714 data = form.to_python(dict(email=email))
714 data = form.to_python(dict(email=email))
715 user = self._get_user(user)
715 user = self._get_user(user)
716
716
717 obj = UserEmailMap()
717 obj = UserEmailMap()
718 obj.user = user
718 obj.user = user
719 obj.email = data['email']
719 obj.email = data['email']
720 self.sa.add(obj)
720 self.sa.add(obj)
721 return obj
721 return obj
722
722
723 def delete_extra_email(self, user, email_id):
723 def delete_extra_email(self, user, email_id):
724 """
724 """
725 Removes email address from UserEmailMap
725 Removes email address from UserEmailMap
726
726
727 :param user:
727 :param user:
728 :param email_id:
728 :param email_id:
729 """
729 """
730 user = self._get_user(user)
730 user = self._get_user(user)
731 obj = UserEmailMap.query().get(email_id)
731 obj = UserEmailMap.query().get(email_id)
732 if obj:
732 if obj:
733 self.sa.delete(obj)
733 self.sa.delete(obj)
734
734
735 def add_extra_ip(self, user, ip):
735 def add_extra_ip(self, user, ip):
736 """
736 """
737 Adds ip address to UserIpMap
737 Adds ip address to UserIpMap
738
738
739 :param user:
739 :param user:
740 :param ip:
740 :param ip:
741 """
741 """
742 from rhodecode.model import forms
742 from rhodecode.model import forms
743 form = forms.UserExtraIpForm()()
743 form = forms.UserExtraIpForm()()
744 data = form.to_python(dict(ip=ip))
744 data = form.to_python(dict(ip=ip))
745 user = self._get_user(user)
745 user = self._get_user(user)
746
746
747 obj = UserIpMap()
747 obj = UserIpMap()
748 obj.user = user
748 obj.user = user
749 obj.ip_addr = data['ip']
749 obj.ip_addr = data['ip']
750 self.sa.add(obj)
750 self.sa.add(obj)
751 return obj
751 return obj
752
752
753 def delete_extra_ip(self, user, ip_id):
753 def delete_extra_ip(self, user, ip_id):
754 """
754 """
755 Removes ip address from UserIpMap
755 Removes ip address from UserIpMap
756
756
757 :param user:
757 :param user:
758 :param ip_id:
758 :param ip_id:
759 """
759 """
760 user = self._get_user(user)
760 user = self._get_user(user)
761 obj = UserIpMap.query().get(ip_id)
761 obj = UserIpMap.query().get(ip_id)
762 if obj:
762 if obj:
763 self.sa.delete(obj)
763 self.sa.delete(obj)
@@ -1,809 +1,809 b''
1 """
1 """
2 Set of generic validators
2 Set of generic validators
3 """
3 """
4 import os
4 import os
5 import re
5 import re
6 import formencode
6 import formencode
7 import logging
7 import logging
8 from collections import defaultdict
8 from collections import defaultdict
9 from pylons.i18n.translation import _
9 from pylons.i18n.translation import _
10 from webhelpers.pylonslib.secure_form import authentication_token
10 from webhelpers.pylonslib.secure_form import authentication_token
11
11
12 from formencode.validators import (
12 from formencode.validators import (
13 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
13 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set,
14 NotEmpty, IPAddress, CIDR
14 NotEmpty, IPAddress, CIDR
15 )
15 )
16 from rhodecode.lib.compat import OrderedSet
16 from rhodecode.lib.compat import OrderedSet
17 from rhodecode.lib import ipaddr
17 from rhodecode.lib import ipaddr
18 from rhodecode.lib.utils import repo_name_slug
18 from rhodecode.lib.utils import repo_name_slug
19 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
19 from rhodecode.model.db import RepoGroup, Repository, UsersGroup, User,\
20 ChangesetStatus
20 ChangesetStatus
21 from rhodecode.lib.exceptions import LdapImportError
21 from rhodecode.lib.exceptions import LdapImportError
22 from rhodecode.config.routing import ADMIN_PREFIX
22 from rhodecode.config.routing import ADMIN_PREFIX
23 from rhodecode.lib.auth import HasReposGroupPermissionAny, HasPermissionAny
23 from rhodecode.lib.auth import HasReposGroupPermissionAny, HasPermissionAny
24
24
25 # silence warnings and pylint
25 # silence warnings and pylint
26 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
26 UnicodeString, OneOf, Int, Number, Regex, Email, Bool, StringBoolean, Set, \
27 NotEmpty, IPAddress, CIDR
27 NotEmpty, IPAddress, CIDR
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31
31
32 class UniqueList(formencode.FancyValidator):
32 class UniqueList(formencode.FancyValidator):
33 """
33 """
34 Unique List !
34 Unique List !
35 """
35 """
36 messages = dict(
36 messages = dict(
37 empty=_('Value cannot be an empty list'),
37 empty=_('Value cannot be an empty list'),
38 missing_value=_('Value cannot be an empty list'),
38 missing_value=_('Value cannot be an empty list'),
39 )
39 )
40
40
41 def _to_python(self, value, state):
41 def _to_python(self, value, state):
42 if isinstance(value, list):
42 if isinstance(value, list):
43 return value
43 return value
44 elif isinstance(value, set):
44 elif isinstance(value, set):
45 return list(value)
45 return list(value)
46 elif isinstance(value, tuple):
46 elif isinstance(value, tuple):
47 return list(value)
47 return list(value)
48 elif value is None:
48 elif value is None:
49 return []
49 return []
50 else:
50 else:
51 return [value]
51 return [value]
52
52
53 def empty_value(self, value):
53 def empty_value(self, value):
54 return []
54 return []
55
55
56
56
57 class StateObj(object):
57 class StateObj(object):
58 """
58 """
59 this is needed to translate the messages using _() in validators
59 this is needed to translate the messages using _() in validators
60 """
60 """
61 _ = staticmethod(_)
61 _ = staticmethod(_)
62
62
63
63
64 def M(self, key, state=None, **kwargs):
64 def M(self, key, state=None, **kwargs):
65 """
65 """
66 returns string from self.message based on given key,
66 returns string from self.message based on given key,
67 passed kw params are used to substitute %(named)s params inside
67 passed kw params are used to substitute %(named)s params inside
68 translated strings
68 translated strings
69
69
70 :param msg:
70 :param msg:
71 :param state:
71 :param state:
72 """
72 """
73 if state is None:
73 if state is None:
74 state = StateObj()
74 state = StateObj()
75 else:
75 else:
76 state._ = staticmethod(_)
76 state._ = staticmethod(_)
77 #inject validator into state object
77 #inject validator into state object
78 return self.message(key, state, **kwargs)
78 return self.message(key, state, **kwargs)
79
79
80
80
81 def ValidUsername(edit=False, old_data={}):
81 def ValidUsername(edit=False, old_data={}):
82 class _validator(formencode.validators.FancyValidator):
82 class _validator(formencode.validators.FancyValidator):
83 messages = {
83 messages = {
84 'username_exists': _(u'Username "%(username)s" already exists'),
84 'username_exists': _(u'Username "%(username)s" already exists'),
85 'system_invalid_username':
85 'system_invalid_username':
86 _(u'Username "%(username)s" is forbidden'),
86 _(u'Username "%(username)s" is forbidden'),
87 'invalid_username':
87 'invalid_username':
88 _(u'Username may only contain alphanumeric characters '
88 _(u'Username may only contain alphanumeric characters '
89 'underscores, periods or dashes and must begin with '
89 'underscores, periods or dashes and must begin with '
90 'alphanumeric character')
90 'alphanumeric character')
91 }
91 }
92
92
93 def validate_python(self, value, state):
93 def validate_python(self, value, state):
94 if value in ['default', 'new_user']:
94 if value in ['default', 'new_user']:
95 msg = M(self, 'system_invalid_username', state, username=value)
95 msg = M(self, 'system_invalid_username', state, username=value)
96 raise formencode.Invalid(msg, value, state)
96 raise formencode.Invalid(msg, value, state)
97 #check if user is unique
97 #check if user is unique
98 old_un = None
98 old_un = None
99 if edit:
99 if edit:
100 old_un = User.get(old_data.get('user_id')).username
100 old_un = User.get(old_data.get('user_id')).username
101
101
102 if old_un != value or not edit:
102 if old_un != value or not edit:
103 if User.get_by_username(value, case_insensitive=True):
103 if User.get_by_username(value, case_insensitive=True):
104 msg = M(self, 'username_exists', state, username=value)
104 msg = M(self, 'username_exists', state, username=value)
105 raise formencode.Invalid(msg, value, state)
105 raise formencode.Invalid(msg, value, state)
106
106
107 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*$', value) is None:
107 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]*$', value) is None:
108 msg = M(self, 'invalid_username', state)
108 msg = M(self, 'invalid_username', state)
109 raise formencode.Invalid(msg, value, state)
109 raise formencode.Invalid(msg, value, state)
110 return _validator
110 return _validator
111
111
112
112
113 def ValidRepoUser():
113 def ValidRepoUser():
114 class _validator(formencode.validators.FancyValidator):
114 class _validator(formencode.validators.FancyValidator):
115 messages = {
115 messages = {
116 'invalid_username': _(u'Username %(username)s is not valid')
116 'invalid_username': _(u'Username %(username)s is not valid')
117 }
117 }
118
118
119 def validate_python(self, value, state):
119 def validate_python(self, value, state):
120 try:
120 try:
121 User.query().filter(User.active == True)\
121 User.query().filter(User.active == True)\
122 .filter(User.username == value).one()
122 .filter(User.username == value).one()
123 except Exception:
123 except Exception:
124 msg = M(self, 'invalid_username', state, username=value)
124 msg = M(self, 'invalid_username', state, username=value)
125 raise formencode.Invalid(msg, value, state,
125 raise formencode.Invalid(msg, value, state,
126 error_dict=dict(username=msg)
126 error_dict=dict(username=msg)
127 )
127 )
128
128
129 return _validator
129 return _validator
130
130
131
131
132 def ValidUsersGroup(edit=False, old_data={}):
132 def ValidUsersGroup(edit=False, old_data={}):
133 class _validator(formencode.validators.FancyValidator):
133 class _validator(formencode.validators.FancyValidator):
134 messages = {
134 messages = {
135 'invalid_group': _(u'Invalid users group name'),
135 'invalid_group': _(u'Invalid user group name'),
136 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
136 'group_exist': _(u'Users group "%(usersgroup)s" already exists'),
137 'invalid_usersgroup_name':
137 'invalid_usersgroup_name':
138 _(u'users group name may only contain alphanumeric '
138 _(u'user group name may only contain alphanumeric '
139 'characters underscores, periods or dashes and must begin '
139 'characters underscores, periods or dashes and must begin '
140 'with alphanumeric character')
140 'with alphanumeric character')
141 }
141 }
142
142
143 def validate_python(self, value, state):
143 def validate_python(self, value, state):
144 if value in ['default']:
144 if value in ['default']:
145 msg = M(self, 'invalid_group', state)
145 msg = M(self, 'invalid_group', state)
146 raise formencode.Invalid(msg, value, state,
146 raise formencode.Invalid(msg, value, state,
147 error_dict=dict(users_group_name=msg)
147 error_dict=dict(users_group_name=msg)
148 )
148 )
149 #check if group is unique
149 #check if group is unique
150 old_ugname = None
150 old_ugname = None
151 if edit:
151 if edit:
152 old_id = old_data.get('users_group_id')
152 old_id = old_data.get('users_group_id')
153 old_ugname = UsersGroup.get(old_id).users_group_name
153 old_ugname = UsersGroup.get(old_id).users_group_name
154
154
155 if old_ugname != value or not edit:
155 if old_ugname != value or not edit:
156 is_existing_group = UsersGroup.get_by_group_name(value,
156 is_existing_group = UsersGroup.get_by_group_name(value,
157 case_insensitive=True)
157 case_insensitive=True)
158 if is_existing_group:
158 if is_existing_group:
159 msg = M(self, 'group_exist', state, usersgroup=value)
159 msg = M(self, 'group_exist', state, usersgroup=value)
160 raise formencode.Invalid(msg, value, state,
160 raise formencode.Invalid(msg, value, state,
161 error_dict=dict(users_group_name=msg)
161 error_dict=dict(users_group_name=msg)
162 )
162 )
163
163
164 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
164 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
165 msg = M(self, 'invalid_usersgroup_name', state)
165 msg = M(self, 'invalid_usersgroup_name', state)
166 raise formencode.Invalid(msg, value, state,
166 raise formencode.Invalid(msg, value, state,
167 error_dict=dict(users_group_name=msg)
167 error_dict=dict(users_group_name=msg)
168 )
168 )
169
169
170 return _validator
170 return _validator
171
171
172
172
173 def ValidReposGroup(edit=False, old_data={}):
173 def ValidReposGroup(edit=False, old_data={}):
174 class _validator(formencode.validators.FancyValidator):
174 class _validator(formencode.validators.FancyValidator):
175 messages = {
175 messages = {
176 'group_parent_id': _(u'Cannot assign this group as parent'),
176 'group_parent_id': _(u'Cannot assign this group as parent'),
177 'group_exists': _(u'Group "%(group_name)s" already exists'),
177 'group_exists': _(u'Group "%(group_name)s" already exists'),
178 'repo_exists':
178 'repo_exists':
179 _(u'Repository with name "%(group_name)s" already exists')
179 _(u'Repository with name "%(group_name)s" already exists')
180 }
180 }
181
181
182 def validate_python(self, value, state):
182 def validate_python(self, value, state):
183 # TODO WRITE VALIDATIONS
183 # TODO WRITE VALIDATIONS
184 group_name = value.get('group_name')
184 group_name = value.get('group_name')
185 group_parent_id = value.get('group_parent_id')
185 group_parent_id = value.get('group_parent_id')
186
186
187 # slugify repo group just in case :)
187 # slugify repo group just in case :)
188 slug = repo_name_slug(group_name)
188 slug = repo_name_slug(group_name)
189
189
190 # check for parent of self
190 # check for parent of self
191 parent_of_self = lambda: (
191 parent_of_self = lambda: (
192 old_data['group_id'] == int(group_parent_id)
192 old_data['group_id'] == int(group_parent_id)
193 if group_parent_id else False
193 if group_parent_id else False
194 )
194 )
195 if edit and parent_of_self():
195 if edit and parent_of_self():
196 msg = M(self, 'group_parent_id', state)
196 msg = M(self, 'group_parent_id', state)
197 raise formencode.Invalid(msg, value, state,
197 raise formencode.Invalid(msg, value, state,
198 error_dict=dict(group_parent_id=msg)
198 error_dict=dict(group_parent_id=msg)
199 )
199 )
200
200
201 old_gname = None
201 old_gname = None
202 if edit:
202 if edit:
203 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
203 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
204
204
205 if old_gname != group_name or not edit:
205 if old_gname != group_name or not edit:
206
206
207 # check group
207 # check group
208 gr = RepoGroup.query()\
208 gr = RepoGroup.query()\
209 .filter(RepoGroup.group_name == slug)\
209 .filter(RepoGroup.group_name == slug)\
210 .filter(RepoGroup.group_parent_id == group_parent_id)\
210 .filter(RepoGroup.group_parent_id == group_parent_id)\
211 .scalar()
211 .scalar()
212
212
213 if gr:
213 if gr:
214 msg = M(self, 'group_exists', state, group_name=slug)
214 msg = M(self, 'group_exists', state, group_name=slug)
215 raise formencode.Invalid(msg, value, state,
215 raise formencode.Invalid(msg, value, state,
216 error_dict=dict(group_name=msg)
216 error_dict=dict(group_name=msg)
217 )
217 )
218
218
219 # check for same repo
219 # check for same repo
220 repo = Repository.query()\
220 repo = Repository.query()\
221 .filter(Repository.repo_name == slug)\
221 .filter(Repository.repo_name == slug)\
222 .scalar()
222 .scalar()
223
223
224 if repo:
224 if repo:
225 msg = M(self, 'repo_exists', state, group_name=slug)
225 msg = M(self, 'repo_exists', state, group_name=slug)
226 raise formencode.Invalid(msg, value, state,
226 raise formencode.Invalid(msg, value, state,
227 error_dict=dict(group_name=msg)
227 error_dict=dict(group_name=msg)
228 )
228 )
229
229
230 return _validator
230 return _validator
231
231
232
232
233 def ValidPassword():
233 def ValidPassword():
234 class _validator(formencode.validators.FancyValidator):
234 class _validator(formencode.validators.FancyValidator):
235 messages = {
235 messages = {
236 'invalid_password':
236 'invalid_password':
237 _(u'Invalid characters (non-ascii) in password')
237 _(u'Invalid characters (non-ascii) in password')
238 }
238 }
239
239
240 def validate_python(self, value, state):
240 def validate_python(self, value, state):
241 try:
241 try:
242 (value or '').decode('ascii')
242 (value or '').decode('ascii')
243 except UnicodeError:
243 except UnicodeError:
244 msg = M(self, 'invalid_password', state)
244 msg = M(self, 'invalid_password', state)
245 raise formencode.Invalid(msg, value, state,)
245 raise formencode.Invalid(msg, value, state,)
246 return _validator
246 return _validator
247
247
248
248
249 def ValidPasswordsMatch():
249 def ValidPasswordsMatch():
250 class _validator(formencode.validators.FancyValidator):
250 class _validator(formencode.validators.FancyValidator):
251 messages = {
251 messages = {
252 'password_mismatch': _(u'Passwords do not match'),
252 'password_mismatch': _(u'Passwords do not match'),
253 }
253 }
254
254
255 def validate_python(self, value, state):
255 def validate_python(self, value, state):
256
256
257 pass_val = value.get('password') or value.get('new_password')
257 pass_val = value.get('password') or value.get('new_password')
258 if pass_val != value['password_confirmation']:
258 if pass_val != value['password_confirmation']:
259 msg = M(self, 'password_mismatch', state)
259 msg = M(self, 'password_mismatch', state)
260 raise formencode.Invalid(msg, value, state,
260 raise formencode.Invalid(msg, value, state,
261 error_dict=dict(password_confirmation=msg)
261 error_dict=dict(password_confirmation=msg)
262 )
262 )
263 return _validator
263 return _validator
264
264
265
265
266 def ValidAuth():
266 def ValidAuth():
267 class _validator(formencode.validators.FancyValidator):
267 class _validator(formencode.validators.FancyValidator):
268 messages = {
268 messages = {
269 'invalid_password': _(u'invalid password'),
269 'invalid_password': _(u'invalid password'),
270 'invalid_username': _(u'invalid user name'),
270 'invalid_username': _(u'invalid user name'),
271 'disabled_account': _(u'Your account is disabled')
271 'disabled_account': _(u'Your account is disabled')
272 }
272 }
273
273
274 def validate_python(self, value, state):
274 def validate_python(self, value, state):
275 from rhodecode.lib.auth import authenticate
275 from rhodecode.lib.auth import authenticate
276
276
277 password = value['password']
277 password = value['password']
278 username = value['username']
278 username = value['username']
279
279
280 if not authenticate(username, password):
280 if not authenticate(username, password):
281 user = User.get_by_username(username)
281 user = User.get_by_username(username)
282 if user and user.active is False:
282 if user and user.active is False:
283 log.warning('user %s is disabled' % username)
283 log.warning('user %s is disabled' % username)
284 msg = M(self, 'disabled_account', state)
284 msg = M(self, 'disabled_account', state)
285 raise formencode.Invalid(msg, value, state,
285 raise formencode.Invalid(msg, value, state,
286 error_dict=dict(username=msg)
286 error_dict=dict(username=msg)
287 )
287 )
288 else:
288 else:
289 log.warning('user %s failed to authenticate' % username)
289 log.warning('user %s failed to authenticate' % username)
290 msg = M(self, 'invalid_username', state)
290 msg = M(self, 'invalid_username', state)
291 msg2 = M(self, 'invalid_password', state)
291 msg2 = M(self, 'invalid_password', state)
292 raise formencode.Invalid(msg, value, state,
292 raise formencode.Invalid(msg, value, state,
293 error_dict=dict(username=msg, password=msg2)
293 error_dict=dict(username=msg, password=msg2)
294 )
294 )
295 return _validator
295 return _validator
296
296
297
297
298 def ValidAuthToken():
298 def ValidAuthToken():
299 class _validator(formencode.validators.FancyValidator):
299 class _validator(formencode.validators.FancyValidator):
300 messages = {
300 messages = {
301 'invalid_token': _(u'Token mismatch')
301 'invalid_token': _(u'Token mismatch')
302 }
302 }
303
303
304 def validate_python(self, value, state):
304 def validate_python(self, value, state):
305 if value != authentication_token():
305 if value != authentication_token():
306 msg = M(self, 'invalid_token', state)
306 msg = M(self, 'invalid_token', state)
307 raise formencode.Invalid(msg, value, state)
307 raise formencode.Invalid(msg, value, state)
308 return _validator
308 return _validator
309
309
310
310
311 def ValidRepoName(edit=False, old_data={}):
311 def ValidRepoName(edit=False, old_data={}):
312 class _validator(formencode.validators.FancyValidator):
312 class _validator(formencode.validators.FancyValidator):
313 messages = {
313 messages = {
314 'invalid_repo_name':
314 'invalid_repo_name':
315 _(u'Repository name %(repo)s is disallowed'),
315 _(u'Repository name %(repo)s is disallowed'),
316 'repository_exists':
316 'repository_exists':
317 _(u'Repository named %(repo)s already exists'),
317 _(u'Repository named %(repo)s already exists'),
318 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
318 'repository_in_group_exists': _(u'Repository "%(repo)s" already '
319 'exists in group "%(group)s"'),
319 'exists in group "%(group)s"'),
320 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
320 'same_group_exists': _(u'Repositories group with name "%(repo)s" '
321 'already exists')
321 'already exists')
322 }
322 }
323
323
324 def _to_python(self, value, state):
324 def _to_python(self, value, state):
325 repo_name = repo_name_slug(value.get('repo_name', ''))
325 repo_name = repo_name_slug(value.get('repo_name', ''))
326 repo_group = value.get('repo_group')
326 repo_group = value.get('repo_group')
327 if repo_group:
327 if repo_group:
328 gr = RepoGroup.get(repo_group)
328 gr = RepoGroup.get(repo_group)
329 group_path = gr.full_path
329 group_path = gr.full_path
330 group_name = gr.group_name
330 group_name = gr.group_name
331 # value needs to be aware of group name in order to check
331 # value needs to be aware of group name in order to check
332 # db key This is an actual just the name to store in the
332 # db key This is an actual just the name to store in the
333 # database
333 # database
334 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
334 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
335 else:
335 else:
336 group_name = group_path = ''
336 group_name = group_path = ''
337 repo_name_full = repo_name
337 repo_name_full = repo_name
338
338
339 value['repo_name'] = repo_name
339 value['repo_name'] = repo_name
340 value['repo_name_full'] = repo_name_full
340 value['repo_name_full'] = repo_name_full
341 value['group_path'] = group_path
341 value['group_path'] = group_path
342 value['group_name'] = group_name
342 value['group_name'] = group_name
343 return value
343 return value
344
344
345 def validate_python(self, value, state):
345 def validate_python(self, value, state):
346
346
347 repo_name = value.get('repo_name')
347 repo_name = value.get('repo_name')
348 repo_name_full = value.get('repo_name_full')
348 repo_name_full = value.get('repo_name_full')
349 group_path = value.get('group_path')
349 group_path = value.get('group_path')
350 group_name = value.get('group_name')
350 group_name = value.get('group_name')
351
351
352 if repo_name in [ADMIN_PREFIX, '']:
352 if repo_name in [ADMIN_PREFIX, '']:
353 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
353 msg = M(self, 'invalid_repo_name', state, repo=repo_name)
354 raise formencode.Invalid(msg, value, state,
354 raise formencode.Invalid(msg, value, state,
355 error_dict=dict(repo_name=msg)
355 error_dict=dict(repo_name=msg)
356 )
356 )
357
357
358 rename = old_data.get('repo_name') != repo_name_full
358 rename = old_data.get('repo_name') != repo_name_full
359 create = not edit
359 create = not edit
360 if rename or create:
360 if rename or create:
361
361
362 if group_path != '':
362 if group_path != '':
363 if Repository.get_by_repo_name(repo_name_full):
363 if Repository.get_by_repo_name(repo_name_full):
364 msg = M(self, 'repository_in_group_exists', state,
364 msg = M(self, 'repository_in_group_exists', state,
365 repo=repo_name, group=group_name)
365 repo=repo_name, group=group_name)
366 raise formencode.Invalid(msg, value, state,
366 raise formencode.Invalid(msg, value, state,
367 error_dict=dict(repo_name=msg)
367 error_dict=dict(repo_name=msg)
368 )
368 )
369 elif RepoGroup.get_by_group_name(repo_name_full):
369 elif RepoGroup.get_by_group_name(repo_name_full):
370 msg = M(self, 'same_group_exists', state,
370 msg = M(self, 'same_group_exists', state,
371 repo=repo_name)
371 repo=repo_name)
372 raise formencode.Invalid(msg, value, state,
372 raise formencode.Invalid(msg, value, state,
373 error_dict=dict(repo_name=msg)
373 error_dict=dict(repo_name=msg)
374 )
374 )
375
375
376 elif Repository.get_by_repo_name(repo_name_full):
376 elif Repository.get_by_repo_name(repo_name_full):
377 msg = M(self, 'repository_exists', state,
377 msg = M(self, 'repository_exists', state,
378 repo=repo_name)
378 repo=repo_name)
379 raise formencode.Invalid(msg, value, state,
379 raise formencode.Invalid(msg, value, state,
380 error_dict=dict(repo_name=msg)
380 error_dict=dict(repo_name=msg)
381 )
381 )
382 return value
382 return value
383 return _validator
383 return _validator
384
384
385
385
386 def ValidForkName(*args, **kwargs):
386 def ValidForkName(*args, **kwargs):
387 return ValidRepoName(*args, **kwargs)
387 return ValidRepoName(*args, **kwargs)
388
388
389
389
390 def SlugifyName():
390 def SlugifyName():
391 class _validator(formencode.validators.FancyValidator):
391 class _validator(formencode.validators.FancyValidator):
392
392
393 def _to_python(self, value, state):
393 def _to_python(self, value, state):
394 return repo_name_slug(value)
394 return repo_name_slug(value)
395
395
396 def validate_python(self, value, state):
396 def validate_python(self, value, state):
397 pass
397 pass
398
398
399 return _validator
399 return _validator
400
400
401
401
402 def ValidCloneUri():
402 def ValidCloneUri():
403 from rhodecode.lib.utils import make_ui
403 from rhodecode.lib.utils import make_ui
404
404
405 def url_handler(repo_type, url, ui=None):
405 def url_handler(repo_type, url, ui=None):
406 if repo_type == 'hg':
406 if repo_type == 'hg':
407 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
407 from rhodecode.lib.vcs.backends.hg.repository import MercurialRepository
408 from mercurial.httppeer import httppeer
408 from mercurial.httppeer import httppeer
409 if url.startswith('http'):
409 if url.startswith('http'):
410 ## initially check if it's at least the proper URL
410 ## initially check if it's at least the proper URL
411 ## or does it pass basic auth
411 ## or does it pass basic auth
412 MercurialRepository._check_url(url)
412 MercurialRepository._check_url(url)
413 httppeer(ui, url)._capabilities()
413 httppeer(ui, url)._capabilities()
414 elif url.startswith('svn+http'):
414 elif url.startswith('svn+http'):
415 from hgsubversion.svnrepo import svnremoterepo
415 from hgsubversion.svnrepo import svnremoterepo
416 svnremoterepo(ui, url).capabilities
416 svnremoterepo(ui, url).capabilities
417 elif url.startswith('git+http'):
417 elif url.startswith('git+http'):
418 raise NotImplementedError()
418 raise NotImplementedError()
419
419
420 elif repo_type == 'git':
420 elif repo_type == 'git':
421 from rhodecode.lib.vcs.backends.git.repository import GitRepository
421 from rhodecode.lib.vcs.backends.git.repository import GitRepository
422 if url.startswith('http'):
422 if url.startswith('http'):
423 ## initially check if it's at least the proper URL
423 ## initially check if it's at least the proper URL
424 ## or does it pass basic auth
424 ## or does it pass basic auth
425 GitRepository._check_url(url)
425 GitRepository._check_url(url)
426 elif url.startswith('svn+http'):
426 elif url.startswith('svn+http'):
427 raise NotImplementedError()
427 raise NotImplementedError()
428 elif url.startswith('hg+http'):
428 elif url.startswith('hg+http'):
429 raise NotImplementedError()
429 raise NotImplementedError()
430
430
431 class _validator(formencode.validators.FancyValidator):
431 class _validator(formencode.validators.FancyValidator):
432 messages = {
432 messages = {
433 'clone_uri': _(u'invalid clone url'),
433 'clone_uri': _(u'invalid clone url'),
434 'invalid_clone_uri': _(u'Invalid clone url, provide a '
434 'invalid_clone_uri': _(u'Invalid clone url, provide a '
435 'valid clone http(s)/svn+http(s) url')
435 'valid clone http(s)/svn+http(s) url')
436 }
436 }
437
437
438 def validate_python(self, value, state):
438 def validate_python(self, value, state):
439 repo_type = value.get('repo_type')
439 repo_type = value.get('repo_type')
440 url = value.get('clone_uri')
440 url = value.get('clone_uri')
441
441
442 if not url:
442 if not url:
443 pass
443 pass
444 else:
444 else:
445 try:
445 try:
446 url_handler(repo_type, url, make_ui('db', clear_session=False))
446 url_handler(repo_type, url, make_ui('db', clear_session=False))
447 except Exception:
447 except Exception:
448 log.exception('Url validation failed')
448 log.exception('Url validation failed')
449 msg = M(self, 'clone_uri')
449 msg = M(self, 'clone_uri')
450 raise formencode.Invalid(msg, value, state,
450 raise formencode.Invalid(msg, value, state,
451 error_dict=dict(clone_uri=msg)
451 error_dict=dict(clone_uri=msg)
452 )
452 )
453 return _validator
453 return _validator
454
454
455
455
456 def ValidForkType(old_data={}):
456 def ValidForkType(old_data={}):
457 class _validator(formencode.validators.FancyValidator):
457 class _validator(formencode.validators.FancyValidator):
458 messages = {
458 messages = {
459 'invalid_fork_type': _(u'Fork have to be the same type as parent')
459 'invalid_fork_type': _(u'Fork have to be the same type as parent')
460 }
460 }
461
461
462 def validate_python(self, value, state):
462 def validate_python(self, value, state):
463 if old_data['repo_type'] != value:
463 if old_data['repo_type'] != value:
464 msg = M(self, 'invalid_fork_type', state)
464 msg = M(self, 'invalid_fork_type', state)
465 raise formencode.Invalid(msg, value, state,
465 raise formencode.Invalid(msg, value, state,
466 error_dict=dict(repo_type=msg)
466 error_dict=dict(repo_type=msg)
467 )
467 )
468 return _validator
468 return _validator
469
469
470
470
471 def CanWriteGroup():
471 def CanWriteGroup():
472 class _validator(formencode.validators.FancyValidator):
472 class _validator(formencode.validators.FancyValidator):
473 messages = {
473 messages = {
474 'permission_denied': _(u"You don't have permissions "
474 'permission_denied': _(u"You don't have permissions "
475 "to create repository in this group"),
475 "to create repository in this group"),
476 'permission_denied_root': _(u"no permission to create repository "
476 'permission_denied_root': _(u"no permission to create repository "
477 "in root location")
477 "in root location")
478 }
478 }
479
479
480 def _to_python(self, value, state):
480 def _to_python(self, value, state):
481 #root location
481 #root location
482 if value in [-1, "-1"]:
482 if value in [-1, "-1"]:
483 return None
483 return None
484 return value
484 return value
485
485
486 def validate_python(self, value, state):
486 def validate_python(self, value, state):
487 gr = RepoGroup.get(value)
487 gr = RepoGroup.get(value)
488 gr_name = gr.group_name if gr else None # None means ROOT location
488 gr_name = gr.group_name if gr else None # None means ROOT location
489 val = HasReposGroupPermissionAny('group.write', 'group.admin')
489 val = HasReposGroupPermissionAny('group.write', 'group.admin')
490 can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository')
490 can_create_repos = HasPermissionAny('hg.admin', 'hg.create.repository')
491 forbidden = not val(gr_name, 'can write into group validator')
491 forbidden = not val(gr_name, 'can write into group validator')
492 #parent group need to be existing
492 #parent group need to be existing
493 if gr and forbidden:
493 if gr and forbidden:
494 msg = M(self, 'permission_denied', state)
494 msg = M(self, 'permission_denied', state)
495 raise formencode.Invalid(msg, value, state,
495 raise formencode.Invalid(msg, value, state,
496 error_dict=dict(repo_type=msg)
496 error_dict=dict(repo_type=msg)
497 )
497 )
498 ## check if we can write to root location !
498 ## check if we can write to root location !
499 elif gr is None and can_create_repos() is False:
499 elif gr is None and can_create_repos() is False:
500 msg = M(self, 'permission_denied_root', state)
500 msg = M(self, 'permission_denied_root', state)
501 raise formencode.Invalid(msg, value, state,
501 raise formencode.Invalid(msg, value, state,
502 error_dict=dict(repo_type=msg)
502 error_dict=dict(repo_type=msg)
503 )
503 )
504
504
505 return _validator
505 return _validator
506
506
507
507
508 def CanCreateGroup(can_create_in_root=False):
508 def CanCreateGroup(can_create_in_root=False):
509 class _validator(formencode.validators.FancyValidator):
509 class _validator(formencode.validators.FancyValidator):
510 messages = {
510 messages = {
511 'permission_denied': _(u"You don't have permissions "
511 'permission_denied': _(u"You don't have permissions "
512 "to create a group in this location")
512 "to create a group in this location")
513 }
513 }
514
514
515 def to_python(self, value, state):
515 def to_python(self, value, state):
516 #root location
516 #root location
517 if value in [-1, "-1"]:
517 if value in [-1, "-1"]:
518 return None
518 return None
519 return value
519 return value
520
520
521 def validate_python(self, value, state):
521 def validate_python(self, value, state):
522 gr = RepoGroup.get(value)
522 gr = RepoGroup.get(value)
523 gr_name = gr.group_name if gr else None # None means ROOT location
523 gr_name = gr.group_name if gr else None # None means ROOT location
524
524
525 if can_create_in_root and gr is None:
525 if can_create_in_root and gr is None:
526 #we can create in root, we're fine no validations required
526 #we can create in root, we're fine no validations required
527 return
527 return
528
528
529 forbidden_in_root = gr is None and can_create_in_root is False
529 forbidden_in_root = gr is None and can_create_in_root is False
530 val = HasReposGroupPermissionAny('group.admin')
530 val = HasReposGroupPermissionAny('group.admin')
531 forbidden = not val(gr_name, 'can create group validator')
531 forbidden = not val(gr_name, 'can create group validator')
532 if forbidden_in_root or forbidden:
532 if forbidden_in_root or forbidden:
533 msg = M(self, 'permission_denied', state)
533 msg = M(self, 'permission_denied', state)
534 raise formencode.Invalid(msg, value, state,
534 raise formencode.Invalid(msg, value, state,
535 error_dict=dict(group_parent_id=msg)
535 error_dict=dict(group_parent_id=msg)
536 )
536 )
537
537
538 return _validator
538 return _validator
539
539
540
540
541 def ValidPerms(type_='repo'):
541 def ValidPerms(type_='repo'):
542 if type_ == 'group':
542 if type_ == 'group':
543 EMPTY_PERM = 'group.none'
543 EMPTY_PERM = 'group.none'
544 elif type_ == 'repo':
544 elif type_ == 'repo':
545 EMPTY_PERM = 'repository.none'
545 EMPTY_PERM = 'repository.none'
546
546
547 class _validator(formencode.validators.FancyValidator):
547 class _validator(formencode.validators.FancyValidator):
548 messages = {
548 messages = {
549 'perm_new_member_name':
549 'perm_new_member_name':
550 _(u'This username or users group name is not valid')
550 _(u'This username or user group name is not valid')
551 }
551 }
552
552
553 def to_python(self, value, state):
553 def to_python(self, value, state):
554 perms_update = OrderedSet()
554 perms_update = OrderedSet()
555 perms_new = OrderedSet()
555 perms_new = OrderedSet()
556 # build a list of permission to update and new permission to create
556 # build a list of permission to update and new permission to create
557
557
558 #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
558 #CLEAN OUT ORG VALUE FROM NEW MEMBERS, and group them using
559 new_perms_group = defaultdict(dict)
559 new_perms_group = defaultdict(dict)
560 for k, v in value.copy().iteritems():
560 for k, v in value.copy().iteritems():
561 if k.startswith('perm_new_member'):
561 if k.startswith('perm_new_member'):
562 del value[k]
562 del value[k]
563 _type, part = k.split('perm_new_member_')
563 _type, part = k.split('perm_new_member_')
564 args = part.split('_')
564 args = part.split('_')
565 if len(args) == 1:
565 if len(args) == 1:
566 new_perms_group[args[0]]['perm'] = v
566 new_perms_group[args[0]]['perm'] = v
567 elif len(args) == 2:
567 elif len(args) == 2:
568 _key, pos = args
568 _key, pos = args
569 new_perms_group[pos][_key] = v
569 new_perms_group[pos][_key] = v
570
570
571 # fill new permissions in order of how they were added
571 # fill new permissions in order of how they were added
572 for k in sorted(map(int, new_perms_group.keys())):
572 for k in sorted(map(int, new_perms_group.keys())):
573 perm_dict = new_perms_group[str(k)]
573 perm_dict = new_perms_group[str(k)]
574 new_member = perm_dict.get('name')
574 new_member = perm_dict.get('name')
575 new_perm = perm_dict.get('perm')
575 new_perm = perm_dict.get('perm')
576 new_type = perm_dict.get('type')
576 new_type = perm_dict.get('type')
577 if new_member and new_perm and new_type:
577 if new_member and new_perm and new_type:
578 perms_new.add((new_member, new_perm, new_type))
578 perms_new.add((new_member, new_perm, new_type))
579
579
580 for k, v in value.iteritems():
580 for k, v in value.iteritems():
581 if k.startswith('u_perm_') or k.startswith('g_perm_'):
581 if k.startswith('u_perm_') or k.startswith('g_perm_'):
582 member = k[7:]
582 member = k[7:]
583 t = {'u': 'user',
583 t = {'u': 'user',
584 'g': 'users_group'
584 'g': 'users_group'
585 }[k[0]]
585 }[k[0]]
586 if member == 'default':
586 if member == 'default':
587 if value.get('repo_private'):
587 if value.get('repo_private'):
588 # set none for default when updating to
588 # set none for default when updating to
589 # private repo
589 # private repo
590 v = EMPTY_PERM
590 v = EMPTY_PERM
591 perms_update.add((member, v, t))
591 perms_update.add((member, v, t))
592 #always set NONE when private flag is set
592 #always set NONE when private flag is set
593 if value.get('repo_private'):
593 if value.get('repo_private'):
594 perms_update.add(('default', EMPTY_PERM, 'user'))
594 perms_update.add(('default', EMPTY_PERM, 'user'))
595
595
596 value['perms_updates'] = list(perms_update)
596 value['perms_updates'] = list(perms_update)
597 value['perms_new'] = list(perms_new)
597 value['perms_new'] = list(perms_new)
598
598
599 # update permissions
599 # update permissions
600 for k, v, t in perms_new:
600 for k, v, t in perms_new:
601 try:
601 try:
602 if t is 'user':
602 if t is 'user':
603 self.user_db = User.query()\
603 self.user_db = User.query()\
604 .filter(User.active == True)\
604 .filter(User.active == True)\
605 .filter(User.username == k).one()
605 .filter(User.username == k).one()
606 if t is 'users_group':
606 if t is 'users_group':
607 self.user_db = UsersGroup.query()\
607 self.user_db = UsersGroup.query()\
608 .filter(UsersGroup.users_group_active == True)\
608 .filter(UsersGroup.users_group_active == True)\
609 .filter(UsersGroup.users_group_name == k).one()
609 .filter(UsersGroup.users_group_name == k).one()
610
610
611 except Exception:
611 except Exception:
612 log.exception('Updated permission failed')
612 log.exception('Updated permission failed')
613 msg = M(self, 'perm_new_member_type', state)
613 msg = M(self, 'perm_new_member_type', state)
614 raise formencode.Invalid(msg, value, state,
614 raise formencode.Invalid(msg, value, state,
615 error_dict=dict(perm_new_member_name=msg)
615 error_dict=dict(perm_new_member_name=msg)
616 )
616 )
617 return value
617 return value
618 return _validator
618 return _validator
619
619
620
620
621 def ValidSettings():
621 def ValidSettings():
622 class _validator(formencode.validators.FancyValidator):
622 class _validator(formencode.validators.FancyValidator):
623 def _to_python(self, value, state):
623 def _to_python(self, value, state):
624 # settings form for users that are not admin
624 # settings form for users that are not admin
625 # can't edit certain parameters, it's extra backup if they mangle
625 # can't edit certain parameters, it's extra backup if they mangle
626 # with forms
626 # with forms
627
627
628 forbidden_params = [
628 forbidden_params = [
629 'user', 'repo_type', 'repo_enable_locking',
629 'user', 'repo_type', 'repo_enable_locking',
630 'repo_enable_downloads', 'repo_enable_statistics'
630 'repo_enable_downloads', 'repo_enable_statistics'
631 ]
631 ]
632
632
633 for param in forbidden_params:
633 for param in forbidden_params:
634 if param in value:
634 if param in value:
635 del value[param]
635 del value[param]
636 return value
636 return value
637
637
638 def validate_python(self, value, state):
638 def validate_python(self, value, state):
639 pass
639 pass
640 return _validator
640 return _validator
641
641
642
642
643 def ValidPath():
643 def ValidPath():
644 class _validator(formencode.validators.FancyValidator):
644 class _validator(formencode.validators.FancyValidator):
645 messages = {
645 messages = {
646 'invalid_path': _(u'This is not a valid path')
646 'invalid_path': _(u'This is not a valid path')
647 }
647 }
648
648
649 def validate_python(self, value, state):
649 def validate_python(self, value, state):
650 if not os.path.isdir(value):
650 if not os.path.isdir(value):
651 msg = M(self, 'invalid_path', state)
651 msg = M(self, 'invalid_path', state)
652 raise formencode.Invalid(msg, value, state,
652 raise formencode.Invalid(msg, value, state,
653 error_dict=dict(paths_root_path=msg)
653 error_dict=dict(paths_root_path=msg)
654 )
654 )
655 return _validator
655 return _validator
656
656
657
657
658 def UniqSystemEmail(old_data={}):
658 def UniqSystemEmail(old_data={}):
659 class _validator(formencode.validators.FancyValidator):
659 class _validator(formencode.validators.FancyValidator):
660 messages = {
660 messages = {
661 'email_taken': _(u'This e-mail address is already taken')
661 'email_taken': _(u'This e-mail address is already taken')
662 }
662 }
663
663
664 def _to_python(self, value, state):
664 def _to_python(self, value, state):
665 return value.lower()
665 return value.lower()
666
666
667 def validate_python(self, value, state):
667 def validate_python(self, value, state):
668 if (old_data.get('email') or '').lower() != value:
668 if (old_data.get('email') or '').lower() != value:
669 user = User.get_by_email(value, case_insensitive=True)
669 user = User.get_by_email(value, case_insensitive=True)
670 if user:
670 if user:
671 msg = M(self, 'email_taken', state)
671 msg = M(self, 'email_taken', state)
672 raise formencode.Invalid(msg, value, state,
672 raise formencode.Invalid(msg, value, state,
673 error_dict=dict(email=msg)
673 error_dict=dict(email=msg)
674 )
674 )
675 return _validator
675 return _validator
676
676
677
677
678 def ValidSystemEmail():
678 def ValidSystemEmail():
679 class _validator(formencode.validators.FancyValidator):
679 class _validator(formencode.validators.FancyValidator):
680 messages = {
680 messages = {
681 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
681 'non_existing_email': _(u'e-mail "%(email)s" does not exist.')
682 }
682 }
683
683
684 def _to_python(self, value, state):
684 def _to_python(self, value, state):
685 return value.lower()
685 return value.lower()
686
686
687 def validate_python(self, value, state):
687 def validate_python(self, value, state):
688 user = User.get_by_email(value, case_insensitive=True)
688 user = User.get_by_email(value, case_insensitive=True)
689 if user is None:
689 if user is None:
690 msg = M(self, 'non_existing_email', state, email=value)
690 msg = M(self, 'non_existing_email', state, email=value)
691 raise formencode.Invalid(msg, value, state,
691 raise formencode.Invalid(msg, value, state,
692 error_dict=dict(email=msg)
692 error_dict=dict(email=msg)
693 )
693 )
694
694
695 return _validator
695 return _validator
696
696
697
697
698 def LdapLibValidator():
698 def LdapLibValidator():
699 class _validator(formencode.validators.FancyValidator):
699 class _validator(formencode.validators.FancyValidator):
700 messages = {
700 messages = {
701
701
702 }
702 }
703
703
704 def validate_python(self, value, state):
704 def validate_python(self, value, state):
705 try:
705 try:
706 import ldap
706 import ldap
707 ldap # pyflakes silence !
707 ldap # pyflakes silence !
708 except ImportError:
708 except ImportError:
709 raise LdapImportError()
709 raise LdapImportError()
710
710
711 return _validator
711 return _validator
712
712
713
713
714 def AttrLoginValidator():
714 def AttrLoginValidator():
715 class _validator(formencode.validators.FancyValidator):
715 class _validator(formencode.validators.FancyValidator):
716 messages = {
716 messages = {
717 'invalid_cn':
717 'invalid_cn':
718 _(u'The LDAP Login attribute of the CN must be specified - '
718 _(u'The LDAP Login attribute of the CN must be specified - '
719 'this is the name of the attribute that is equivalent '
719 'this is the name of the attribute that is equivalent '
720 'to "username"')
720 'to "username"')
721 }
721 }
722
722
723 def validate_python(self, value, state):
723 def validate_python(self, value, state):
724 if not value or not isinstance(value, (str, unicode)):
724 if not value or not isinstance(value, (str, unicode)):
725 msg = M(self, 'invalid_cn', state)
725 msg = M(self, 'invalid_cn', state)
726 raise formencode.Invalid(msg, value, state,
726 raise formencode.Invalid(msg, value, state,
727 error_dict=dict(ldap_attr_login=msg)
727 error_dict=dict(ldap_attr_login=msg)
728 )
728 )
729
729
730 return _validator
730 return _validator
731
731
732
732
733 def NotReviewedRevisions(repo_id):
733 def NotReviewedRevisions(repo_id):
734 class _validator(formencode.validators.FancyValidator):
734 class _validator(formencode.validators.FancyValidator):
735 messages = {
735 messages = {
736 'rev_already_reviewed':
736 'rev_already_reviewed':
737 _(u'Revisions %(revs)s are already part of pull request '
737 _(u'Revisions %(revs)s are already part of pull request '
738 'or have set status')
738 'or have set status')
739 }
739 }
740
740
741 def validate_python(self, value, state):
741 def validate_python(self, value, state):
742 # check revisions if they are not reviewed, or a part of another
742 # check revisions if they are not reviewed, or a part of another
743 # pull request
743 # pull request
744 statuses = ChangesetStatus.query()\
744 statuses = ChangesetStatus.query()\
745 .filter(ChangesetStatus.revision.in_(value))\
745 .filter(ChangesetStatus.revision.in_(value))\
746 .filter(ChangesetStatus.repo_id == repo_id)\
746 .filter(ChangesetStatus.repo_id == repo_id)\
747 .all()
747 .all()
748
748
749 errors = []
749 errors = []
750 for cs in statuses:
750 for cs in statuses:
751 if cs.pull_request_id:
751 if cs.pull_request_id:
752 errors.append(['pull_req', cs.revision[:12]])
752 errors.append(['pull_req', cs.revision[:12]])
753 elif cs.status:
753 elif cs.status:
754 errors.append(['status', cs.revision[:12]])
754 errors.append(['status', cs.revision[:12]])
755
755
756 if errors:
756 if errors:
757 revs = ','.join([x[1] for x in errors])
757 revs = ','.join([x[1] for x in errors])
758 msg = M(self, 'rev_already_reviewed', state, revs=revs)
758 msg = M(self, 'rev_already_reviewed', state, revs=revs)
759 raise formencode.Invalid(msg, value, state,
759 raise formencode.Invalid(msg, value, state,
760 error_dict=dict(revisions=revs)
760 error_dict=dict(revisions=revs)
761 )
761 )
762
762
763 return _validator
763 return _validator
764
764
765
765
766 def ValidIp():
766 def ValidIp():
767 class _validator(CIDR):
767 class _validator(CIDR):
768 messages = dict(
768 messages = dict(
769 badFormat=_('Please enter a valid IPv4 or IpV6 address'),
769 badFormat=_('Please enter a valid IPv4 or IpV6 address'),
770 illegalBits=_('The network size (bits) must be within the range'
770 illegalBits=_('The network size (bits) must be within the range'
771 ' of 0-32 (not %(bits)r)'))
771 ' of 0-32 (not %(bits)r)'))
772
772
773 def to_python(self, value, state):
773 def to_python(self, value, state):
774 v = super(_validator, self).to_python(value, state)
774 v = super(_validator, self).to_python(value, state)
775 v = v.strip()
775 v = v.strip()
776 net = ipaddr.IPNetwork(address=v)
776 net = ipaddr.IPNetwork(address=v)
777 if isinstance(net, ipaddr.IPv4Network):
777 if isinstance(net, ipaddr.IPv4Network):
778 #if IPv4 doesn't end with a mask, add /32
778 #if IPv4 doesn't end with a mask, add /32
779 if '/' not in value:
779 if '/' not in value:
780 v += '/32'
780 v += '/32'
781 if isinstance(net, ipaddr.IPv6Network):
781 if isinstance(net, ipaddr.IPv6Network):
782 #if IPv6 doesn't end with a mask, add /128
782 #if IPv6 doesn't end with a mask, add /128
783 if '/' not in value:
783 if '/' not in value:
784 v += '/128'
784 v += '/128'
785 return v
785 return v
786
786
787 def validate_python(self, value, state):
787 def validate_python(self, value, state):
788 try:
788 try:
789 addr = value.strip()
789 addr = value.strip()
790 #this raises an ValueError if address is not IpV4 or IpV6
790 #this raises an ValueError if address is not IpV4 or IpV6
791 ipaddr.IPNetwork(address=addr)
791 ipaddr.IPNetwork(address=addr)
792 except ValueError:
792 except ValueError:
793 raise formencode.Invalid(self.message('badFormat', state),
793 raise formencode.Invalid(self.message('badFormat', state),
794 value, state)
794 value, state)
795
795
796 return _validator
796 return _validator
797
797
798
798
799 def FieldKey():
799 def FieldKey():
800 class _validator(formencode.validators.FancyValidator):
800 class _validator(formencode.validators.FancyValidator):
801 messages = dict(
801 messages = dict(
802 badFormat=_('Key name can only consist of letters, '
802 badFormat=_('Key name can only consist of letters, '
803 'underscore, dash or numbers'),)
803 'underscore, dash or numbers'),)
804
804
805 def validate_python(self, value, state):
805 def validate_python(self, value, state):
806 if not re.match('[a-zA-Z0-9_-]+$', value):
806 if not re.match('[a-zA-Z0-9_-]+$', value):
807 raise formencode.Invalid(self.message('badFormat', state),
807 raise formencode.Invalid(self.message('badFormat', state),
808 value, state)
808 value, state)
809 return _validator
809 return _validator
@@ -1,78 +1,78 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Repository groups administration')} - ${c.rhodecode_name}
5 ${_('Repository groups administration')} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8
8
9 <%def name="breadcrumbs_links()">
9 <%def name="breadcrumbs_links()">
10 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 ${h.link_to(_('Admin'),h.url('admin_home'))}
11 &raquo;
11 &raquo;
12 ${_('repositories groups')}
12 ${_('repository groups')}
13 </%def>
13 </%def>
14 <%def name="page_nav()">
14 <%def name="page_nav()">
15 ${self.menu('admin')}
15 ${self.menu('admin')}
16 </%def>
16 </%def>
17 <%def name="main()">
17 <%def name="main()">
18 <div class="box">
18 <div class="box">
19 <!-- box / title -->
19 <!-- box / title -->
20 <div class="title">
20 <div class="title">
21 ${self.breadcrumbs()}
21 ${self.breadcrumbs()}
22 <ul class="links">
22 <ul class="links">
23 <li>
23 <li>
24 %if h.HasPermissionAny('hg.admin')():
24 %if h.HasPermissionAny('hg.admin')():
25 <span>${h.link_to(_(u'Add group'),h.url('new_repos_group'))}</span>
25 <span>${h.link_to(_(u'Add group'),h.url('new_repos_group'))}</span>
26 %endif
26 %endif
27 </li>
27 </li>
28 </ul>
28 </ul>
29 </div>
29 </div>
30 <!-- end box / title -->
30 <!-- end box / title -->
31 <div class="table">
31 <div class="table">
32 % if c.groups:
32 % if c.groups:
33 <table class="table_disp">
33 <table class="table_disp">
34
34
35 <thead>
35 <thead>
36 <tr>
36 <tr>
37 <th class="left"><a href="#">${_('Group name')}</a></th>
37 <th class="left"><a href="#">${_('Group name')}</a></th>
38 <th class="left"><a href="#">${_('Description')}</a></th>
38 <th class="left"><a href="#">${_('Description')}</a></th>
39 <th class="left"><a href="#">${_('Number of toplevel repositories')}</a></th>
39 <th class="left"><a href="#">${_('Number of toplevel repositories')}</a></th>
40 <th class="left" colspan="2">${_('action')}</th>
40 <th class="left" colspan="2">${_('action')}</th>
41 </tr>
41 </tr>
42 </thead>
42 </thead>
43
43
44 ## REPO GROUPS
44 ## REPO GROUPS
45
45
46 % for gr in c.groups:
46 % for gr in c.groups:
47 <% gr_cn = gr.repositories.count() %>
47 <% gr_cn = gr.repositories.count() %>
48 <tr>
48 <tr>
49 <td>
49 <td>
50 <div style="white-space: nowrap">
50 <div style="white-space: nowrap">
51 <img class="icon" alt="${_('Repository group')}" src="${h.url('/images/icons/database_link.png')}"/>
51 <img class="icon" alt="${_('Repository group')}" src="${h.url('/images/icons/database_link.png')}"/>
52 ${h.link_to(h.literal(' &raquo; '.join(map(h.safe_unicode,[g.name for g in gr.parents+[gr]]))), url('repos_group_home',group_name=gr.group_name))}
52 ${h.link_to(h.literal(' &raquo; '.join(map(h.safe_unicode,[g.name for g in gr.parents+[gr]]))), url('repos_group_home',group_name=gr.group_name))}
53 </div>
53 </div>
54 </td>
54 </td>
55 <td>${gr.group_description}</td>
55 <td>${gr.group_description}</td>
56 <td><b>${gr_cn}</b></td>
56 <td><b>${gr_cn}</b></td>
57 <td>
57 <td>
58 <a href="${h.url('edit_repos_group',group_name=gr.group_name)}" title="${_('edit')}">
58 <a href="${h.url('edit_repos_group',group_name=gr.group_name)}" title="${_('edit')}">
59 ${h.submit('edit_%s' % gr.group_name,_('edit'),class_="edit_icon action_button")}
59 ${h.submit('edit_%s' % gr.group_name,_('edit'),class_="edit_icon action_button")}
60 </a>
60 </a>
61 </td>
61 </td>
62 <td>
62 <td>
63 ${h.form(url('repos_group', group_name=gr.group_name),method='delete')}
63 ${h.form(url('repos_group', group_name=gr.group_name),method='delete')}
64 ${h.submit('remove_%s' % gr.name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_cn) % (gr.name,gr_cn)+"');")}
64 ${h.submit('remove_%s' % gr.name,_('delete'),class_="delete_icon action_button",onclick="return confirm('"+ungettext('Confirm to delete this group: %s with %s repository','Confirm to delete this group: %s with %s repositories',gr_cn) % (gr.name,gr_cn)+"');")}
65 ${h.end_form()}
65 ${h.end_form()}
66 </td>
66 </td>
67 </tr>
67 </tr>
68 % endfor
68 % endfor
69
69
70 </table>
70 </table>
71 % else:
71 % else:
72 ${_('There are no repository groups yet')}
72 ${_('There are no repository groups yet')}
73 % endif
73 % endif
74
74
75 </div>
75 </div>
76 </div>
76 </div>
77
77
78 </%def>
78 </%def>
@@ -1,228 +1,228 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.html"/>
2 <%inherit file="/base/base.html"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Edit users group')} ${c.users_group.users_group_name} - ${c.rhodecode_name}
5 ${_('Edit user group')} ${c.users_group.users_group_name} - ${c.rhodecode_name}
6 </%def>
6 </%def>
7
7
8 <%def name="breadcrumbs_links()">
8 <%def name="breadcrumbs_links()">
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
9 ${h.link_to(_('Admin'),h.url('admin_home'))}
10 &raquo;
10 &raquo;
11 ${h.link_to(_('UsersGroups'),h.url('users_groups'))}
11 ${h.link_to(_('UsersGroups'),h.url('users_groups'))}
12 &raquo;
12 &raquo;
13 ${_('edit')} "${c.users_group.users_group_name}"
13 ${_('edit')} "${c.users_group.users_group_name}"
14 </%def>
14 </%def>
15
15
16 <%def name="page_nav()">
16 <%def name="page_nav()">
17 ${self.menu('admin')}
17 ${self.menu('admin')}
18 </%def>
18 </%def>
19
19
20 <%def name="main()">
20 <%def name="main()">
21 <div class="box box-left">
21 <div class="box box-left">
22 <!-- box / title -->
22 <!-- box / title -->
23 <div class="title">
23 <div class="title">
24 ${self.breadcrumbs()}
24 ${self.breadcrumbs()}
25 </div>
25 </div>
26 <!-- end box / title -->
26 <!-- end box / title -->
27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
27 ${h.form(url('users_group', id=c.users_group.users_group_id),method='put', id='edit_users_group')}
28 <div class="form">
28 <div class="form">
29 <!-- fields -->
29 <!-- fields -->
30 <div class="fields">
30 <div class="fields">
31 <div class="field">
31 <div class="field">
32 <div class="label">
32 <div class="label">
33 <label for="users_group_name">${_('Group name')}:</label>
33 <label for="users_group_name">${_('Group name')}:</label>
34 </div>
34 </div>
35 <div class="input">
35 <div class="input">
36 ${h.text('users_group_name',class_='small')}
36 ${h.text('users_group_name',class_='small')}
37 </div>
37 </div>
38 </div>
38 </div>
39
39
40 <div class="field">
40 <div class="field">
41 <div class="label label-checkbox">
41 <div class="label label-checkbox">
42 <label for="users_group_active">${_('Active')}:</label>
42 <label for="users_group_active">${_('Active')}:</label>
43 </div>
43 </div>
44 <div class="checkboxes">
44 <div class="checkboxes">
45 ${h.checkbox('users_group_active',value=True)}
45 ${h.checkbox('users_group_active',value=True)}
46 </div>
46 </div>
47 </div>
47 </div>
48 <div class="field">
48 <div class="field">
49 <div class="label">
49 <div class="label">
50 <label for="users_group_active">${_('Members')}:</label>
50 <label for="users_group_active">${_('Members')}:</label>
51 </div>
51 </div>
52 <div class="select">
52 <div class="select">
53 <table>
53 <table>
54 <tr>
54 <tr>
55 <td>
55 <td>
56 <div>
56 <div>
57 <div style="float:left">
57 <div style="float:left">
58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
58 <div class="text" style="padding: 0px 0px 6px;">${_('Choosen group members')}</div>
59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
59 ${h.select('users_group_members',[x[0] for x in c.group_members],c.group_members,multiple=True,size=8,style="min-width:210px")}
60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
60 <div id="remove_all_elements" style="cursor:pointer;text-align:center">
61 ${_('Remove all elements')}
61 ${_('Remove all elements')}
62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
62 <img alt="remove" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_right.png')}"/>
63 </div>
63 </div>
64 </div>
64 </div>
65 <div style="float:left;width:20px;padding-top:50px">
65 <div style="float:left;width:20px;padding-top:50px">
66 <img alt="add" id="add_element"
66 <img alt="add" id="add_element"
67 style="padding:2px;cursor:pointer"
67 style="padding:2px;cursor:pointer"
68 src="${h.url('/images/icons/arrow_left.png')}"/>
68 src="${h.url('/images/icons/arrow_left.png')}"/>
69 <br />
69 <br />
70 <img alt="remove" id="remove_element"
70 <img alt="remove" id="remove_element"
71 style="padding:2px;cursor:pointer"
71 style="padding:2px;cursor:pointer"
72 src="${h.url('/images/icons/arrow_right.png')}"/>
72 src="${h.url('/images/icons/arrow_right.png')}"/>
73 </div>
73 </div>
74 <div style="float:left">
74 <div style="float:left">
75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
75 <div class="text" style="padding: 0px 0px 6px;">${_('Available members')}</div>
76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
76 ${h.select('available_members',[],c.available_members,multiple=True,size=8,style="min-width:210px")}
77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
77 <div id="add_all_elements" style="cursor:pointer;text-align:center">
78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
78 <img alt="add" style="vertical-align:text-bottom" src="${h.url('/images/icons/arrow_left.png')}"/>
79 ${_('Add all elements')}
79 ${_('Add all elements')}
80 </div>
80 </div>
81 </div>
81 </div>
82 </div>
82 </div>
83 </td>
83 </td>
84 </tr>
84 </tr>
85 </table>
85 </table>
86 </div>
86 </div>
87
87
88 </div>
88 </div>
89 <div class="buttons">
89 <div class="buttons">
90 ${h.submit('save',_('save'),class_="ui-btn large")}
90 ${h.submit('save',_('save'),class_="ui-btn large")}
91 </div>
91 </div>
92 </div>
92 </div>
93 </div>
93 </div>
94 ${h.end_form()}
94 ${h.end_form()}
95 </div>
95 </div>
96
96
97 <div class="box box-right">
97 <div class="box box-right">
98 <!-- box / title -->
98 <!-- box / title -->
99 <div class="title">
99 <div class="title">
100 <h5>${_('Permissions')}</h5>
100 <h5>${_('Permissions')}</h5>
101 </div>
101 </div>
102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
102 ${h.form(url('users_group_perm', id=c.users_group.users_group_id), method='put')}
103 <div class="form">
103 <div class="form">
104 <!-- fields -->
104 <!-- fields -->
105 <div class="fields">
105 <div class="fields">
106 <div class="field">
106 <div class="field">
107 <div class="label label-checkbox">
107 <div class="label label-checkbox">
108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
108 <label for="inherit_permissions">${_('Inherit default permissions')}:</label>
109 </div>
109 </div>
110 <div class="checkboxes">
110 <div class="checkboxes">
111 ${h.checkbox('inherit_default_permissions',value=True)}
111 ${h.checkbox('inherit_default_permissions',value=True)}
112 </div>
112 </div>
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
113 <span class="help-block">${h.literal(_('Select to inherit permissions from %s settings. '
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
114 'With this selected below options does not have any action') % h.link_to('default', url('edit_permission', id='default')))}</span>
115 </div>
115 </div>
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
116 <div id="inherit_overlay" style="${'opacity:0.3' if c.users_group.inherit_default_permissions else ''}" >
117 <div class="field">
117 <div class="field">
118 <div class="label label-checkbox">
118 <div class="label label-checkbox">
119 <label for="create_repo_perm">${_('Create repositories')}:</label>
119 <label for="create_repo_perm">${_('Create repositories')}:</label>
120 </div>
120 </div>
121 <div class="checkboxes">
121 <div class="checkboxes">
122 ${h.checkbox('create_repo_perm',value=True)}
122 ${h.checkbox('create_repo_perm',value=True)}
123 </div>
123 </div>
124 </div>
124 </div>
125 <div class="field">
125 <div class="field">
126 <div class="label label-checkbox">
126 <div class="label label-checkbox">
127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
127 <label for="fork_repo_perm">${_('Fork repositories')}:</label>
128 </div>
128 </div>
129 <div class="checkboxes">
129 <div class="checkboxes">
130 ${h.checkbox('fork_repo_perm',value=True)}
130 ${h.checkbox('fork_repo_perm',value=True)}
131 </div>
131 </div>
132 </div>
132 </div>
133 </div>
133 </div>
134 <div class="buttons">
134 <div class="buttons">
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
135 ${h.submit('save',_('Save'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
136 ${h.reset('reset',_('Reset'),class_="ui-btn large")}
137 </div>
137 </div>
138 </div>
138 </div>
139 </div>
139 </div>
140 ${h.end_form()}
140 ${h.end_form()}
141 </div>
141 </div>
142
142
143 <div class="box box-right">
143 <div class="box box-right">
144 <!-- box / title -->
144 <!-- box / title -->
145 <div class="title">
145 <div class="title">
146 <h5>${_('Group members')}</h5>
146 <h5>${_('Group members')}</h5>
147 </div>
147 </div>
148
148
149 <div class="group_members_wrap">
149 <div class="group_members_wrap">
150 % if c.group_members_obj:
150 % if c.group_members_obj:
151 <ul class="group_members">
151 <ul class="group_members">
152 %for user in c.group_members_obj:
152 %for user in c.group_members_obj:
153 <li>
153 <li>
154 <div class="group_member">
154 <div class="group_member">
155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
155 <div class="gravatar"><img alt="gravatar" src="${h.gravatar_url(user.email,24)}"/> </div>
156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
156 <div>${h.link_to(user.username, h.url('edit_user',id=user.user_id))}</div>
157 <div>${user.full_name}</div>
157 <div>${user.full_name}</div>
158 </div>
158 </div>
159 </li>
159 </li>
160 %endfor
160 %endfor
161 </ul>
161 </ul>
162 %else:
162 %else:
163 <span class="empty_data">${_('No members yet')}</span>
163 <span class="empty_data">${_('No members yet')}</span>
164 %endif
164 %endif
165 </div>
165 </div>
166 </div>
166 </div>
167
167
168 <div class="box box-left">
168 <div class="box box-left">
169 <!-- box / title -->
169 <!-- box / title -->
170 <div class="title">
170 <div class="title">
171 <h5>${_('Permissions defined for this group')}</h5>
171 <h5>${_('Permissions defined for this group')}</h5>
172 </div>
172 </div>
173 ## permissions overview
173 ## permissions overview
174 <div id="perms" class="table">
174 <div id="perms" class="table">
175 %for section in sorted(c.users_group.permissions.keys()):
175 %for section in sorted(c.users_group.permissions.keys()):
176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
176 <div class="perms_section_head">${section.replace("_"," ").capitalize()}</div>
177 %if not c.users_group.permissions:
177 %if not c.users_group.permissions:
178 <span class="empty_data">${_('No permissions set yet')}</span>
178 <span class="empty_data">${_('No permissions set yet')}</span>
179 %else:
179 %else:
180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
180 <div id='tbl_list_wrap_${section}' class="yui-skin-sam">
181 <table id="tbl_list_repository">
181 <table id="tbl_list_repository">
182 <thead>
182 <thead>
183 <tr>
183 <tr>
184 <th class="left">${_('Name')}</th>
184 <th class="left">${_('Name')}</th>
185 <th class="left">${_('Permission')}</th>
185 <th class="left">${_('Permission')}</th>
186 <th class="left">${_('Edit Permission')}</th>
186 <th class="left">${_('Edit Permission')}</th>
187 </thead>
187 </thead>
188 <tbody>
188 <tbody>
189 %for k in c.users_group.permissions[section]:
189 %for k in c.users_group.permissions[section]:
190 <%
190 <%
191 section_perm = c.users_group.permissions[section].get(k)
191 section_perm = c.users_group.permissions[section].get(k)
192 _perm = section_perm.split('.')[-1]
192 _perm = section_perm.split('.')[-1]
193 %>
193 %>
194 <tr>
194 <tr>
195 <td>
195 <td>
196 %if section == 'repositories':
196 %if section == 'repositories':
197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
197 <a href="${h.url('summary_home',repo_name=k)}">${k}</a>
198 %elif section == 'repositories_groups':
198 %elif section == 'repositories_groups':
199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
199 <a href="${h.url('repos_group_home',group_name=k)}">${k}</a>
200 %endif
200 %endif
201 </td>
201 </td>
202 <td>
202 <td>
203 <span class="perm_tag ${_perm}">${section_perm}</span>
203 <span class="perm_tag ${_perm}">${section_perm}</span>
204 </td>
204 </td>
205 <td>
205 <td>
206 %if section == 'repositories':
206 %if section == 'repositories':
207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
207 <a href="${h.url('edit_repo',repo_name=k,anchor='permissions_manage')}">${_('edit')}</a>
208 %elif section == 'repositories_groups':
208 %elif section == 'repositories_groups':
209 <a href="${h.url('edit_repos_group',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
209 <a href="${h.url('edit_repos_group',group_name=k,anchor='permissions_manage')}">${_('edit')}</a>
210 %else:
210 %else:
211 --
211 --
212 %endif
212 %endif
213 </td>
213 </td>
214 </tr>
214 </tr>
215 %endfor
215 %endfor
216 </tbody>
216 </tbody>
217 </table>
217 </table>
218 </div>
218 </div>
219 %endif
219 %endif
220 %endfor
220 %endfor
221 </div>
221 </div>
222 </div>
222 </div>
223
223
224
224
225 <script type="text/javascript">
225 <script type="text/javascript">
226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
226 MultiSelectWidget('users_group_members','available_members','edit_users_group');
227 </script>
227 </script>
228 </%def>
228 </%def>
@@ -1,1294 +1,1294 b''
1 from __future__ import with_statement
1 from __future__ import with_statement
2 import random
2 import random
3 import mock
3 import mock
4
4
5 from rhodecode.tests import *
5 from rhodecode.tests import *
6 from rhodecode.lib.compat import json
6 from rhodecode.lib.compat import json
7 from rhodecode.lib.auth import AuthUser
7 from rhodecode.lib.auth import AuthUser
8 from rhodecode.model.user import UserModel
8 from rhodecode.model.user import UserModel
9 from rhodecode.model.users_group import UsersGroupModel
9 from rhodecode.model.users_group import UsersGroupModel
10 from rhodecode.model.repo import RepoModel
10 from rhodecode.model.repo import RepoModel
11 from rhodecode.model.meta import Session
11 from rhodecode.model.meta import Session
12 from rhodecode.model.scm import ScmModel
12 from rhodecode.model.scm import ScmModel
13 from rhodecode.model.db import Repository
13 from rhodecode.model.db import Repository
14
14
15 API_URL = '/_admin/api'
15 API_URL = '/_admin/api'
16
16
17
17
18 def _build_data(apikey, method, **kw):
18 def _build_data(apikey, method, **kw):
19 """
19 """
20 Builds API data with given random ID
20 Builds API data with given random ID
21
21
22 :param random_id:
22 :param random_id:
23 :type random_id:
23 :type random_id:
24 """
24 """
25 random_id = random.randrange(1, 9999)
25 random_id = random.randrange(1, 9999)
26 return random_id, json.dumps({
26 return random_id, json.dumps({
27 "id": random_id,
27 "id": random_id,
28 "api_key": apikey,
28 "api_key": apikey,
29 "method": method,
29 "method": method,
30 "args": kw
30 "args": kw
31 })
31 })
32
32
33 jsonify = lambda obj: json.loads(json.dumps(obj))
33 jsonify = lambda obj: json.loads(json.dumps(obj))
34
34
35
35
36 def crash(*args, **kwargs):
36 def crash(*args, **kwargs):
37 raise Exception('Total Crash !')
37 raise Exception('Total Crash !')
38
38
39
39
40 def api_call(test_obj, params):
40 def api_call(test_obj, params):
41 response = test_obj.app.post(API_URL, content_type='application/json',
41 response = test_obj.app.post(API_URL, content_type='application/json',
42 params=params)
42 params=params)
43 return response
43 return response
44
44
45
45
46 TEST_USERS_GROUP = 'test_users_group'
46 TEST_USERS_GROUP = 'test_users_group'
47
47
48
48
49 def make_users_group(name=TEST_USERS_GROUP):
49 def make_users_group(name=TEST_USERS_GROUP):
50 gr = UsersGroupModel().create(name=name)
50 gr = UsersGroupModel().create(name=name)
51 UsersGroupModel().add_user_to_group(users_group=gr,
51 UsersGroupModel().add_user_to_group(users_group=gr,
52 user=TEST_USER_ADMIN_LOGIN)
52 user=TEST_USER_ADMIN_LOGIN)
53 Session().commit()
53 Session().commit()
54 return gr
54 return gr
55
55
56
56
57 def destroy_users_group(name=TEST_USERS_GROUP):
57 def destroy_users_group(name=TEST_USERS_GROUP):
58 UsersGroupModel().delete(users_group=name, force=True)
58 UsersGroupModel().delete(users_group=name, force=True)
59 Session().commit()
59 Session().commit()
60
60
61
61
62 def create_repo(repo_name, repo_type, owner=None):
62 def create_repo(repo_name, repo_type, owner=None):
63 # create new repo
63 # create new repo
64 form_data = _get_repo_create_params(
64 form_data = _get_repo_create_params(
65 repo_name_full=repo_name,
65 repo_name_full=repo_name,
66 repo_description='description %s' % repo_name,
66 repo_description='description %s' % repo_name,
67 )
67 )
68 cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
68 cur_user = UserModel().get_by_username(owner or TEST_USER_ADMIN_LOGIN)
69 r = RepoModel().create(form_data, cur_user)
69 r = RepoModel().create(form_data, cur_user)
70 Session().commit()
70 Session().commit()
71 return r
71 return r
72
72
73
73
74 def create_fork(fork_name, fork_type, fork_of):
74 def create_fork(fork_name, fork_type, fork_of):
75 fork = RepoModel(Session())._get_repo(fork_of)
75 fork = RepoModel(Session())._get_repo(fork_of)
76 r = create_repo(fork_name, fork_type)
76 r = create_repo(fork_name, fork_type)
77 r.fork = fork
77 r.fork = fork
78 Session().add(r)
78 Session().add(r)
79 Session().commit()
79 Session().commit()
80 return r
80 return r
81
81
82
82
83 def destroy_repo(repo_name):
83 def destroy_repo(repo_name):
84 RepoModel().delete(repo_name)
84 RepoModel().delete(repo_name)
85 Session().commit()
85 Session().commit()
86
86
87
87
88 class BaseTestApi(object):
88 class BaseTestApi(object):
89 REPO = None
89 REPO = None
90 REPO_TYPE = None
90 REPO_TYPE = None
91
91
92 @classmethod
92 @classmethod
93 def setUpClass(self):
93 def setUpClass(self):
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
94 self.usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
95 self.apikey = self.usr.api_key
95 self.apikey = self.usr.api_key
96 self.test_user = UserModel().create_or_update(
96 self.test_user = UserModel().create_or_update(
97 username='test-api',
97 username='test-api',
98 password='test',
98 password='test',
99 email='test@api.rhodecode.org',
99 email='test@api.rhodecode.org',
100 firstname='first',
100 firstname='first',
101 lastname='last'
101 lastname='last'
102 )
102 )
103 Session().commit()
103 Session().commit()
104 self.TEST_USER_LOGIN = self.test_user.username
104 self.TEST_USER_LOGIN = self.test_user.username
105 self.apikey_regular = self.test_user.api_key
105 self.apikey_regular = self.test_user.api_key
106
106
107 @classmethod
107 @classmethod
108 def teardownClass(self):
108 def teardownClass(self):
109 pass
109 pass
110
110
111 def setUp(self):
111 def setUp(self):
112 self.maxDiff = None
112 self.maxDiff = None
113 make_users_group()
113 make_users_group()
114
114
115 def tearDown(self):
115 def tearDown(self):
116 destroy_users_group()
116 destroy_users_group()
117
117
118 def _compare_ok(self, id_, expected, given):
118 def _compare_ok(self, id_, expected, given):
119 expected = jsonify({
119 expected = jsonify({
120 'id': id_,
120 'id': id_,
121 'error': None,
121 'error': None,
122 'result': expected
122 'result': expected
123 })
123 })
124 given = json.loads(given)
124 given = json.loads(given)
125 self.assertEqual(expected, given)
125 self.assertEqual(expected, given)
126
126
127 def _compare_error(self, id_, expected, given):
127 def _compare_error(self, id_, expected, given):
128 expected = jsonify({
128 expected = jsonify({
129 'id': id_,
129 'id': id_,
130 'error': expected,
130 'error': expected,
131 'result': None
131 'result': None
132 })
132 })
133 given = json.loads(given)
133 given = json.loads(given)
134 self.assertEqual(expected, given)
134 self.assertEqual(expected, given)
135
135
136 # def test_Optional(self):
136 # def test_Optional(self):
137 # from rhodecode.controllers.api.api import Optional
137 # from rhodecode.controllers.api.api import Optional
138 # option1 = Optional(None)
138 # option1 = Optional(None)
139 # self.assertEqual('<Optional:%s>' % None, repr(option1))
139 # self.assertEqual('<Optional:%s>' % None, repr(option1))
140 #
140 #
141 # self.assertEqual(1, Optional.extract(Optional(1)))
141 # self.assertEqual(1, Optional.extract(Optional(1)))
142 # self.assertEqual('trololo', Optional.extract('trololo'))
142 # self.assertEqual('trololo', Optional.extract('trololo'))
143
143
144 def test_api_wrong_key(self):
144 def test_api_wrong_key(self):
145 id_, params = _build_data('trololo', 'get_user')
145 id_, params = _build_data('trololo', 'get_user')
146 response = api_call(self, params)
146 response = api_call(self, params)
147
147
148 expected = 'Invalid API KEY'
148 expected = 'Invalid API KEY'
149 self._compare_error(id_, expected, given=response.body)
149 self._compare_error(id_, expected, given=response.body)
150
150
151 def test_api_missing_non_optional_param(self):
151 def test_api_missing_non_optional_param(self):
152 id_, params = _build_data(self.apikey, 'get_repo')
152 id_, params = _build_data(self.apikey, 'get_repo')
153 response = api_call(self, params)
153 response = api_call(self, params)
154
154
155 expected = 'Missing non optional `repoid` arg in JSON DATA'
155 expected = 'Missing non optional `repoid` arg in JSON DATA'
156 self._compare_error(id_, expected, given=response.body)
156 self._compare_error(id_, expected, given=response.body)
157
157
158 def test_api_missing_non_optional_param_args_null(self):
158 def test_api_missing_non_optional_param_args_null(self):
159 id_, params = _build_data(self.apikey, 'get_repo')
159 id_, params = _build_data(self.apikey, 'get_repo')
160 params = params.replace('"args": {}', '"args": null')
160 params = params.replace('"args": {}', '"args": null')
161 response = api_call(self, params)
161 response = api_call(self, params)
162
162
163 expected = 'Missing non optional `repoid` arg in JSON DATA'
163 expected = 'Missing non optional `repoid` arg in JSON DATA'
164 self._compare_error(id_, expected, given=response.body)
164 self._compare_error(id_, expected, given=response.body)
165
165
166 def test_api_missing_non_optional_param_args_bad(self):
166 def test_api_missing_non_optional_param_args_bad(self):
167 id_, params = _build_data(self.apikey, 'get_repo')
167 id_, params = _build_data(self.apikey, 'get_repo')
168 params = params.replace('"args": {}', '"args": 1')
168 params = params.replace('"args": {}', '"args": 1')
169 response = api_call(self, params)
169 response = api_call(self, params)
170
170
171 expected = 'Missing non optional `repoid` arg in JSON DATA'
171 expected = 'Missing non optional `repoid` arg in JSON DATA'
172 self._compare_error(id_, expected, given=response.body)
172 self._compare_error(id_, expected, given=response.body)
173
173
174 def test_api_args_is_null(self):
174 def test_api_args_is_null(self):
175 id_, params = _build_data(self.apikey, 'get_users',)
175 id_, params = _build_data(self.apikey, 'get_users',)
176 params = params.replace('"args": {}', '"args": null')
176 params = params.replace('"args": {}', '"args": null')
177 response = api_call(self, params)
177 response = api_call(self, params)
178 self.assertEqual(response.status, '200 OK')
178 self.assertEqual(response.status, '200 OK')
179
179
180 def test_api_args_is_bad(self):
180 def test_api_args_is_bad(self):
181 id_, params = _build_data(self.apikey, 'get_users',)
181 id_, params = _build_data(self.apikey, 'get_users',)
182 params = params.replace('"args": {}', '"args": 1')
182 params = params.replace('"args": {}', '"args": 1')
183 response = api_call(self, params)
183 response = api_call(self, params)
184 self.assertEqual(response.status, '200 OK')
184 self.assertEqual(response.status, '200 OK')
185
185
186 def test_api_get_users(self):
186 def test_api_get_users(self):
187 id_, params = _build_data(self.apikey, 'get_users',)
187 id_, params = _build_data(self.apikey, 'get_users',)
188 response = api_call(self, params)
188 response = api_call(self, params)
189 ret_all = []
189 ret_all = []
190 for usr in UserModel().get_all():
190 for usr in UserModel().get_all():
191 ret = usr.get_api_data()
191 ret = usr.get_api_data()
192 ret_all.append(jsonify(ret))
192 ret_all.append(jsonify(ret))
193 expected = ret_all
193 expected = ret_all
194 self._compare_ok(id_, expected, given=response.body)
194 self._compare_ok(id_, expected, given=response.body)
195
195
196 def test_api_get_user(self):
196 def test_api_get_user(self):
197 id_, params = _build_data(self.apikey, 'get_user',
197 id_, params = _build_data(self.apikey, 'get_user',
198 userid=TEST_USER_ADMIN_LOGIN)
198 userid=TEST_USER_ADMIN_LOGIN)
199 response = api_call(self, params)
199 response = api_call(self, params)
200
200
201 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
201 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
202 ret = usr.get_api_data()
202 ret = usr.get_api_data()
203 ret['permissions'] = AuthUser(usr.user_id).permissions
203 ret['permissions'] = AuthUser(usr.user_id).permissions
204
204
205 expected = ret
205 expected = ret
206 self._compare_ok(id_, expected, given=response.body)
206 self._compare_ok(id_, expected, given=response.body)
207
207
208 def test_api_get_user_that_does_not_exist(self):
208 def test_api_get_user_that_does_not_exist(self):
209 id_, params = _build_data(self.apikey, 'get_user',
209 id_, params = _build_data(self.apikey, 'get_user',
210 userid='trololo')
210 userid='trololo')
211 response = api_call(self, params)
211 response = api_call(self, params)
212
212
213 expected = "user `%s` does not exist" % 'trololo'
213 expected = "user `%s` does not exist" % 'trololo'
214 self._compare_error(id_, expected, given=response.body)
214 self._compare_error(id_, expected, given=response.body)
215
215
216 def test_api_get_user_without_giving_userid(self):
216 def test_api_get_user_without_giving_userid(self):
217 id_, params = _build_data(self.apikey, 'get_user')
217 id_, params = _build_data(self.apikey, 'get_user')
218 response = api_call(self, params)
218 response = api_call(self, params)
219
219
220 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
220 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
221 ret = usr.get_api_data()
221 ret = usr.get_api_data()
222 ret['permissions'] = AuthUser(usr.user_id).permissions
222 ret['permissions'] = AuthUser(usr.user_id).permissions
223
223
224 expected = ret
224 expected = ret
225 self._compare_ok(id_, expected, given=response.body)
225 self._compare_ok(id_, expected, given=response.body)
226
226
227 def test_api_get_user_without_giving_userid_non_admin(self):
227 def test_api_get_user_without_giving_userid_non_admin(self):
228 id_, params = _build_data(self.apikey_regular, 'get_user')
228 id_, params = _build_data(self.apikey_regular, 'get_user')
229 response = api_call(self, params)
229 response = api_call(self, params)
230
230
231 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
231 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
232 ret = usr.get_api_data()
232 ret = usr.get_api_data()
233 ret['permissions'] = AuthUser(usr.user_id).permissions
233 ret['permissions'] = AuthUser(usr.user_id).permissions
234
234
235 expected = ret
235 expected = ret
236 self._compare_ok(id_, expected, given=response.body)
236 self._compare_ok(id_, expected, given=response.body)
237
237
238 def test_api_get_user_with_giving_userid_non_admin(self):
238 def test_api_get_user_with_giving_userid_non_admin(self):
239 id_, params = _build_data(self.apikey_regular, 'get_user',
239 id_, params = _build_data(self.apikey_regular, 'get_user',
240 userid=self.TEST_USER_LOGIN)
240 userid=self.TEST_USER_LOGIN)
241 response = api_call(self, params)
241 response = api_call(self, params)
242
242
243 expected = 'userid is not the same as your user'
243 expected = 'userid is not the same as your user'
244 self._compare_error(id_, expected, given=response.body)
244 self._compare_error(id_, expected, given=response.body)
245
245
246 def test_api_pull(self):
246 def test_api_pull(self):
247 #TODO: issues with rhodecode_extras here.. not sure why !
247 #TODO: issues with rhodecode_extras here.. not sure why !
248 pass
248 pass
249
249
250 # repo_name = 'test_pull'
250 # repo_name = 'test_pull'
251 # r = create_repo(repo_name, self.REPO_TYPE)
251 # r = create_repo(repo_name, self.REPO_TYPE)
252 # r.clone_uri = TEST_self.REPO
252 # r.clone_uri = TEST_self.REPO
253 # Session.add(r)
253 # Session.add(r)
254 # Session.commit()
254 # Session.commit()
255 #
255 #
256 # id_, params = _build_data(self.apikey, 'pull',
256 # id_, params = _build_data(self.apikey, 'pull',
257 # repoid=repo_name,)
257 # repoid=repo_name,)
258 # response = self.app.post(API_URL, content_type='application/json',
258 # response = self.app.post(API_URL, content_type='application/json',
259 # params=params)
259 # params=params)
260 #
260 #
261 # expected = 'Pulled from `%s`' % repo_name
261 # expected = 'Pulled from `%s`' % repo_name
262 # self._compare_ok(id_, expected, given=response.body)
262 # self._compare_ok(id_, expected, given=response.body)
263 #
263 #
264 # destroy_repo(repo_name)
264 # destroy_repo(repo_name)
265
265
266 def test_api_pull_error(self):
266 def test_api_pull_error(self):
267 id_, params = _build_data(self.apikey, 'pull',
267 id_, params = _build_data(self.apikey, 'pull',
268 repoid=self.REPO,)
268 repoid=self.REPO,)
269 response = api_call(self, params)
269 response = api_call(self, params)
270
270
271 expected = 'Unable to pull changes from `%s`' % self.REPO
271 expected = 'Unable to pull changes from `%s`' % self.REPO
272 self._compare_error(id_, expected, given=response.body)
272 self._compare_error(id_, expected, given=response.body)
273
273
274 def test_api_rescan_repos(self):
274 def test_api_rescan_repos(self):
275 id_, params = _build_data(self.apikey, 'rescan_repos')
275 id_, params = _build_data(self.apikey, 'rescan_repos')
276 response = api_call(self, params)
276 response = api_call(self, params)
277
277
278 expected = {'added': [], 'removed': []}
278 expected = {'added': [], 'removed': []}
279 self._compare_ok(id_, expected, given=response.body)
279 self._compare_ok(id_, expected, given=response.body)
280
280
281 @mock.patch.object(ScmModel, 'repo_scan', crash)
281 @mock.patch.object(ScmModel, 'repo_scan', crash)
282 def test_api_rescann_error(self):
282 def test_api_rescann_error(self):
283 id_, params = _build_data(self.apikey, 'rescan_repos',)
283 id_, params = _build_data(self.apikey, 'rescan_repos',)
284 response = api_call(self, params)
284 response = api_call(self, params)
285
285
286 expected = 'Error occurred during rescan repositories action'
286 expected = 'Error occurred during rescan repositories action'
287 self._compare_error(id_, expected, given=response.body)
287 self._compare_error(id_, expected, given=response.body)
288
288
289 def test_api_invalidate_cache(self):
289 def test_api_invalidate_cache(self):
290 id_, params = _build_data(self.apikey, 'invalidate_cache',
290 id_, params = _build_data(self.apikey, 'invalidate_cache',
291 repoid=self.REPO)
291 repoid=self.REPO)
292 response = api_call(self, params)
292 response = api_call(self, params)
293
293
294 expected = ("Cache for repository `%s` was invalidated: "
294 expected = ("Cache for repository `%s` was invalidated: "
295 "invalidated cache keys: %s" % (self.REPO,
295 "invalidated cache keys: %s" % (self.REPO,
296 [unicode(self.REPO)]))
296 [unicode(self.REPO)]))
297 self._compare_ok(id_, expected, given=response.body)
297 self._compare_ok(id_, expected, given=response.body)
298
298
299 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
299 @mock.patch.object(ScmModel, 'mark_for_invalidation', crash)
300 def test_api_invalidate_cache_error(self):
300 def test_api_invalidate_cache_error(self):
301 id_, params = _build_data(self.apikey, 'invalidate_cache',
301 id_, params = _build_data(self.apikey, 'invalidate_cache',
302 repoid=self.REPO)
302 repoid=self.REPO)
303 response = api_call(self, params)
303 response = api_call(self, params)
304
304
305 expected = 'Error occurred during cache invalidation action'
305 expected = 'Error occurred during cache invalidation action'
306 self._compare_error(id_, expected, given=response.body)
306 self._compare_error(id_, expected, given=response.body)
307
307
308 def test_api_lock_repo_lock_aquire(self):
308 def test_api_lock_repo_lock_aquire(self):
309 id_, params = _build_data(self.apikey, 'lock',
309 id_, params = _build_data(self.apikey, 'lock',
310 userid=TEST_USER_ADMIN_LOGIN,
310 userid=TEST_USER_ADMIN_LOGIN,
311 repoid=self.REPO,
311 repoid=self.REPO,
312 locked=True)
312 locked=True)
313 response = api_call(self, params)
313 response = api_call(self, params)
314 expected = ('User `%s` set lock state for repo `%s` to `%s`'
314 expected = ('User `%s` set lock state for repo `%s` to `%s`'
315 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
315 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
316 self._compare_ok(id_, expected, given=response.body)
316 self._compare_ok(id_, expected, given=response.body)
317
317
318 def test_api_lock_repo_lock_aquire_by_non_admin(self):
318 def test_api_lock_repo_lock_aquire_by_non_admin(self):
319 repo_name = 'api_delete_me'
319 repo_name = 'api_delete_me'
320 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
320 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
321 try:
321 try:
322 id_, params = _build_data(self.apikey_regular, 'lock',
322 id_, params = _build_data(self.apikey_regular, 'lock',
323 repoid=repo_name,
323 repoid=repo_name,
324 locked=True)
324 locked=True)
325 response = api_call(self, params)
325 response = api_call(self, params)
326 expected = ('User `%s` set lock state for repo `%s` to `%s`'
326 expected = ('User `%s` set lock state for repo `%s` to `%s`'
327 % (self.TEST_USER_LOGIN, repo_name, True))
327 % (self.TEST_USER_LOGIN, repo_name, True))
328 self._compare_ok(id_, expected, given=response.body)
328 self._compare_ok(id_, expected, given=response.body)
329 finally:
329 finally:
330 destroy_repo(repo_name)
330 destroy_repo(repo_name)
331
331
332 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
332 def test_api_lock_repo_lock_aquire_non_admin_with_userid(self):
333 repo_name = 'api_delete_me'
333 repo_name = 'api_delete_me'
334 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
334 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
335 try:
335 try:
336 id_, params = _build_data(self.apikey_regular, 'lock',
336 id_, params = _build_data(self.apikey_regular, 'lock',
337 userid=TEST_USER_ADMIN_LOGIN,
337 userid=TEST_USER_ADMIN_LOGIN,
338 repoid=repo_name,
338 repoid=repo_name,
339 locked=True)
339 locked=True)
340 response = api_call(self, params)
340 response = api_call(self, params)
341 expected = 'userid is not the same as your user'
341 expected = 'userid is not the same as your user'
342 self._compare_error(id_, expected, given=response.body)
342 self._compare_error(id_, expected, given=response.body)
343 finally:
343 finally:
344 destroy_repo(repo_name)
344 destroy_repo(repo_name)
345
345
346 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
346 def test_api_lock_repo_lock_aquire_non_admin_not_his_repo(self):
347 id_, params = _build_data(self.apikey_regular, 'lock',
347 id_, params = _build_data(self.apikey_regular, 'lock',
348 repoid=self.REPO,
348 repoid=self.REPO,
349 locked=True)
349 locked=True)
350 response = api_call(self, params)
350 response = api_call(self, params)
351 expected = 'repository `%s` does not exist' % (self.REPO)
351 expected = 'repository `%s` does not exist' % (self.REPO)
352 self._compare_error(id_, expected, given=response.body)
352 self._compare_error(id_, expected, given=response.body)
353
353
354 def test_api_lock_repo_lock_release(self):
354 def test_api_lock_repo_lock_release(self):
355 id_, params = _build_data(self.apikey, 'lock',
355 id_, params = _build_data(self.apikey, 'lock',
356 userid=TEST_USER_ADMIN_LOGIN,
356 userid=TEST_USER_ADMIN_LOGIN,
357 repoid=self.REPO,
357 repoid=self.REPO,
358 locked=False)
358 locked=False)
359 response = api_call(self, params)
359 response = api_call(self, params)
360 expected = ('User `%s` set lock state for repo `%s` to `%s`'
360 expected = ('User `%s` set lock state for repo `%s` to `%s`'
361 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
361 % (TEST_USER_ADMIN_LOGIN, self.REPO, False))
362 self._compare_ok(id_, expected, given=response.body)
362 self._compare_ok(id_, expected, given=response.body)
363
363
364 def test_api_lock_repo_lock_aquire_optional_userid(self):
364 def test_api_lock_repo_lock_aquire_optional_userid(self):
365 id_, params = _build_data(self.apikey, 'lock',
365 id_, params = _build_data(self.apikey, 'lock',
366 repoid=self.REPO,
366 repoid=self.REPO,
367 locked=True)
367 locked=True)
368 response = api_call(self, params)
368 response = api_call(self, params)
369 expected = ('User `%s` set lock state for repo `%s` to `%s`'
369 expected = ('User `%s` set lock state for repo `%s` to `%s`'
370 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
370 % (TEST_USER_ADMIN_LOGIN, self.REPO, True))
371 self._compare_ok(id_, expected, given=response.body)
371 self._compare_ok(id_, expected, given=response.body)
372
372
373 @mock.patch.object(Repository, 'lock', crash)
373 @mock.patch.object(Repository, 'lock', crash)
374 def test_api_lock_error(self):
374 def test_api_lock_error(self):
375 id_, params = _build_data(self.apikey, 'lock',
375 id_, params = _build_data(self.apikey, 'lock',
376 userid=TEST_USER_ADMIN_LOGIN,
376 userid=TEST_USER_ADMIN_LOGIN,
377 repoid=self.REPO,
377 repoid=self.REPO,
378 locked=True)
378 locked=True)
379 response = api_call(self, params)
379 response = api_call(self, params)
380
380
381 expected = 'Error occurred locking repository `%s`' % self.REPO
381 expected = 'Error occurred locking repository `%s`' % self.REPO
382 self._compare_error(id_, expected, given=response.body)
382 self._compare_error(id_, expected, given=response.body)
383
383
384 def test_api_create_existing_user(self):
384 def test_api_create_existing_user(self):
385 id_, params = _build_data(self.apikey, 'create_user',
385 id_, params = _build_data(self.apikey, 'create_user',
386 username=TEST_USER_ADMIN_LOGIN,
386 username=TEST_USER_ADMIN_LOGIN,
387 email='test@foo.com',
387 email='test@foo.com',
388 password='trololo')
388 password='trololo')
389 response = api_call(self, params)
389 response = api_call(self, params)
390
390
391 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
391 expected = "user `%s` already exist" % TEST_USER_ADMIN_LOGIN
392 self._compare_error(id_, expected, given=response.body)
392 self._compare_error(id_, expected, given=response.body)
393
393
394 def test_api_create_user_with_existing_email(self):
394 def test_api_create_user_with_existing_email(self):
395 id_, params = _build_data(self.apikey, 'create_user',
395 id_, params = _build_data(self.apikey, 'create_user',
396 username=TEST_USER_ADMIN_LOGIN + 'new',
396 username=TEST_USER_ADMIN_LOGIN + 'new',
397 email=TEST_USER_REGULAR_EMAIL,
397 email=TEST_USER_REGULAR_EMAIL,
398 password='trololo')
398 password='trololo')
399 response = api_call(self, params)
399 response = api_call(self, params)
400
400
401 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
401 expected = "email `%s` already exist" % TEST_USER_REGULAR_EMAIL
402 self._compare_error(id_, expected, given=response.body)
402 self._compare_error(id_, expected, given=response.body)
403
403
404 def test_api_create_user(self):
404 def test_api_create_user(self):
405 username = 'test_new_api_user'
405 username = 'test_new_api_user'
406 email = username + "@foo.com"
406 email = username + "@foo.com"
407
407
408 id_, params = _build_data(self.apikey, 'create_user',
408 id_, params = _build_data(self.apikey, 'create_user',
409 username=username,
409 username=username,
410 email=email,
410 email=email,
411 password='trololo')
411 password='trololo')
412 response = api_call(self, params)
412 response = api_call(self, params)
413
413
414 usr = UserModel().get_by_username(username)
414 usr = UserModel().get_by_username(username)
415 ret = dict(
415 ret = dict(
416 msg='created new user `%s`' % username,
416 msg='created new user `%s`' % username,
417 user=jsonify(usr.get_api_data())
417 user=jsonify(usr.get_api_data())
418 )
418 )
419
419
420 expected = ret
420 expected = ret
421 self._compare_ok(id_, expected, given=response.body)
421 self._compare_ok(id_, expected, given=response.body)
422
422
423 UserModel().delete(usr.user_id)
423 UserModel().delete(usr.user_id)
424 Session().commit()
424 Session().commit()
425
425
426 @mock.patch.object(UserModel, 'create_or_update', crash)
426 @mock.patch.object(UserModel, 'create_or_update', crash)
427 def test_api_create_user_when_exception_happened(self):
427 def test_api_create_user_when_exception_happened(self):
428
428
429 username = 'test_new_api_user'
429 username = 'test_new_api_user'
430 email = username + "@foo.com"
430 email = username + "@foo.com"
431
431
432 id_, params = _build_data(self.apikey, 'create_user',
432 id_, params = _build_data(self.apikey, 'create_user',
433 username=username,
433 username=username,
434 email=email,
434 email=email,
435 password='trololo')
435 password='trololo')
436 response = api_call(self, params)
436 response = api_call(self, params)
437 expected = 'failed to create user `%s`' % username
437 expected = 'failed to create user `%s`' % username
438 self._compare_error(id_, expected, given=response.body)
438 self._compare_error(id_, expected, given=response.body)
439
439
440 def test_api_delete_user(self):
440 def test_api_delete_user(self):
441 usr = UserModel().create_or_update(username=u'test_user',
441 usr = UserModel().create_or_update(username=u'test_user',
442 password=u'qweqwe',
442 password=u'qweqwe',
443 email=u'u232@rhodecode.org',
443 email=u'u232@rhodecode.org',
444 firstname=u'u1', lastname=u'u1')
444 firstname=u'u1', lastname=u'u1')
445 Session().commit()
445 Session().commit()
446 username = usr.username
446 username = usr.username
447 email = usr.email
447 email = usr.email
448 usr_id = usr.user_id
448 usr_id = usr.user_id
449 ## DELETE THIS USER NOW
449 ## DELETE THIS USER NOW
450
450
451 id_, params = _build_data(self.apikey, 'delete_user',
451 id_, params = _build_data(self.apikey, 'delete_user',
452 userid=username,)
452 userid=username,)
453 response = api_call(self, params)
453 response = api_call(self, params)
454
454
455 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
455 ret = {'msg': 'deleted user ID:%s %s' % (usr_id, username),
456 'user': None}
456 'user': None}
457 expected = ret
457 expected = ret
458 self._compare_ok(id_, expected, given=response.body)
458 self._compare_ok(id_, expected, given=response.body)
459
459
460 @mock.patch.object(UserModel, 'delete', crash)
460 @mock.patch.object(UserModel, 'delete', crash)
461 def test_api_delete_user_when_exception_happened(self):
461 def test_api_delete_user_when_exception_happened(self):
462 usr = UserModel().create_or_update(username=u'test_user',
462 usr = UserModel().create_or_update(username=u'test_user',
463 password=u'qweqwe',
463 password=u'qweqwe',
464 email=u'u232@rhodecode.org',
464 email=u'u232@rhodecode.org',
465 firstname=u'u1', lastname=u'u1')
465 firstname=u'u1', lastname=u'u1')
466 Session().commit()
466 Session().commit()
467 username = usr.username
467 username = usr.username
468
468
469 id_, params = _build_data(self.apikey, 'delete_user',
469 id_, params = _build_data(self.apikey, 'delete_user',
470 userid=username,)
470 userid=username,)
471 response = api_call(self, params)
471 response = api_call(self, params)
472 ret = 'failed to delete ID:%s %s' % (usr.user_id,
472 ret = 'failed to delete ID:%s %s' % (usr.user_id,
473 usr.username)
473 usr.username)
474 expected = ret
474 expected = ret
475 self._compare_error(id_, expected, given=response.body)
475 self._compare_error(id_, expected, given=response.body)
476
476
477 @parameterized.expand([('firstname', 'new_username'),
477 @parameterized.expand([('firstname', 'new_username'),
478 ('lastname', 'new_username'),
478 ('lastname', 'new_username'),
479 ('email', 'new_username'),
479 ('email', 'new_username'),
480 ('admin', True),
480 ('admin', True),
481 ('admin', False),
481 ('admin', False),
482 ('ldap_dn', 'test'),
482 ('ldap_dn', 'test'),
483 ('ldap_dn', None),
483 ('ldap_dn', None),
484 ('active', False),
484 ('active', False),
485 ('active', True),
485 ('active', True),
486 ('password', 'newpass')
486 ('password', 'newpass')
487 ])
487 ])
488 def test_api_update_user(self, name, expected):
488 def test_api_update_user(self, name, expected):
489 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
489 usr = UserModel().get_by_username(self.TEST_USER_LOGIN)
490 kw = {name: expected,
490 kw = {name: expected,
491 'userid': usr.user_id}
491 'userid': usr.user_id}
492 id_, params = _build_data(self.apikey, 'update_user', **kw)
492 id_, params = _build_data(self.apikey, 'update_user', **kw)
493 response = api_call(self, params)
493 response = api_call(self, params)
494
494
495 ret = {
495 ret = {
496 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
496 'msg': 'updated user ID:%s %s' % (usr.user_id, self.TEST_USER_LOGIN),
497 'user': jsonify(UserModel()\
497 'user': jsonify(UserModel()\
498 .get_by_username(self.TEST_USER_LOGIN)\
498 .get_by_username(self.TEST_USER_LOGIN)\
499 .get_api_data())
499 .get_api_data())
500 }
500 }
501
501
502 expected = ret
502 expected = ret
503 self._compare_ok(id_, expected, given=response.body)
503 self._compare_ok(id_, expected, given=response.body)
504
504
505 def test_api_update_user_no_changed_params(self):
505 def test_api_update_user_no_changed_params(self):
506 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
506 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
507 ret = jsonify(usr.get_api_data())
507 ret = jsonify(usr.get_api_data())
508 id_, params = _build_data(self.apikey, 'update_user',
508 id_, params = _build_data(self.apikey, 'update_user',
509 userid=TEST_USER_ADMIN_LOGIN)
509 userid=TEST_USER_ADMIN_LOGIN)
510
510
511 response = api_call(self, params)
511 response = api_call(self, params)
512 ret = {
512 ret = {
513 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
513 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
514 'user': ret
514 'user': ret
515 }
515 }
516 expected = ret
516 expected = ret
517 self._compare_ok(id_, expected, given=response.body)
517 self._compare_ok(id_, expected, given=response.body)
518
518
519 def test_api_update_user_by_user_id(self):
519 def test_api_update_user_by_user_id(self):
520 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
520 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
521 ret = jsonify(usr.get_api_data())
521 ret = jsonify(usr.get_api_data())
522 id_, params = _build_data(self.apikey, 'update_user',
522 id_, params = _build_data(self.apikey, 'update_user',
523 userid=usr.user_id)
523 userid=usr.user_id)
524
524
525 response = api_call(self, params)
525 response = api_call(self, params)
526 ret = {
526 ret = {
527 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
527 'msg': 'updated user ID:%s %s' % (usr.user_id, TEST_USER_ADMIN_LOGIN),
528 'user': ret
528 'user': ret
529 }
529 }
530 expected = ret
530 expected = ret
531 self._compare_ok(id_, expected, given=response.body)
531 self._compare_ok(id_, expected, given=response.body)
532
532
533 @mock.patch.object(UserModel, 'update_user', crash)
533 @mock.patch.object(UserModel, 'update_user', crash)
534 def test_api_update_user_when_exception_happens(self):
534 def test_api_update_user_when_exception_happens(self):
535 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
535 usr = UserModel().get_by_username(TEST_USER_ADMIN_LOGIN)
536 ret = jsonify(usr.get_api_data())
536 ret = jsonify(usr.get_api_data())
537 id_, params = _build_data(self.apikey, 'update_user',
537 id_, params = _build_data(self.apikey, 'update_user',
538 userid=usr.user_id)
538 userid=usr.user_id)
539
539
540 response = api_call(self, params)
540 response = api_call(self, params)
541 ret = 'failed to update user `%s`' % usr.user_id
541 ret = 'failed to update user `%s`' % usr.user_id
542
542
543 expected = ret
543 expected = ret
544 self._compare_error(id_, expected, given=response.body)
544 self._compare_error(id_, expected, given=response.body)
545
545
546 def test_api_get_repo(self):
546 def test_api_get_repo(self):
547 new_group = 'some_new_group'
547 new_group = 'some_new_group'
548 make_users_group(new_group)
548 make_users_group(new_group)
549 RepoModel().grant_users_group_permission(repo=self.REPO,
549 RepoModel().grant_users_group_permission(repo=self.REPO,
550 group_name=new_group,
550 group_name=new_group,
551 perm='repository.read')
551 perm='repository.read')
552 Session().commit()
552 Session().commit()
553 id_, params = _build_data(self.apikey, 'get_repo',
553 id_, params = _build_data(self.apikey, 'get_repo',
554 repoid=self.REPO)
554 repoid=self.REPO)
555 response = api_call(self, params)
555 response = api_call(self, params)
556
556
557 repo = RepoModel().get_by_repo_name(self.REPO)
557 repo = RepoModel().get_by_repo_name(self.REPO)
558 ret = repo.get_api_data()
558 ret = repo.get_api_data()
559
559
560 members = []
560 members = []
561 followers = []
561 followers = []
562 for user in repo.repo_to_perm:
562 for user in repo.repo_to_perm:
563 perm = user.permission.permission_name
563 perm = user.permission.permission_name
564 user = user.user
564 user = user.user
565 user_data = user.get_api_data()
565 user_data = user.get_api_data()
566 user_data['type'] = "user"
566 user_data['type'] = "user"
567 user_data['permission'] = perm
567 user_data['permission'] = perm
568 members.append(user_data)
568 members.append(user_data)
569
569
570 for users_group in repo.users_group_to_perm:
570 for users_group in repo.users_group_to_perm:
571 perm = users_group.permission.permission_name
571 perm = users_group.permission.permission_name
572 users_group = users_group.users_group
572 users_group = users_group.users_group
573 users_group_data = users_group.get_api_data()
573 users_group_data = users_group.get_api_data()
574 users_group_data['type'] = "users_group"
574 users_group_data['type'] = "users_group"
575 users_group_data['permission'] = perm
575 users_group_data['permission'] = perm
576 members.append(users_group_data)
576 members.append(users_group_data)
577
577
578 for user in repo.followers:
578 for user in repo.followers:
579 followers.append(user.user.get_api_data())
579 followers.append(user.user.get_api_data())
580
580
581 ret['members'] = members
581 ret['members'] = members
582 ret['followers'] = followers
582 ret['followers'] = followers
583
583
584 expected = ret
584 expected = ret
585 self._compare_ok(id_, expected, given=response.body)
585 self._compare_ok(id_, expected, given=response.body)
586 destroy_users_group(new_group)
586 destroy_users_group(new_group)
587
587
588 def test_api_get_repo_by_non_admin(self):
588 def test_api_get_repo_by_non_admin(self):
589 id_, params = _build_data(self.apikey, 'get_repo',
589 id_, params = _build_data(self.apikey, 'get_repo',
590 repoid=self.REPO)
590 repoid=self.REPO)
591 response = api_call(self, params)
591 response = api_call(self, params)
592
592
593 repo = RepoModel().get_by_repo_name(self.REPO)
593 repo = RepoModel().get_by_repo_name(self.REPO)
594 ret = repo.get_api_data()
594 ret = repo.get_api_data()
595
595
596 members = []
596 members = []
597 followers = []
597 followers = []
598 for user in repo.repo_to_perm:
598 for user in repo.repo_to_perm:
599 perm = user.permission.permission_name
599 perm = user.permission.permission_name
600 user = user.user
600 user = user.user
601 user_data = user.get_api_data()
601 user_data = user.get_api_data()
602 user_data['type'] = "user"
602 user_data['type'] = "user"
603 user_data['permission'] = perm
603 user_data['permission'] = perm
604 members.append(user_data)
604 members.append(user_data)
605
605
606 for users_group in repo.users_group_to_perm:
606 for users_group in repo.users_group_to_perm:
607 perm = users_group.permission.permission_name
607 perm = users_group.permission.permission_name
608 users_group = users_group.users_group
608 users_group = users_group.users_group
609 users_group_data = users_group.get_api_data()
609 users_group_data = users_group.get_api_data()
610 users_group_data['type'] = "users_group"
610 users_group_data['type'] = "users_group"
611 users_group_data['permission'] = perm
611 users_group_data['permission'] = perm
612 members.append(users_group_data)
612 members.append(users_group_data)
613
613
614 for user in repo.followers:
614 for user in repo.followers:
615 followers.append(user.user.get_api_data())
615 followers.append(user.user.get_api_data())
616
616
617 ret['members'] = members
617 ret['members'] = members
618 ret['followers'] = followers
618 ret['followers'] = followers
619
619
620 expected = ret
620 expected = ret
621 self._compare_ok(id_, expected, given=response.body)
621 self._compare_ok(id_, expected, given=response.body)
622
622
623 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
623 def test_api_get_repo_by_non_admin_no_permission_to_repo(self):
624 RepoModel().grant_user_permission(repo=self.REPO,
624 RepoModel().grant_user_permission(repo=self.REPO,
625 user=self.TEST_USER_LOGIN,
625 user=self.TEST_USER_LOGIN,
626 perm='repository.none')
626 perm='repository.none')
627
627
628 id_, params = _build_data(self.apikey_regular, 'get_repo',
628 id_, params = _build_data(self.apikey_regular, 'get_repo',
629 repoid=self.REPO)
629 repoid=self.REPO)
630 response = api_call(self, params)
630 response = api_call(self, params)
631
631
632 expected = 'repository `%s` does not exist' % (self.REPO)
632 expected = 'repository `%s` does not exist' % (self.REPO)
633 self._compare_error(id_, expected, given=response.body)
633 self._compare_error(id_, expected, given=response.body)
634
634
635 def test_api_get_repo_that_doesn_not_exist(self):
635 def test_api_get_repo_that_doesn_not_exist(self):
636 id_, params = _build_data(self.apikey, 'get_repo',
636 id_, params = _build_data(self.apikey, 'get_repo',
637 repoid='no-such-repo')
637 repoid='no-such-repo')
638 response = api_call(self, params)
638 response = api_call(self, params)
639
639
640 ret = 'repository `%s` does not exist' % 'no-such-repo'
640 ret = 'repository `%s` does not exist' % 'no-such-repo'
641 expected = ret
641 expected = ret
642 self._compare_error(id_, expected, given=response.body)
642 self._compare_error(id_, expected, given=response.body)
643
643
644 def test_api_get_repos(self):
644 def test_api_get_repos(self):
645 id_, params = _build_data(self.apikey, 'get_repos')
645 id_, params = _build_data(self.apikey, 'get_repos')
646 response = api_call(self, params)
646 response = api_call(self, params)
647
647
648 result = []
648 result = []
649 for repo in RepoModel().get_all():
649 for repo in RepoModel().get_all():
650 result.append(repo.get_api_data())
650 result.append(repo.get_api_data())
651 ret = jsonify(result)
651 ret = jsonify(result)
652
652
653 expected = ret
653 expected = ret
654 self._compare_ok(id_, expected, given=response.body)
654 self._compare_ok(id_, expected, given=response.body)
655
655
656 def test_api_get_repos_non_admin(self):
656 def test_api_get_repos_non_admin(self):
657 id_, params = _build_data(self.apikey_regular, 'get_repos')
657 id_, params = _build_data(self.apikey_regular, 'get_repos')
658 response = api_call(self, params)
658 response = api_call(self, params)
659
659
660 result = []
660 result = []
661 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
661 for repo in RepoModel().get_all_user_repos(self.TEST_USER_LOGIN):
662 result.append(repo.get_api_data())
662 result.append(repo.get_api_data())
663 ret = jsonify(result)
663 ret = jsonify(result)
664
664
665 expected = ret
665 expected = ret
666 self._compare_ok(id_, expected, given=response.body)
666 self._compare_ok(id_, expected, given=response.body)
667
667
668 @parameterized.expand([('all', 'all'),
668 @parameterized.expand([('all', 'all'),
669 ('dirs', 'dirs'),
669 ('dirs', 'dirs'),
670 ('files', 'files'), ])
670 ('files', 'files'), ])
671 def test_api_get_repo_nodes(self, name, ret_type):
671 def test_api_get_repo_nodes(self, name, ret_type):
672 rev = 'tip'
672 rev = 'tip'
673 path = '/'
673 path = '/'
674 id_, params = _build_data(self.apikey, 'get_repo_nodes',
674 id_, params = _build_data(self.apikey, 'get_repo_nodes',
675 repoid=self.REPO, revision=rev,
675 repoid=self.REPO, revision=rev,
676 root_path=path,
676 root_path=path,
677 ret_type=ret_type)
677 ret_type=ret_type)
678 response = api_call(self, params)
678 response = api_call(self, params)
679
679
680 # we don't the actual return types here since it's tested somewhere
680 # we don't the actual return types here since it's tested somewhere
681 # else
681 # else
682 expected = json.loads(response.body)['result']
682 expected = json.loads(response.body)['result']
683 self._compare_ok(id_, expected, given=response.body)
683 self._compare_ok(id_, expected, given=response.body)
684
684
685 def test_api_get_repo_nodes_bad_revisions(self):
685 def test_api_get_repo_nodes_bad_revisions(self):
686 rev = 'i-dont-exist'
686 rev = 'i-dont-exist'
687 path = '/'
687 path = '/'
688 id_, params = _build_data(self.apikey, 'get_repo_nodes',
688 id_, params = _build_data(self.apikey, 'get_repo_nodes',
689 repoid=self.REPO, revision=rev,
689 repoid=self.REPO, revision=rev,
690 root_path=path,)
690 root_path=path,)
691 response = api_call(self, params)
691 response = api_call(self, params)
692
692
693 expected = 'failed to get repo: `%s` nodes' % self.REPO
693 expected = 'failed to get repo: `%s` nodes' % self.REPO
694 self._compare_error(id_, expected, given=response.body)
694 self._compare_error(id_, expected, given=response.body)
695
695
696 def test_api_get_repo_nodes_bad_path(self):
696 def test_api_get_repo_nodes_bad_path(self):
697 rev = 'tip'
697 rev = 'tip'
698 path = '/idontexits'
698 path = '/idontexits'
699 id_, params = _build_data(self.apikey, 'get_repo_nodes',
699 id_, params = _build_data(self.apikey, 'get_repo_nodes',
700 repoid=self.REPO, revision=rev,
700 repoid=self.REPO, revision=rev,
701 root_path=path,)
701 root_path=path,)
702 response = api_call(self, params)
702 response = api_call(self, params)
703
703
704 expected = 'failed to get repo: `%s` nodes' % self.REPO
704 expected = 'failed to get repo: `%s` nodes' % self.REPO
705 self._compare_error(id_, expected, given=response.body)
705 self._compare_error(id_, expected, given=response.body)
706
706
707 def test_api_get_repo_nodes_bad_ret_type(self):
707 def test_api_get_repo_nodes_bad_ret_type(self):
708 rev = 'tip'
708 rev = 'tip'
709 path = '/'
709 path = '/'
710 ret_type = 'error'
710 ret_type = 'error'
711 id_, params = _build_data(self.apikey, 'get_repo_nodes',
711 id_, params = _build_data(self.apikey, 'get_repo_nodes',
712 repoid=self.REPO, revision=rev,
712 repoid=self.REPO, revision=rev,
713 root_path=path,
713 root_path=path,
714 ret_type=ret_type)
714 ret_type=ret_type)
715 response = api_call(self, params)
715 response = api_call(self, params)
716
716
717 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
717 expected = 'ret_type must be one of %s' % (['files', 'dirs', 'all'])
718 self._compare_error(id_, expected, given=response.body)
718 self._compare_error(id_, expected, given=response.body)
719
719
720 def test_api_create_repo(self):
720 def test_api_create_repo(self):
721 repo_name = 'api-repo'
721 repo_name = 'api-repo'
722 id_, params = _build_data(self.apikey, 'create_repo',
722 id_, params = _build_data(self.apikey, 'create_repo',
723 repo_name=repo_name,
723 repo_name=repo_name,
724 owner=TEST_USER_ADMIN_LOGIN,
724 owner=TEST_USER_ADMIN_LOGIN,
725 repo_type='hg',
725 repo_type='hg',
726 )
726 )
727 response = api_call(self, params)
727 response = api_call(self, params)
728
728
729 repo = RepoModel().get_by_repo_name(repo_name)
729 repo = RepoModel().get_by_repo_name(repo_name)
730 ret = {
730 ret = {
731 'msg': 'Created new repository `%s`' % repo_name,
731 'msg': 'Created new repository `%s`' % repo_name,
732 'repo': jsonify(repo.get_api_data())
732 'repo': jsonify(repo.get_api_data())
733 }
733 }
734 expected = ret
734 expected = ret
735 self._compare_ok(id_, expected, given=response.body)
735 self._compare_ok(id_, expected, given=response.body)
736 destroy_repo(repo_name)
736 destroy_repo(repo_name)
737
737
738 def test_api_create_repo_unknown_owner(self):
738 def test_api_create_repo_unknown_owner(self):
739 repo_name = 'api-repo'
739 repo_name = 'api-repo'
740 owner = 'i-dont-exist'
740 owner = 'i-dont-exist'
741 id_, params = _build_data(self.apikey, 'create_repo',
741 id_, params = _build_data(self.apikey, 'create_repo',
742 repo_name=repo_name,
742 repo_name=repo_name,
743 owner=owner,
743 owner=owner,
744 repo_type='hg',
744 repo_type='hg',
745 )
745 )
746 response = api_call(self, params)
746 response = api_call(self, params)
747 expected = 'user `%s` does not exist' % owner
747 expected = 'user `%s` does not exist' % owner
748 self._compare_error(id_, expected, given=response.body)
748 self._compare_error(id_, expected, given=response.body)
749
749
750 def test_api_create_repo_dont_specify_owner(self):
750 def test_api_create_repo_dont_specify_owner(self):
751 repo_name = 'api-repo'
751 repo_name = 'api-repo'
752 owner = 'i-dont-exist'
752 owner = 'i-dont-exist'
753 id_, params = _build_data(self.apikey, 'create_repo',
753 id_, params = _build_data(self.apikey, 'create_repo',
754 repo_name=repo_name,
754 repo_name=repo_name,
755 repo_type='hg',
755 repo_type='hg',
756 )
756 )
757 response = api_call(self, params)
757 response = api_call(self, params)
758
758
759 repo = RepoModel().get_by_repo_name(repo_name)
759 repo = RepoModel().get_by_repo_name(repo_name)
760 ret = {
760 ret = {
761 'msg': 'Created new repository `%s`' % repo_name,
761 'msg': 'Created new repository `%s`' % repo_name,
762 'repo': jsonify(repo.get_api_data())
762 'repo': jsonify(repo.get_api_data())
763 }
763 }
764 expected = ret
764 expected = ret
765 self._compare_ok(id_, expected, given=response.body)
765 self._compare_ok(id_, expected, given=response.body)
766 destroy_repo(repo_name)
766 destroy_repo(repo_name)
767
767
768 def test_api_create_repo_by_non_admin(self):
768 def test_api_create_repo_by_non_admin(self):
769 repo_name = 'api-repo'
769 repo_name = 'api-repo'
770 owner = 'i-dont-exist'
770 owner = 'i-dont-exist'
771 id_, params = _build_data(self.apikey_regular, 'create_repo',
771 id_, params = _build_data(self.apikey_regular, 'create_repo',
772 repo_name=repo_name,
772 repo_name=repo_name,
773 repo_type='hg',
773 repo_type='hg',
774 )
774 )
775 response = api_call(self, params)
775 response = api_call(self, params)
776
776
777 repo = RepoModel().get_by_repo_name(repo_name)
777 repo = RepoModel().get_by_repo_name(repo_name)
778 ret = {
778 ret = {
779 'msg': 'Created new repository `%s`' % repo_name,
779 'msg': 'Created new repository `%s`' % repo_name,
780 'repo': jsonify(repo.get_api_data())
780 'repo': jsonify(repo.get_api_data())
781 }
781 }
782 expected = ret
782 expected = ret
783 self._compare_ok(id_, expected, given=response.body)
783 self._compare_ok(id_, expected, given=response.body)
784 destroy_repo(repo_name)
784 destroy_repo(repo_name)
785
785
786 def test_api_create_repo_by_non_admin_specify_owner(self):
786 def test_api_create_repo_by_non_admin_specify_owner(self):
787 repo_name = 'api-repo'
787 repo_name = 'api-repo'
788 owner = 'i-dont-exist'
788 owner = 'i-dont-exist'
789 id_, params = _build_data(self.apikey_regular, 'create_repo',
789 id_, params = _build_data(self.apikey_regular, 'create_repo',
790 repo_name=repo_name,
790 repo_name=repo_name,
791 repo_type='hg',
791 repo_type='hg',
792 owner=owner
792 owner=owner
793 )
793 )
794 response = api_call(self, params)
794 response = api_call(self, params)
795
795
796 expected = 'Only RhodeCode admin can specify `owner` param'
796 expected = 'Only RhodeCode admin can specify `owner` param'
797 self._compare_error(id_, expected, given=response.body)
797 self._compare_error(id_, expected, given=response.body)
798 destroy_repo(repo_name)
798 destroy_repo(repo_name)
799
799
800 def test_api_create_repo_exists(self):
800 def test_api_create_repo_exists(self):
801 repo_name = self.REPO
801 repo_name = self.REPO
802 id_, params = _build_data(self.apikey, 'create_repo',
802 id_, params = _build_data(self.apikey, 'create_repo',
803 repo_name=repo_name,
803 repo_name=repo_name,
804 owner=TEST_USER_ADMIN_LOGIN,
804 owner=TEST_USER_ADMIN_LOGIN,
805 repo_type='hg',
805 repo_type='hg',
806 )
806 )
807 response = api_call(self, params)
807 response = api_call(self, params)
808 expected = "repo `%s` already exist" % repo_name
808 expected = "repo `%s` already exist" % repo_name
809 self._compare_error(id_, expected, given=response.body)
809 self._compare_error(id_, expected, given=response.body)
810
810
811 @mock.patch.object(RepoModel, 'create_repo', crash)
811 @mock.patch.object(RepoModel, 'create_repo', crash)
812 def test_api_create_repo_exception_occurred(self):
812 def test_api_create_repo_exception_occurred(self):
813 repo_name = 'api-repo'
813 repo_name = 'api-repo'
814 id_, params = _build_data(self.apikey, 'create_repo',
814 id_, params = _build_data(self.apikey, 'create_repo',
815 repo_name=repo_name,
815 repo_name=repo_name,
816 owner=TEST_USER_ADMIN_LOGIN,
816 owner=TEST_USER_ADMIN_LOGIN,
817 repo_type='hg',
817 repo_type='hg',
818 )
818 )
819 response = api_call(self, params)
819 response = api_call(self, params)
820 expected = 'failed to create repository `%s`' % repo_name
820 expected = 'failed to create repository `%s`' % repo_name
821 self._compare_error(id_, expected, given=response.body)
821 self._compare_error(id_, expected, given=response.body)
822
822
823 def test_api_delete_repo(self):
823 def test_api_delete_repo(self):
824 repo_name = 'api_delete_me'
824 repo_name = 'api_delete_me'
825 create_repo(repo_name, self.REPO_TYPE)
825 create_repo(repo_name, self.REPO_TYPE)
826
826
827 id_, params = _build_data(self.apikey, 'delete_repo',
827 id_, params = _build_data(self.apikey, 'delete_repo',
828 repoid=repo_name,)
828 repoid=repo_name,)
829 response = api_call(self, params)
829 response = api_call(self, params)
830
830
831 ret = {
831 ret = {
832 'msg': 'Deleted repository `%s`' % repo_name,
832 'msg': 'Deleted repository `%s`' % repo_name,
833 'success': True
833 'success': True
834 }
834 }
835 expected = ret
835 expected = ret
836 self._compare_ok(id_, expected, given=response.body)
836 self._compare_ok(id_, expected, given=response.body)
837
837
838 def test_api_delete_repo_by_non_admin(self):
838 def test_api_delete_repo_by_non_admin(self):
839 repo_name = 'api_delete_me'
839 repo_name = 'api_delete_me'
840 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
840 create_repo(repo_name, self.REPO_TYPE, owner=self.TEST_USER_LOGIN)
841 try:
841 try:
842 id_, params = _build_data(self.apikey_regular, 'delete_repo',
842 id_, params = _build_data(self.apikey_regular, 'delete_repo',
843 repoid=repo_name,)
843 repoid=repo_name,)
844 response = api_call(self, params)
844 response = api_call(self, params)
845
845
846 ret = {
846 ret = {
847 'msg': 'Deleted repository `%s`' % repo_name,
847 'msg': 'Deleted repository `%s`' % repo_name,
848 'success': True
848 'success': True
849 }
849 }
850 expected = ret
850 expected = ret
851 self._compare_ok(id_, expected, given=response.body)
851 self._compare_ok(id_, expected, given=response.body)
852 finally:
852 finally:
853 destroy_repo(repo_name)
853 destroy_repo(repo_name)
854
854
855 def test_api_delete_repo_by_non_admin_no_permission(self):
855 def test_api_delete_repo_by_non_admin_no_permission(self):
856 repo_name = 'api_delete_me'
856 repo_name = 'api_delete_me'
857 create_repo(repo_name, self.REPO_TYPE)
857 create_repo(repo_name, self.REPO_TYPE)
858 try:
858 try:
859 id_, params = _build_data(self.apikey_regular, 'delete_repo',
859 id_, params = _build_data(self.apikey_regular, 'delete_repo',
860 repoid=repo_name,)
860 repoid=repo_name,)
861 response = api_call(self, params)
861 response = api_call(self, params)
862 expected = 'repository `%s` does not exist' % (repo_name)
862 expected = 'repository `%s` does not exist' % (repo_name)
863 self._compare_error(id_, expected, given=response.body)
863 self._compare_error(id_, expected, given=response.body)
864 finally:
864 finally:
865 destroy_repo(repo_name)
865 destroy_repo(repo_name)
866
866
867 def test_api_delete_repo_exception_occurred(self):
867 def test_api_delete_repo_exception_occurred(self):
868 repo_name = 'api_delete_me'
868 repo_name = 'api_delete_me'
869 create_repo(repo_name, self.REPO_TYPE)
869 create_repo(repo_name, self.REPO_TYPE)
870 try:
870 try:
871 with mock.patch.object(RepoModel, 'delete', crash):
871 with mock.patch.object(RepoModel, 'delete', crash):
872 id_, params = _build_data(self.apikey, 'delete_repo',
872 id_, params = _build_data(self.apikey, 'delete_repo',
873 repoid=repo_name,)
873 repoid=repo_name,)
874 response = api_call(self, params)
874 response = api_call(self, params)
875
875
876 expected = 'failed to delete repository `%s`' % repo_name
876 expected = 'failed to delete repository `%s`' % repo_name
877 self._compare_error(id_, expected, given=response.body)
877 self._compare_error(id_, expected, given=response.body)
878 finally:
878 finally:
879 destroy_repo(repo_name)
879 destroy_repo(repo_name)
880
880
881 def test_api_fork_repo(self):
881 def test_api_fork_repo(self):
882 fork_name = 'api-repo-fork'
882 fork_name = 'api-repo-fork'
883 id_, params = _build_data(self.apikey, 'fork_repo',
883 id_, params = _build_data(self.apikey, 'fork_repo',
884 repoid=self.REPO,
884 repoid=self.REPO,
885 fork_name=fork_name,
885 fork_name=fork_name,
886 owner=TEST_USER_ADMIN_LOGIN,
886 owner=TEST_USER_ADMIN_LOGIN,
887 )
887 )
888 response = api_call(self, params)
888 response = api_call(self, params)
889
889
890 ret = {
890 ret = {
891 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
891 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
892 fork_name),
892 fork_name),
893 'success': True
893 'success': True
894 }
894 }
895 expected = ret
895 expected = ret
896 self._compare_ok(id_, expected, given=response.body)
896 self._compare_ok(id_, expected, given=response.body)
897 destroy_repo(fork_name)
897 destroy_repo(fork_name)
898
898
899 def test_api_fork_repo_non_admin(self):
899 def test_api_fork_repo_non_admin(self):
900 fork_name = 'api-repo-fork'
900 fork_name = 'api-repo-fork'
901 id_, params = _build_data(self.apikey_regular, 'fork_repo',
901 id_, params = _build_data(self.apikey_regular, 'fork_repo',
902 repoid=self.REPO,
902 repoid=self.REPO,
903 fork_name=fork_name,
903 fork_name=fork_name,
904 )
904 )
905 response = api_call(self, params)
905 response = api_call(self, params)
906
906
907 ret = {
907 ret = {
908 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
908 'msg': 'Created fork of `%s` as `%s`' % (self.REPO,
909 fork_name),
909 fork_name),
910 'success': True
910 'success': True
911 }
911 }
912 expected = ret
912 expected = ret
913 self._compare_ok(id_, expected, given=response.body)
913 self._compare_ok(id_, expected, given=response.body)
914 destroy_repo(fork_name)
914 destroy_repo(fork_name)
915
915
916 def test_api_fork_repo_non_admin_specify_owner(self):
916 def test_api_fork_repo_non_admin_specify_owner(self):
917 fork_name = 'api-repo-fork'
917 fork_name = 'api-repo-fork'
918 id_, params = _build_data(self.apikey_regular, 'fork_repo',
918 id_, params = _build_data(self.apikey_regular, 'fork_repo',
919 repoid=self.REPO,
919 repoid=self.REPO,
920 fork_name=fork_name,
920 fork_name=fork_name,
921 owner=TEST_USER_ADMIN_LOGIN,
921 owner=TEST_USER_ADMIN_LOGIN,
922 )
922 )
923 response = api_call(self, params)
923 response = api_call(self, params)
924 expected = 'Only RhodeCode admin can specify `owner` param'
924 expected = 'Only RhodeCode admin can specify `owner` param'
925 self._compare_error(id_, expected, given=response.body)
925 self._compare_error(id_, expected, given=response.body)
926 destroy_repo(fork_name)
926 destroy_repo(fork_name)
927
927
928 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
928 def test_api_fork_repo_non_admin_no_permission_to_fork(self):
929 RepoModel().grant_user_permission(repo=self.REPO,
929 RepoModel().grant_user_permission(repo=self.REPO,
930 user=self.TEST_USER_LOGIN,
930 user=self.TEST_USER_LOGIN,
931 perm='repository.none')
931 perm='repository.none')
932 fork_name = 'api-repo-fork'
932 fork_name = 'api-repo-fork'
933 id_, params = _build_data(self.apikey_regular, 'fork_repo',
933 id_, params = _build_data(self.apikey_regular, 'fork_repo',
934 repoid=self.REPO,
934 repoid=self.REPO,
935 fork_name=fork_name,
935 fork_name=fork_name,
936 )
936 )
937 response = api_call(self, params)
937 response = api_call(self, params)
938 expected = 'repository `%s` does not exist' % (self.REPO)
938 expected = 'repository `%s` does not exist' % (self.REPO)
939 self._compare_error(id_, expected, given=response.body)
939 self._compare_error(id_, expected, given=response.body)
940 destroy_repo(fork_name)
940 destroy_repo(fork_name)
941
941
942 def test_api_fork_repo_unknown_owner(self):
942 def test_api_fork_repo_unknown_owner(self):
943 fork_name = 'api-repo-fork'
943 fork_name = 'api-repo-fork'
944 owner = 'i-dont-exist'
944 owner = 'i-dont-exist'
945 id_, params = _build_data(self.apikey, 'fork_repo',
945 id_, params = _build_data(self.apikey, 'fork_repo',
946 repoid=self.REPO,
946 repoid=self.REPO,
947 fork_name=fork_name,
947 fork_name=fork_name,
948 owner=owner,
948 owner=owner,
949 )
949 )
950 response = api_call(self, params)
950 response = api_call(self, params)
951 expected = 'user `%s` does not exist' % owner
951 expected = 'user `%s` does not exist' % owner
952 self._compare_error(id_, expected, given=response.body)
952 self._compare_error(id_, expected, given=response.body)
953
953
954 def test_api_fork_repo_fork_exists(self):
954 def test_api_fork_repo_fork_exists(self):
955 fork_name = 'api-repo-fork'
955 fork_name = 'api-repo-fork'
956 create_fork(fork_name, self.REPO_TYPE, self.REPO)
956 create_fork(fork_name, self.REPO_TYPE, self.REPO)
957
957
958 try:
958 try:
959 fork_name = 'api-repo-fork'
959 fork_name = 'api-repo-fork'
960
960
961 id_, params = _build_data(self.apikey, 'fork_repo',
961 id_, params = _build_data(self.apikey, 'fork_repo',
962 repoid=self.REPO,
962 repoid=self.REPO,
963 fork_name=fork_name,
963 fork_name=fork_name,
964 owner=TEST_USER_ADMIN_LOGIN,
964 owner=TEST_USER_ADMIN_LOGIN,
965 )
965 )
966 response = api_call(self, params)
966 response = api_call(self, params)
967
967
968 expected = "fork `%s` already exist" % fork_name
968 expected = "fork `%s` already exist" % fork_name
969 self._compare_error(id_, expected, given=response.body)
969 self._compare_error(id_, expected, given=response.body)
970 finally:
970 finally:
971 destroy_repo(fork_name)
971 destroy_repo(fork_name)
972
972
973 def test_api_fork_repo_repo_exists(self):
973 def test_api_fork_repo_repo_exists(self):
974 fork_name = self.REPO
974 fork_name = self.REPO
975
975
976 id_, params = _build_data(self.apikey, 'fork_repo',
976 id_, params = _build_data(self.apikey, 'fork_repo',
977 repoid=self.REPO,
977 repoid=self.REPO,
978 fork_name=fork_name,
978 fork_name=fork_name,
979 owner=TEST_USER_ADMIN_LOGIN,
979 owner=TEST_USER_ADMIN_LOGIN,
980 )
980 )
981 response = api_call(self, params)
981 response = api_call(self, params)
982
982
983 expected = "repo `%s` already exist" % fork_name
983 expected = "repo `%s` already exist" % fork_name
984 self._compare_error(id_, expected, given=response.body)
984 self._compare_error(id_, expected, given=response.body)
985
985
986 @mock.patch.object(RepoModel, 'create_fork', crash)
986 @mock.patch.object(RepoModel, 'create_fork', crash)
987 def test_api_fork_repo_exception_occurred(self):
987 def test_api_fork_repo_exception_occurred(self):
988 fork_name = 'api-repo-fork'
988 fork_name = 'api-repo-fork'
989 id_, params = _build_data(self.apikey, 'fork_repo',
989 id_, params = _build_data(self.apikey, 'fork_repo',
990 repoid=self.REPO,
990 repoid=self.REPO,
991 fork_name=fork_name,
991 fork_name=fork_name,
992 owner=TEST_USER_ADMIN_LOGIN,
992 owner=TEST_USER_ADMIN_LOGIN,
993 )
993 )
994 response = api_call(self, params)
994 response = api_call(self, params)
995
995
996 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
996 expected = 'failed to fork repository `%s` as `%s`' % (self.REPO,
997 fork_name)
997 fork_name)
998 self._compare_error(id_, expected, given=response.body)
998 self._compare_error(id_, expected, given=response.body)
999
999
1000 def test_api_get_users_group(self):
1000 def test_api_get_users_group(self):
1001 id_, params = _build_data(self.apikey, 'get_users_group',
1001 id_, params = _build_data(self.apikey, 'get_users_group',
1002 usersgroupid=TEST_USERS_GROUP)
1002 usersgroupid=TEST_USERS_GROUP)
1003 response = api_call(self, params)
1003 response = api_call(self, params)
1004
1004
1005 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
1005 users_group = UsersGroupModel().get_group(TEST_USERS_GROUP)
1006 members = []
1006 members = []
1007 for user in users_group.members:
1007 for user in users_group.members:
1008 user = user.user
1008 user = user.user
1009 members.append(user.get_api_data())
1009 members.append(user.get_api_data())
1010
1010
1011 ret = users_group.get_api_data()
1011 ret = users_group.get_api_data()
1012 ret['members'] = members
1012 ret['members'] = members
1013 expected = ret
1013 expected = ret
1014 self._compare_ok(id_, expected, given=response.body)
1014 self._compare_ok(id_, expected, given=response.body)
1015
1015
1016 def test_api_get_users_groups(self):
1016 def test_api_get_users_groups(self):
1017
1017
1018 make_users_group('test_users_group2')
1018 make_users_group('test_users_group2')
1019
1019
1020 id_, params = _build_data(self.apikey, 'get_users_groups',)
1020 id_, params = _build_data(self.apikey, 'get_users_groups',)
1021 response = api_call(self, params)
1021 response = api_call(self, params)
1022
1022
1023 expected = []
1023 expected = []
1024 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
1024 for gr_name in [TEST_USERS_GROUP, 'test_users_group2']:
1025 users_group = UsersGroupModel().get_group(gr_name)
1025 users_group = UsersGroupModel().get_group(gr_name)
1026 ret = users_group.get_api_data()
1026 ret = users_group.get_api_data()
1027 expected.append(ret)
1027 expected.append(ret)
1028 self._compare_ok(id_, expected, given=response.body)
1028 self._compare_ok(id_, expected, given=response.body)
1029
1029
1030 UsersGroupModel().delete(users_group='test_users_group2')
1030 UsersGroupModel().delete(users_group='test_users_group2')
1031 Session().commit()
1031 Session().commit()
1032
1032
1033 def test_api_create_users_group(self):
1033 def test_api_create_users_group(self):
1034 group_name = 'some_new_group'
1034 group_name = 'some_new_group'
1035 id_, params = _build_data(self.apikey, 'create_users_group',
1035 id_, params = _build_data(self.apikey, 'create_users_group',
1036 group_name=group_name)
1036 group_name=group_name)
1037 response = api_call(self, params)
1037 response = api_call(self, params)
1038
1038
1039 ret = {
1039 ret = {
1040 'msg': 'created new users group `%s`' % group_name,
1040 'msg': 'created new user group `%s`' % group_name,
1041 'users_group': jsonify(UsersGroupModel()\
1041 'users_group': jsonify(UsersGroupModel()\
1042 .get_by_name(group_name)\
1042 .get_by_name(group_name)\
1043 .get_api_data())
1043 .get_api_data())
1044 }
1044 }
1045 expected = ret
1045 expected = ret
1046 self._compare_ok(id_, expected, given=response.body)
1046 self._compare_ok(id_, expected, given=response.body)
1047
1047
1048 destroy_users_group(group_name)
1048 destroy_users_group(group_name)
1049
1049
1050 def test_api_get_users_group_that_exist(self):
1050 def test_api_get_users_group_that_exist(self):
1051 id_, params = _build_data(self.apikey, 'create_users_group',
1051 id_, params = _build_data(self.apikey, 'create_users_group',
1052 group_name=TEST_USERS_GROUP)
1052 group_name=TEST_USERS_GROUP)
1053 response = api_call(self, params)
1053 response = api_call(self, params)
1054
1054
1055 expected = "users group `%s` already exist" % TEST_USERS_GROUP
1055 expected = "user group `%s` already exist" % TEST_USERS_GROUP
1056 self._compare_error(id_, expected, given=response.body)
1056 self._compare_error(id_, expected, given=response.body)
1057
1057
1058 @mock.patch.object(UsersGroupModel, 'create', crash)
1058 @mock.patch.object(UsersGroupModel, 'create', crash)
1059 def test_api_get_users_group_exception_occurred(self):
1059 def test_api_get_users_group_exception_occurred(self):
1060 group_name = 'exception_happens'
1060 group_name = 'exception_happens'
1061 id_, params = _build_data(self.apikey, 'create_users_group',
1061 id_, params = _build_data(self.apikey, 'create_users_group',
1062 group_name=group_name)
1062 group_name=group_name)
1063 response = api_call(self, params)
1063 response = api_call(self, params)
1064
1064
1065 expected = 'failed to create group `%s`' % group_name
1065 expected = 'failed to create group `%s`' % group_name
1066 self._compare_error(id_, expected, given=response.body)
1066 self._compare_error(id_, expected, given=response.body)
1067
1067
1068 def test_api_add_user_to_users_group(self):
1068 def test_api_add_user_to_users_group(self):
1069 gr_name = 'test_group'
1069 gr_name = 'test_group'
1070 UsersGroupModel().create(gr_name)
1070 UsersGroupModel().create(gr_name)
1071 Session().commit()
1071 Session().commit()
1072 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1072 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1073 usersgroupid=gr_name,
1073 usersgroupid=gr_name,
1074 userid=TEST_USER_ADMIN_LOGIN)
1074 userid=TEST_USER_ADMIN_LOGIN)
1075 response = api_call(self, params)
1075 response = api_call(self, params)
1076
1076
1077 expected = {
1077 expected = {
1078 'msg': 'added member `%s` to users group `%s`' % (
1078 'msg': 'added member `%s` to user group `%s`' % (
1079 TEST_USER_ADMIN_LOGIN, gr_name
1079 TEST_USER_ADMIN_LOGIN, gr_name
1080 ),
1080 ),
1081 'success': True}
1081 'success': True}
1082 self._compare_ok(id_, expected, given=response.body)
1082 self._compare_ok(id_, expected, given=response.body)
1083
1083
1084 UsersGroupModel().delete(users_group=gr_name)
1084 UsersGroupModel().delete(users_group=gr_name)
1085 Session().commit()
1085 Session().commit()
1086
1086
1087 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1087 def test_api_add_user_to_users_group_that_doesnt_exist(self):
1088 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1088 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1089 usersgroupid='false-group',
1089 usersgroupid='false-group',
1090 userid=TEST_USER_ADMIN_LOGIN)
1090 userid=TEST_USER_ADMIN_LOGIN)
1091 response = api_call(self, params)
1091 response = api_call(self, params)
1092
1092
1093 expected = 'users group `%s` does not exist' % 'false-group'
1093 expected = 'user group `%s` does not exist' % 'false-group'
1094 self._compare_error(id_, expected, given=response.body)
1094 self._compare_error(id_, expected, given=response.body)
1095
1095
1096 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
1096 @mock.patch.object(UsersGroupModel, 'add_user_to_group', crash)
1097 def test_api_add_user_to_users_group_exception_occurred(self):
1097 def test_api_add_user_to_users_group_exception_occurred(self):
1098 gr_name = 'test_group'
1098 gr_name = 'test_group'
1099 UsersGroupModel().create(gr_name)
1099 UsersGroupModel().create(gr_name)
1100 Session().commit()
1100 Session().commit()
1101 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1101 id_, params = _build_data(self.apikey, 'add_user_to_users_group',
1102 usersgroupid=gr_name,
1102 usersgroupid=gr_name,
1103 userid=TEST_USER_ADMIN_LOGIN)
1103 userid=TEST_USER_ADMIN_LOGIN)
1104 response = api_call(self, params)
1104 response = api_call(self, params)
1105
1105
1106 expected = 'failed to add member to users group `%s`' % gr_name
1106 expected = 'failed to add member to user group `%s`' % gr_name
1107 self._compare_error(id_, expected, given=response.body)
1107 self._compare_error(id_, expected, given=response.body)
1108
1108
1109 UsersGroupModel().delete(users_group=gr_name)
1109 UsersGroupModel().delete(users_group=gr_name)
1110 Session().commit()
1110 Session().commit()
1111
1111
1112 def test_api_remove_user_from_users_group(self):
1112 def test_api_remove_user_from_users_group(self):
1113 gr_name = 'test_group_3'
1113 gr_name = 'test_group_3'
1114 gr = UsersGroupModel().create(gr_name)
1114 gr = UsersGroupModel().create(gr_name)
1115 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1115 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1116 Session().commit()
1116 Session().commit()
1117 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1117 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1118 usersgroupid=gr_name,
1118 usersgroupid=gr_name,
1119 userid=TEST_USER_ADMIN_LOGIN)
1119 userid=TEST_USER_ADMIN_LOGIN)
1120 response = api_call(self, params)
1120 response = api_call(self, params)
1121
1121
1122 expected = {
1122 expected = {
1123 'msg': 'removed member `%s` from users group `%s`' % (
1123 'msg': 'removed member `%s` from user group `%s`' % (
1124 TEST_USER_ADMIN_LOGIN, gr_name
1124 TEST_USER_ADMIN_LOGIN, gr_name
1125 ),
1125 ),
1126 'success': True}
1126 'success': True}
1127 self._compare_ok(id_, expected, given=response.body)
1127 self._compare_ok(id_, expected, given=response.body)
1128
1128
1129 UsersGroupModel().delete(users_group=gr_name)
1129 UsersGroupModel().delete(users_group=gr_name)
1130 Session().commit()
1130 Session().commit()
1131
1131
1132 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
1132 @mock.patch.object(UsersGroupModel, 'remove_user_from_group', crash)
1133 def test_api_remove_user_from_users_group_exception_occurred(self):
1133 def test_api_remove_user_from_users_group_exception_occurred(self):
1134 gr_name = 'test_group_3'
1134 gr_name = 'test_group_3'
1135 gr = UsersGroupModel().create(gr_name)
1135 gr = UsersGroupModel().create(gr_name)
1136 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1136 UsersGroupModel().add_user_to_group(gr, user=TEST_USER_ADMIN_LOGIN)
1137 Session().commit()
1137 Session().commit()
1138 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1138 id_, params = _build_data(self.apikey, 'remove_user_from_users_group',
1139 usersgroupid=gr_name,
1139 usersgroupid=gr_name,
1140 userid=TEST_USER_ADMIN_LOGIN)
1140 userid=TEST_USER_ADMIN_LOGIN)
1141 response = api_call(self, params)
1141 response = api_call(self, params)
1142
1142
1143 expected = 'failed to remove member from users group `%s`' % gr_name
1143 expected = 'failed to remove member from user group `%s`' % gr_name
1144 self._compare_error(id_, expected, given=response.body)
1144 self._compare_error(id_, expected, given=response.body)
1145
1145
1146 UsersGroupModel().delete(users_group=gr_name)
1146 UsersGroupModel().delete(users_group=gr_name)
1147 Session().commit()
1147 Session().commit()
1148
1148
1149 @parameterized.expand([('none', 'repository.none'),
1149 @parameterized.expand([('none', 'repository.none'),
1150 ('read', 'repository.read'),
1150 ('read', 'repository.read'),
1151 ('write', 'repository.write'),
1151 ('write', 'repository.write'),
1152 ('admin', 'repository.admin')])
1152 ('admin', 'repository.admin')])
1153 def test_api_grant_user_permission(self, name, perm):
1153 def test_api_grant_user_permission(self, name, perm):
1154 id_, params = _build_data(self.apikey, 'grant_user_permission',
1154 id_, params = _build_data(self.apikey, 'grant_user_permission',
1155 repoid=self.REPO,
1155 repoid=self.REPO,
1156 userid=TEST_USER_ADMIN_LOGIN,
1156 userid=TEST_USER_ADMIN_LOGIN,
1157 perm=perm)
1157 perm=perm)
1158 response = api_call(self, params)
1158 response = api_call(self, params)
1159
1159
1160 ret = {
1160 ret = {
1161 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1161 'msg': 'Granted perm: `%s` for user: `%s` in repo: `%s`' % (
1162 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1162 perm, TEST_USER_ADMIN_LOGIN, self.REPO
1163 ),
1163 ),
1164 'success': True
1164 'success': True
1165 }
1165 }
1166 expected = ret
1166 expected = ret
1167 self._compare_ok(id_, expected, given=response.body)
1167 self._compare_ok(id_, expected, given=response.body)
1168
1168
1169 def test_api_grant_user_permission_wrong_permission(self):
1169 def test_api_grant_user_permission_wrong_permission(self):
1170 perm = 'haha.no.permission'
1170 perm = 'haha.no.permission'
1171 id_, params = _build_data(self.apikey, 'grant_user_permission',
1171 id_, params = _build_data(self.apikey, 'grant_user_permission',
1172 repoid=self.REPO,
1172 repoid=self.REPO,
1173 userid=TEST_USER_ADMIN_LOGIN,
1173 userid=TEST_USER_ADMIN_LOGIN,
1174 perm=perm)
1174 perm=perm)
1175 response = api_call(self, params)
1175 response = api_call(self, params)
1176
1176
1177 expected = 'permission `%s` does not exist' % perm
1177 expected = 'permission `%s` does not exist' % perm
1178 self._compare_error(id_, expected, given=response.body)
1178 self._compare_error(id_, expected, given=response.body)
1179
1179
1180 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1180 @mock.patch.object(RepoModel, 'grant_user_permission', crash)
1181 def test_api_grant_user_permission_exception_when_adding(self):
1181 def test_api_grant_user_permission_exception_when_adding(self):
1182 perm = 'repository.read'
1182 perm = 'repository.read'
1183 id_, params = _build_data(self.apikey, 'grant_user_permission',
1183 id_, params = _build_data(self.apikey, 'grant_user_permission',
1184 repoid=self.REPO,
1184 repoid=self.REPO,
1185 userid=TEST_USER_ADMIN_LOGIN,
1185 userid=TEST_USER_ADMIN_LOGIN,
1186 perm=perm)
1186 perm=perm)
1187 response = api_call(self, params)
1187 response = api_call(self, params)
1188
1188
1189 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1189 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1190 TEST_USER_ADMIN_LOGIN, self.REPO
1190 TEST_USER_ADMIN_LOGIN, self.REPO
1191 )
1191 )
1192 self._compare_error(id_, expected, given=response.body)
1192 self._compare_error(id_, expected, given=response.body)
1193
1193
1194 def test_api_revoke_user_permission(self):
1194 def test_api_revoke_user_permission(self):
1195 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1195 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1196 repoid=self.REPO,
1196 repoid=self.REPO,
1197 userid=TEST_USER_ADMIN_LOGIN,)
1197 userid=TEST_USER_ADMIN_LOGIN,)
1198 response = api_call(self, params)
1198 response = api_call(self, params)
1199
1199
1200 expected = {
1200 expected = {
1201 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1201 'msg': 'Revoked perm for user: `%s` in repo: `%s`' % (
1202 TEST_USER_ADMIN_LOGIN, self.REPO
1202 TEST_USER_ADMIN_LOGIN, self.REPO
1203 ),
1203 ),
1204 'success': True
1204 'success': True
1205 }
1205 }
1206 self._compare_ok(id_, expected, given=response.body)
1206 self._compare_ok(id_, expected, given=response.body)
1207
1207
1208 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1208 @mock.patch.object(RepoModel, 'revoke_user_permission', crash)
1209 def test_api_revoke_user_permission_exception_when_adding(self):
1209 def test_api_revoke_user_permission_exception_when_adding(self):
1210 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1210 id_, params = _build_data(self.apikey, 'revoke_user_permission',
1211 repoid=self.REPO,
1211 repoid=self.REPO,
1212 userid=TEST_USER_ADMIN_LOGIN,)
1212 userid=TEST_USER_ADMIN_LOGIN,)
1213 response = api_call(self, params)
1213 response = api_call(self, params)
1214
1214
1215 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1215 expected = 'failed to edit permission for user: `%s` in repo: `%s`' % (
1216 TEST_USER_ADMIN_LOGIN, self.REPO
1216 TEST_USER_ADMIN_LOGIN, self.REPO
1217 )
1217 )
1218 self._compare_error(id_, expected, given=response.body)
1218 self._compare_error(id_, expected, given=response.body)
1219
1219
1220 @parameterized.expand([('none', 'repository.none'),
1220 @parameterized.expand([('none', 'repository.none'),
1221 ('read', 'repository.read'),
1221 ('read', 'repository.read'),
1222 ('write', 'repository.write'),
1222 ('write', 'repository.write'),
1223 ('admin', 'repository.admin')])
1223 ('admin', 'repository.admin')])
1224 def test_api_grant_users_group_permission(self, name, perm):
1224 def test_api_grant_users_group_permission(self, name, perm):
1225 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1225 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1226 repoid=self.REPO,
1226 repoid=self.REPO,
1227 usersgroupid=TEST_USERS_GROUP,
1227 usersgroupid=TEST_USERS_GROUP,
1228 perm=perm)
1228 perm=perm)
1229 response = api_call(self, params)
1229 response = api_call(self, params)
1230
1230
1231 ret = {
1231 ret = {
1232 'msg': 'Granted perm: `%s` for users group: `%s` in repo: `%s`' % (
1232 'msg': 'Granted perm: `%s` for user group: `%s` in repo: `%s`' % (
1233 perm, TEST_USERS_GROUP, self.REPO
1233 perm, TEST_USERS_GROUP, self.REPO
1234 ),
1234 ),
1235 'success': True
1235 'success': True
1236 }
1236 }
1237 expected = ret
1237 expected = ret
1238 self._compare_ok(id_, expected, given=response.body)
1238 self._compare_ok(id_, expected, given=response.body)
1239
1239
1240 def test_api_grant_users_group_permission_wrong_permission(self):
1240 def test_api_grant_users_group_permission_wrong_permission(self):
1241 perm = 'haha.no.permission'
1241 perm = 'haha.no.permission'
1242 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1242 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1243 repoid=self.REPO,
1243 repoid=self.REPO,
1244 usersgroupid=TEST_USERS_GROUP,
1244 usersgroupid=TEST_USERS_GROUP,
1245 perm=perm)
1245 perm=perm)
1246 response = api_call(self, params)
1246 response = api_call(self, params)
1247
1247
1248 expected = 'permission `%s` does not exist' % perm
1248 expected = 'permission `%s` does not exist' % perm
1249 self._compare_error(id_, expected, given=response.body)
1249 self._compare_error(id_, expected, given=response.body)
1250
1250
1251 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1251 @mock.patch.object(RepoModel, 'grant_users_group_permission', crash)
1252 def test_api_grant_users_group_permission_exception_when_adding(self):
1252 def test_api_grant_users_group_permission_exception_when_adding(self):
1253 perm = 'repository.read'
1253 perm = 'repository.read'
1254 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1254 id_, params = _build_data(self.apikey, 'grant_users_group_permission',
1255 repoid=self.REPO,
1255 repoid=self.REPO,
1256 usersgroupid=TEST_USERS_GROUP,
1256 usersgroupid=TEST_USERS_GROUP,
1257 perm=perm)
1257 perm=perm)
1258 response = api_call(self, params)
1258 response = api_call(self, params)
1259
1259
1260 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
1260 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1261 TEST_USERS_GROUP, self.REPO
1261 TEST_USERS_GROUP, self.REPO
1262 )
1262 )
1263 self._compare_error(id_, expected, given=response.body)
1263 self._compare_error(id_, expected, given=response.body)
1264
1264
1265 def test_api_revoke_users_group_permission(self):
1265 def test_api_revoke_users_group_permission(self):
1266 RepoModel().grant_users_group_permission(repo=self.REPO,
1266 RepoModel().grant_users_group_permission(repo=self.REPO,
1267 group_name=TEST_USERS_GROUP,
1267 group_name=TEST_USERS_GROUP,
1268 perm='repository.read')
1268 perm='repository.read')
1269 Session().commit()
1269 Session().commit()
1270 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1270 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1271 repoid=self.REPO,
1271 repoid=self.REPO,
1272 usersgroupid=TEST_USERS_GROUP,)
1272 usersgroupid=TEST_USERS_GROUP,)
1273 response = api_call(self, params)
1273 response = api_call(self, params)
1274
1274
1275 expected = {
1275 expected = {
1276 'msg': 'Revoked perm for users group: `%s` in repo: `%s`' % (
1276 'msg': 'Revoked perm for user group: `%s` in repo: `%s`' % (
1277 TEST_USERS_GROUP, self.REPO
1277 TEST_USERS_GROUP, self.REPO
1278 ),
1278 ),
1279 'success': True
1279 'success': True
1280 }
1280 }
1281 self._compare_ok(id_, expected, given=response.body)
1281 self._compare_ok(id_, expected, given=response.body)
1282
1282
1283 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1283 @mock.patch.object(RepoModel, 'revoke_users_group_permission', crash)
1284 def test_api_revoke_users_group_permission_exception_when_adding(self):
1284 def test_api_revoke_users_group_permission_exception_when_adding(self):
1285
1285
1286 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1286 id_, params = _build_data(self.apikey, 'revoke_users_group_permission',
1287 repoid=self.REPO,
1287 repoid=self.REPO,
1288 usersgroupid=TEST_USERS_GROUP,)
1288 usersgroupid=TEST_USERS_GROUP,)
1289 response = api_call(self, params)
1289 response = api_call(self, params)
1290
1290
1291 expected = 'failed to edit permission for users group: `%s` in repo: `%s`' % (
1291 expected = 'failed to edit permission for user group: `%s` in repo: `%s`' % (
1292 TEST_USERS_GROUP, self.REPO
1292 TEST_USERS_GROUP, self.REPO
1293 )
1293 )
1294 self._compare_error(id_, expected, given=response.body)
1294 self._compare_error(id_, expected, given=response.body)
@@ -1,224 +1,224 b''
1 from rhodecode.tests import *
1 from rhodecode.tests import *
2 from rhodecode.model.db import UsersGroup, UsersGroupToPerm, Permission
2 from rhodecode.model.db import UsersGroup, UsersGroupToPerm, Permission
3
3
4 TEST_USERS_GROUP = 'admins_test'
4 TEST_USERS_GROUP = 'admins_test'
5
5
6
6
7 class TestAdminUsersGroupsController(TestController):
7 class TestAdminUsersGroupsController(TestController):
8
8
9 def test_index(self):
9 def test_index(self):
10 response = self.app.get(url('users_groups'))
10 response = self.app.get(url('users_groups'))
11 # Test response...
11 # Test response...
12
12
13 def test_index_as_xml(self):
13 def test_index_as_xml(self):
14 response = self.app.get(url('formatted_users_groups', format='xml'))
14 response = self.app.get(url('formatted_users_groups', format='xml'))
15
15
16 def test_create(self):
16 def test_create(self):
17 self.log_user()
17 self.log_user()
18 users_group_name = TEST_USERS_GROUP
18 users_group_name = TEST_USERS_GROUP
19 response = self.app.post(url('users_groups'),
19 response = self.app.post(url('users_groups'),
20 {'users_group_name': users_group_name,
20 {'users_group_name': users_group_name,
21 'active':True})
21 'active':True})
22 response.follow()
22 response.follow()
23
23
24 self.checkSessionFlash(response,
24 self.checkSessionFlash(response,
25 'created users group %s' % TEST_USERS_GROUP)
25 'created user group %s' % TEST_USERS_GROUP)
26
26
27 def test_new(self):
27 def test_new(self):
28 response = self.app.get(url('new_users_group'))
28 response = self.app.get(url('new_users_group'))
29
29
30 def test_new_as_xml(self):
30 def test_new_as_xml(self):
31 response = self.app.get(url('formatted_new_users_group', format='xml'))
31 response = self.app.get(url('formatted_new_users_group', format='xml'))
32
32
33 def test_update(self):
33 def test_update(self):
34 response = self.app.put(url('users_group', id=1))
34 response = self.app.put(url('users_group', id=1))
35
35
36 def test_update_browser_fakeout(self):
36 def test_update_browser_fakeout(self):
37 response = self.app.post(url('users_group', id=1),
37 response = self.app.post(url('users_group', id=1),
38 params=dict(_method='put'))
38 params=dict(_method='put'))
39
39
40 def test_delete(self):
40 def test_delete(self):
41 self.log_user()
41 self.log_user()
42 users_group_name = TEST_USERS_GROUP + 'another'
42 users_group_name = TEST_USERS_GROUP + 'another'
43 response = self.app.post(url('users_groups'),
43 response = self.app.post(url('users_groups'),
44 {'users_group_name':users_group_name,
44 {'users_group_name':users_group_name,
45 'active':True})
45 'active':True})
46 response.follow()
46 response.follow()
47
47
48 self.checkSessionFlash(response,
48 self.checkSessionFlash(response,
49 'created users group %s' % users_group_name)
49 'created user group %s' % users_group_name)
50
50
51 gr = self.Session.query(UsersGroup)\
51 gr = self.Session.query(UsersGroup)\
52 .filter(UsersGroup.users_group_name ==
52 .filter(UsersGroup.users_group_name ==
53 users_group_name).one()
53 users_group_name).one()
54
54
55 response = self.app.delete(url('users_group', id=gr.users_group_id))
55 response = self.app.delete(url('users_group', id=gr.users_group_id))
56
56
57 gr = self.Session.query(UsersGroup)\
57 gr = self.Session.query(UsersGroup)\
58 .filter(UsersGroup.users_group_name ==
58 .filter(UsersGroup.users_group_name ==
59 users_group_name).scalar()
59 users_group_name).scalar()
60
60
61 self.assertEqual(gr, None)
61 self.assertEqual(gr, None)
62
62
63 def test_enable_repository_read_on_group(self):
63 def test_enable_repository_read_on_group(self):
64 self.log_user()
64 self.log_user()
65 users_group_name = TEST_USERS_GROUP + 'another2'
65 users_group_name = TEST_USERS_GROUP + 'another2'
66 response = self.app.post(url('users_groups'),
66 response = self.app.post(url('users_groups'),
67 {'users_group_name': users_group_name,
67 {'users_group_name': users_group_name,
68 'active': True})
68 'active': True})
69 response.follow()
69 response.follow()
70
70
71 ug = UsersGroup.get_by_group_name(users_group_name)
71 ug = UsersGroup.get_by_group_name(users_group_name)
72 self.checkSessionFlash(response,
72 self.checkSessionFlash(response,
73 'created users group %s' % users_group_name)
73 'created user group %s' % users_group_name)
74 ## ENABLE REPO CREATE ON A GROUP
74 ## ENABLE REPO CREATE ON A GROUP
75 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
75 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
76 {'create_repo_perm': True})
76 {'create_repo_perm': True})
77
77
78 response.follow()
78 response.follow()
79 ug = UsersGroup.get_by_group_name(users_group_name)
79 ug = UsersGroup.get_by_group_name(users_group_name)
80 p = Permission.get_by_key('hg.create.repository')
80 p = Permission.get_by_key('hg.create.repository')
81 p2 = Permission.get_by_key('hg.fork.none')
81 p2 = Permission.get_by_key('hg.fork.none')
82 # check if user has this perms, they should be here since
82 # check if user has this perms, they should be here since
83 # defaults are on
83 # defaults are on
84 perms = UsersGroupToPerm.query()\
84 perms = UsersGroupToPerm.query()\
85 .filter(UsersGroupToPerm.users_group == ug).all()
85 .filter(UsersGroupToPerm.users_group == ug).all()
86
86
87 self.assertEqual(
87 self.assertEqual(
88 [[x.users_group_id, x.permission_id, ] for x in perms],
88 [[x.users_group_id, x.permission_id, ] for x in perms],
89 [[ug.users_group_id, p.permission_id],
89 [[ug.users_group_id, p.permission_id],
90 [ug.users_group_id, p2.permission_id]]
90 [ug.users_group_id, p2.permission_id]]
91 )
91 )
92
92
93 ## DISABLE REPO CREATE ON A GROUP
93 ## DISABLE REPO CREATE ON A GROUP
94 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
94 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
95 {})
95 {})
96
96
97 response.follow()
97 response.follow()
98 ug = UsersGroup.get_by_group_name(users_group_name)
98 ug = UsersGroup.get_by_group_name(users_group_name)
99 p = Permission.get_by_key('hg.create.none')
99 p = Permission.get_by_key('hg.create.none')
100 p2 = Permission.get_by_key('hg.fork.none')
100 p2 = Permission.get_by_key('hg.fork.none')
101 # check if user has this perms, they should be here since
101 # check if user has this perms, they should be here since
102 # defaults are on
102 # defaults are on
103 perms = UsersGroupToPerm.query()\
103 perms = UsersGroupToPerm.query()\
104 .filter(UsersGroupToPerm.users_group == ug).all()
104 .filter(UsersGroupToPerm.users_group == ug).all()
105
105
106 self.assertEqual(
106 self.assertEqual(
107 sorted([[x.users_group_id, x.permission_id, ] for x in perms]),
107 sorted([[x.users_group_id, x.permission_id, ] for x in perms]),
108 sorted([[ug.users_group_id, p.permission_id],
108 sorted([[ug.users_group_id, p.permission_id],
109 [ug.users_group_id, p2.permission_id]])
109 [ug.users_group_id, p2.permission_id]])
110 )
110 )
111
111
112 # DELETE !
112 # DELETE !
113 ug = UsersGroup.get_by_group_name(users_group_name)
113 ug = UsersGroup.get_by_group_name(users_group_name)
114 ugid = ug.users_group_id
114 ugid = ug.users_group_id
115 response = self.app.delete(url('users_group', id=ug.users_group_id))
115 response = self.app.delete(url('users_group', id=ug.users_group_id))
116 response = response.follow()
116 response = response.follow()
117 gr = self.Session.query(UsersGroup)\
117 gr = self.Session.query(UsersGroup)\
118 .filter(UsersGroup.users_group_name ==
118 .filter(UsersGroup.users_group_name ==
119 users_group_name).scalar()
119 users_group_name).scalar()
120
120
121 self.assertEqual(gr, None)
121 self.assertEqual(gr, None)
122 p = Permission.get_by_key('hg.create.repository')
122 p = Permission.get_by_key('hg.create.repository')
123 perms = UsersGroupToPerm.query()\
123 perms = UsersGroupToPerm.query()\
124 .filter(UsersGroupToPerm.users_group_id == ugid).all()
124 .filter(UsersGroupToPerm.users_group_id == ugid).all()
125 perms = [[x.users_group_id,
125 perms = [[x.users_group_id,
126 x.permission_id, ] for x in perms]
126 x.permission_id, ] for x in perms]
127 self.assertEqual(
127 self.assertEqual(
128 perms,
128 perms,
129 []
129 []
130 )
130 )
131
131
132 def test_enable_repository_fork_on_group(self):
132 def test_enable_repository_fork_on_group(self):
133 self.log_user()
133 self.log_user()
134 users_group_name = TEST_USERS_GROUP + 'another2'
134 users_group_name = TEST_USERS_GROUP + 'another2'
135 response = self.app.post(url('users_groups'),
135 response = self.app.post(url('users_groups'),
136 {'users_group_name': users_group_name,
136 {'users_group_name': users_group_name,
137 'active': True})
137 'active': True})
138 response.follow()
138 response.follow()
139
139
140 ug = UsersGroup.get_by_group_name(users_group_name)
140 ug = UsersGroup.get_by_group_name(users_group_name)
141 self.checkSessionFlash(response,
141 self.checkSessionFlash(response,
142 'created users group %s' % users_group_name)
142 'created user group %s' % users_group_name)
143 ## ENABLE REPO CREATE ON A GROUP
143 ## ENABLE REPO CREATE ON A GROUP
144 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
144 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
145 {'fork_repo_perm': True})
145 {'fork_repo_perm': True})
146
146
147 response.follow()
147 response.follow()
148 ug = UsersGroup.get_by_group_name(users_group_name)
148 ug = UsersGroup.get_by_group_name(users_group_name)
149 p = Permission.get_by_key('hg.create.none')
149 p = Permission.get_by_key('hg.create.none')
150 p2 = Permission.get_by_key('hg.fork.repository')
150 p2 = Permission.get_by_key('hg.fork.repository')
151 # check if user has this perms, they should be here since
151 # check if user has this perms, they should be here since
152 # defaults are on
152 # defaults are on
153 perms = UsersGroupToPerm.query()\
153 perms = UsersGroupToPerm.query()\
154 .filter(UsersGroupToPerm.users_group == ug).all()
154 .filter(UsersGroupToPerm.users_group == ug).all()
155
155
156 self.assertEqual(
156 self.assertEqual(
157 [[x.users_group_id, x.permission_id, ] for x in perms],
157 [[x.users_group_id, x.permission_id, ] for x in perms],
158 [[ug.users_group_id, p.permission_id],
158 [[ug.users_group_id, p.permission_id],
159 [ug.users_group_id, p2.permission_id]]
159 [ug.users_group_id, p2.permission_id]]
160 )
160 )
161
161
162 ## DISABLE REPO CREATE ON A GROUP
162 ## DISABLE REPO CREATE ON A GROUP
163 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
163 response = self.app.put(url('users_group_perm', id=ug.users_group_id),
164 {})
164 {})
165
165
166 response.follow()
166 response.follow()
167 ug = UsersGroup.get_by_group_name(users_group_name)
167 ug = UsersGroup.get_by_group_name(users_group_name)
168 p = Permission.get_by_key('hg.create.none')
168 p = Permission.get_by_key('hg.create.none')
169 p2 = Permission.get_by_key('hg.fork.none')
169 p2 = Permission.get_by_key('hg.fork.none')
170 # check if user has this perms, they should be here since
170 # check if user has this perms, they should be here since
171 # defaults are on
171 # defaults are on
172 perms = UsersGroupToPerm.query()\
172 perms = UsersGroupToPerm.query()\
173 .filter(UsersGroupToPerm.users_group == ug).all()
173 .filter(UsersGroupToPerm.users_group == ug).all()
174
174
175 self.assertEqual(
175 self.assertEqual(
176 [[x.users_group_id, x.permission_id, ] for x in perms],
176 [[x.users_group_id, x.permission_id, ] for x in perms],
177 [[ug.users_group_id, p.permission_id],
177 [[ug.users_group_id, p.permission_id],
178 [ug.users_group_id, p2.permission_id]]
178 [ug.users_group_id, p2.permission_id]]
179 )
179 )
180
180
181 # DELETE !
181 # DELETE !
182 ug = UsersGroup.get_by_group_name(users_group_name)
182 ug = UsersGroup.get_by_group_name(users_group_name)
183 ugid = ug.users_group_id
183 ugid = ug.users_group_id
184 response = self.app.delete(url('users_group', id=ug.users_group_id))
184 response = self.app.delete(url('users_group', id=ug.users_group_id))
185 response = response.follow()
185 response = response.follow()
186 gr = self.Session.query(UsersGroup)\
186 gr = self.Session.query(UsersGroup)\
187 .filter(UsersGroup.users_group_name ==
187 .filter(UsersGroup.users_group_name ==
188 users_group_name).scalar()
188 users_group_name).scalar()
189
189
190 self.assertEqual(gr, None)
190 self.assertEqual(gr, None)
191 p = Permission.get_by_key('hg.fork.repository')
191 p = Permission.get_by_key('hg.fork.repository')
192 perms = UsersGroupToPerm.query()\
192 perms = UsersGroupToPerm.query()\
193 .filter(UsersGroupToPerm.users_group_id == ugid).all()
193 .filter(UsersGroupToPerm.users_group_id == ugid).all()
194 perms = [[x.users_group_id,
194 perms = [[x.users_group_id,
195 x.permission_id, ] for x in perms]
195 x.permission_id, ] for x in perms]
196 self.assertEqual(
196 self.assertEqual(
197 perms,
197 perms,
198 []
198 []
199 )
199 )
200
200
201 def test_delete_browser_fakeout(self):
201 def test_delete_browser_fakeout(self):
202 response = self.app.post(url('users_group', id=1),
202 response = self.app.post(url('users_group', id=1),
203 params=dict(_method='delete'))
203 params=dict(_method='delete'))
204
204
205 def test_show(self):
205 def test_show(self):
206 response = self.app.get(url('users_group', id=1))
206 response = self.app.get(url('users_group', id=1))
207
207
208 def test_show_as_xml(self):
208 def test_show_as_xml(self):
209 response = self.app.get(url('formatted_users_group', id=1, format='xml'))
209 response = self.app.get(url('formatted_users_group', id=1, format='xml'))
210
210
211 def test_edit(self):
211 def test_edit(self):
212 response = self.app.get(url('edit_users_group', id=1))
212 response = self.app.get(url('edit_users_group', id=1))
213
213
214 def test_edit_as_xml(self):
214 def test_edit_as_xml(self):
215 response = self.app.get(url('formatted_edit_users_group', id=1, format='xml'))
215 response = self.app.get(url('formatted_edit_users_group', id=1, format='xml'))
216
216
217 def test_assign_members(self):
217 def test_assign_members(self):
218 pass
218 pass
219
219
220 def test_add_create_permission(self):
220 def test_add_create_permission(self):
221 pass
221 pass
222
222
223 def test_revoke_members(self):
223 def test_revoke_members(self):
224 pass
224 pass
@@ -1,472 +1,472 b''
1 import os
1 import os
2 import unittest
2 import unittest
3 from rhodecode.tests import *
3 from rhodecode.tests import *
4 from rhodecode.tests.models.common import _make_group
4 from rhodecode.tests.models.common import _make_group
5 from rhodecode.model.repos_group import ReposGroupModel
5 from rhodecode.model.repos_group import ReposGroupModel
6 from rhodecode.model.repo import RepoModel
6 from rhodecode.model.repo import RepoModel
7 from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
7 from rhodecode.model.db import RepoGroup, User, UsersGroupRepoGroupToPerm
8 from rhodecode.model.user import UserModel
8 from rhodecode.model.user import UserModel
9
9
10 from rhodecode.model.meta import Session
10 from rhodecode.model.meta import Session
11 from rhodecode.model.users_group import UsersGroupModel
11 from rhodecode.model.users_group import UsersGroupModel
12 from rhodecode.lib.auth import AuthUser
12 from rhodecode.lib.auth import AuthUser
13 from rhodecode.tests.api.api_base import create_repo
13 from rhodecode.tests.api.api_base import create_repo
14
14
15
15
16 class TestPermissions(unittest.TestCase):
16 class TestPermissions(unittest.TestCase):
17 def __init__(self, methodName='runTest'):
17 def __init__(self, methodName='runTest'):
18 super(TestPermissions, self).__init__(methodName=methodName)
18 super(TestPermissions, self).__init__(methodName=methodName)
19
19
20 def setUp(self):
20 def setUp(self):
21 self.u1 = UserModel().create_or_update(
21 self.u1 = UserModel().create_or_update(
22 username=u'u1', password=u'qweqwe',
22 username=u'u1', password=u'qweqwe',
23 email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1'
23 email=u'u1@rhodecode.org', firstname=u'u1', lastname=u'u1'
24 )
24 )
25 self.u2 = UserModel().create_or_update(
25 self.u2 = UserModel().create_or_update(
26 username=u'u2', password=u'qweqwe',
26 username=u'u2', password=u'qweqwe',
27 email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2'
27 email=u'u2@rhodecode.org', firstname=u'u2', lastname=u'u2'
28 )
28 )
29 self.u3 = UserModel().create_or_update(
29 self.u3 = UserModel().create_or_update(
30 username=u'u3', password=u'qweqwe',
30 username=u'u3', password=u'qweqwe',
31 email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3'
31 email=u'u3@rhodecode.org', firstname=u'u3', lastname=u'u3'
32 )
32 )
33 self.anon = User.get_by_username('default')
33 self.anon = User.get_by_username('default')
34 self.a1 = UserModel().create_or_update(
34 self.a1 = UserModel().create_or_update(
35 username=u'a1', password=u'qweqwe',
35 username=u'a1', password=u'qweqwe',
36 email=u'a1@rhodecode.org', firstname=u'a1', lastname=u'a1', admin=True
36 email=u'a1@rhodecode.org', firstname=u'a1', lastname=u'a1', admin=True
37 )
37 )
38 Session().commit()
38 Session().commit()
39
39
40 def tearDown(self):
40 def tearDown(self):
41 if hasattr(self, 'test_repo'):
41 if hasattr(self, 'test_repo'):
42 RepoModel().delete(repo=self.test_repo)
42 RepoModel().delete(repo=self.test_repo)
43
43
44 UserModel().delete(self.u1)
44 UserModel().delete(self.u1)
45 UserModel().delete(self.u2)
45 UserModel().delete(self.u2)
46 UserModel().delete(self.u3)
46 UserModel().delete(self.u3)
47 UserModel().delete(self.a1)
47 UserModel().delete(self.a1)
48 if hasattr(self, 'g1'):
48 if hasattr(self, 'g1'):
49 ReposGroupModel().delete(self.g1.group_id)
49 ReposGroupModel().delete(self.g1.group_id)
50 if hasattr(self, 'g2'):
50 if hasattr(self, 'g2'):
51 ReposGroupModel().delete(self.g2.group_id)
51 ReposGroupModel().delete(self.g2.group_id)
52
52
53 if hasattr(self, 'ug1'):
53 if hasattr(self, 'ug1'):
54 UsersGroupModel().delete(self.ug1, force=True)
54 UsersGroupModel().delete(self.ug1, force=True)
55
55
56 Session().commit()
56 Session().commit()
57
57
58 def test_default_perms_set(self):
58 def test_default_perms_set(self):
59 u1_auth = AuthUser(user_id=self.u1.user_id)
59 u1_auth = AuthUser(user_id=self.u1.user_id)
60 perms = {
60 perms = {
61 'repositories_groups': {},
61 'repositories_groups': {},
62 'global': set([u'hg.create.repository', u'repository.read',
62 'global': set([u'hg.create.repository', u'repository.read',
63 u'hg.register.manual_activate']),
63 u'hg.register.manual_activate']),
64 'repositories': {u'vcs_test_hg': u'repository.read'}
64 'repositories': {u'vcs_test_hg': u'repository.read'}
65 }
65 }
66 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
66 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
67 perms['repositories'][HG_REPO])
67 perms['repositories'][HG_REPO])
68 new_perm = 'repository.write'
68 new_perm = 'repository.write'
69 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
69 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
70 perm=new_perm)
70 perm=new_perm)
71 Session().commit()
71 Session().commit()
72
72
73 u1_auth = AuthUser(user_id=self.u1.user_id)
73 u1_auth = AuthUser(user_id=self.u1.user_id)
74 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
74 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
75 new_perm)
75 new_perm)
76
76
77 def test_default_admin_perms_set(self):
77 def test_default_admin_perms_set(self):
78 a1_auth = AuthUser(user_id=self.a1.user_id)
78 a1_auth = AuthUser(user_id=self.a1.user_id)
79 perms = {
79 perms = {
80 'repositories_groups': {},
80 'repositories_groups': {},
81 'global': set([u'hg.admin']),
81 'global': set([u'hg.admin']),
82 'repositories': {u'vcs_test_hg': u'repository.admin'}
82 'repositories': {u'vcs_test_hg': u'repository.admin'}
83 }
83 }
84 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
84 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
85 perms['repositories'][HG_REPO])
85 perms['repositories'][HG_REPO])
86 new_perm = 'repository.write'
86 new_perm = 'repository.write'
87 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1,
87 RepoModel().grant_user_permission(repo=HG_REPO, user=self.a1,
88 perm=new_perm)
88 perm=new_perm)
89 Session().commit()
89 Session().commit()
90 # cannot really downgrade admins permissions !? they still get's set as
90 # cannot really downgrade admins permissions !? they still get's set as
91 # admin !
91 # admin !
92 u1_auth = AuthUser(user_id=self.a1.user_id)
92 u1_auth = AuthUser(user_id=self.a1.user_id)
93 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
93 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
94 perms['repositories'][HG_REPO])
94 perms['repositories'][HG_REPO])
95
95
96 def test_default_group_perms(self):
96 def test_default_group_perms(self):
97 self.g1 = _make_group('test1', skip_if_exists=True)
97 self.g1 = _make_group('test1', skip_if_exists=True)
98 self.g2 = _make_group('test2', skip_if_exists=True)
98 self.g2 = _make_group('test2', skip_if_exists=True)
99 u1_auth = AuthUser(user_id=self.u1.user_id)
99 u1_auth = AuthUser(user_id=self.u1.user_id)
100 perms = {
100 perms = {
101 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
101 'repositories_groups': {u'test1': 'group.read', u'test2': 'group.read'},
102 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
102 'global': set([u'hg.create.repository', u'repository.read', u'hg.register.manual_activate']),
103 'repositories': {u'vcs_test_hg': u'repository.read'}
103 'repositories': {u'vcs_test_hg': u'repository.read'}
104 }
104 }
105 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
105 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
106 perms['repositories'][HG_REPO])
106 perms['repositories'][HG_REPO])
107 self.assertEqual(u1_auth.permissions['repositories_groups'],
107 self.assertEqual(u1_auth.permissions['repositories_groups'],
108 perms['repositories_groups'])
108 perms['repositories_groups'])
109
109
110 def test_default_admin_group_perms(self):
110 def test_default_admin_group_perms(self):
111 self.g1 = _make_group('test1', skip_if_exists=True)
111 self.g1 = _make_group('test1', skip_if_exists=True)
112 self.g2 = _make_group('test2', skip_if_exists=True)
112 self.g2 = _make_group('test2', skip_if_exists=True)
113 a1_auth = AuthUser(user_id=self.a1.user_id)
113 a1_auth = AuthUser(user_id=self.a1.user_id)
114 perms = {
114 perms = {
115 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
115 'repositories_groups': {u'test1': 'group.admin', u'test2': 'group.admin'},
116 'global': set(['hg.admin']),
116 'global': set(['hg.admin']),
117 'repositories': {u'vcs_test_hg': 'repository.admin'}
117 'repositories': {u'vcs_test_hg': 'repository.admin'}
118 }
118 }
119
119
120 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
120 self.assertEqual(a1_auth.permissions['repositories'][HG_REPO],
121 perms['repositories'][HG_REPO])
121 perms['repositories'][HG_REPO])
122 self.assertEqual(a1_auth.permissions['repositories_groups'],
122 self.assertEqual(a1_auth.permissions['repositories_groups'],
123 perms['repositories_groups'])
123 perms['repositories_groups'])
124
124
125 def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
125 def test_propagated_permission_from_users_group_by_explicit_perms_exist(self):
126 # make group
126 # make group
127 self.ug1 = UsersGroupModel().create('G1')
127 self.ug1 = UsersGroupModel().create('G1')
128 # add user to group
128 # add user to group
129
129
130 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
130 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
131
131
132 # set permission to lower
132 # set permission to lower
133 new_perm = 'repository.none'
133 new_perm = 'repository.none'
134 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
134 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1, perm=new_perm)
135 Session().commit()
135 Session().commit()
136 u1_auth = AuthUser(user_id=self.u1.user_id)
136 u1_auth = AuthUser(user_id=self.u1.user_id)
137 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
137 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
138 new_perm)
138 new_perm)
139
139
140 # grant perm for group this should not override permission from user
140 # grant perm for group this should not override permission from user
141 # since it has explicitly set
141 # since it has explicitly set
142 new_perm_gr = 'repository.write'
142 new_perm_gr = 'repository.write'
143 RepoModel().grant_users_group_permission(repo=HG_REPO,
143 RepoModel().grant_users_group_permission(repo=HG_REPO,
144 group_name=self.ug1,
144 group_name=self.ug1,
145 perm=new_perm_gr)
145 perm=new_perm_gr)
146 # check perms
146 # check perms
147 u1_auth = AuthUser(user_id=self.u1.user_id)
147 u1_auth = AuthUser(user_id=self.u1.user_id)
148 perms = {
148 perms = {
149 'repositories_groups': {},
149 'repositories_groups': {},
150 'global': set([u'hg.create.repository', u'repository.read',
150 'global': set([u'hg.create.repository', u'repository.read',
151 u'hg.register.manual_activate']),
151 u'hg.register.manual_activate']),
152 'repositories': {u'vcs_test_hg': u'repository.read'}
152 'repositories': {u'vcs_test_hg': u'repository.read'}
153 }
153 }
154 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
154 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
155 new_perm)
155 new_perm)
156 self.assertEqual(u1_auth.permissions['repositories_groups'],
156 self.assertEqual(u1_auth.permissions['repositories_groups'],
157 perms['repositories_groups'])
157 perms['repositories_groups'])
158
158
159 def test_propagated_permission_from_users_group(self):
159 def test_propagated_permission_from_users_group(self):
160 # make group
160 # make group
161 self.ug1 = UsersGroupModel().create('G1')
161 self.ug1 = UsersGroupModel().create('G1')
162 # add user to group
162 # add user to group
163
163
164 UsersGroupModel().add_user_to_group(self.ug1, self.u3)
164 UsersGroupModel().add_user_to_group(self.ug1, self.u3)
165
165
166 # grant perm for group this should override default permission from user
166 # grant perm for group this should override default permission from user
167 new_perm_gr = 'repository.write'
167 new_perm_gr = 'repository.write'
168 RepoModel().grant_users_group_permission(repo=HG_REPO,
168 RepoModel().grant_users_group_permission(repo=HG_REPO,
169 group_name=self.ug1,
169 group_name=self.ug1,
170 perm=new_perm_gr)
170 perm=new_perm_gr)
171 # check perms
171 # check perms
172 u3_auth = AuthUser(user_id=self.u3.user_id)
172 u3_auth = AuthUser(user_id=self.u3.user_id)
173 perms = {
173 perms = {
174 'repositories_groups': {},
174 'repositories_groups': {},
175 'global': set([u'hg.create.repository', u'repository.read',
175 'global': set([u'hg.create.repository', u'repository.read',
176 u'hg.register.manual_activate']),
176 u'hg.register.manual_activate']),
177 'repositories': {u'vcs_test_hg': u'repository.read'}
177 'repositories': {u'vcs_test_hg': u'repository.read'}
178 }
178 }
179 self.assertEqual(u3_auth.permissions['repositories'][HG_REPO],
179 self.assertEqual(u3_auth.permissions['repositories'][HG_REPO],
180 new_perm_gr)
180 new_perm_gr)
181 self.assertEqual(u3_auth.permissions['repositories_groups'],
181 self.assertEqual(u3_auth.permissions['repositories_groups'],
182 perms['repositories_groups'])
182 perms['repositories_groups'])
183
183
184 def test_propagated_permission_from_users_group_lower_weight(self):
184 def test_propagated_permission_from_users_group_lower_weight(self):
185 # make group
185 # make group
186 self.ug1 = UsersGroupModel().create('G1')
186 self.ug1 = UsersGroupModel().create('G1')
187 # add user to group
187 # add user to group
188 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
188 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
189
189
190 # set permission to lower
190 # set permission to lower
191 new_perm_h = 'repository.write'
191 new_perm_h = 'repository.write'
192 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
192 RepoModel().grant_user_permission(repo=HG_REPO, user=self.u1,
193 perm=new_perm_h)
193 perm=new_perm_h)
194 Session().commit()
194 Session().commit()
195 u1_auth = AuthUser(user_id=self.u1.user_id)
195 u1_auth = AuthUser(user_id=self.u1.user_id)
196 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
196 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
197 new_perm_h)
197 new_perm_h)
198
198
199 # grant perm for group this should NOT override permission from user
199 # grant perm for group this should NOT override permission from user
200 # since it's lower than granted
200 # since it's lower than granted
201 new_perm_l = 'repository.read'
201 new_perm_l = 'repository.read'
202 RepoModel().grant_users_group_permission(repo=HG_REPO,
202 RepoModel().grant_users_group_permission(repo=HG_REPO,
203 group_name=self.ug1,
203 group_name=self.ug1,
204 perm=new_perm_l)
204 perm=new_perm_l)
205 # check perms
205 # check perms
206 u1_auth = AuthUser(user_id=self.u1.user_id)
206 u1_auth = AuthUser(user_id=self.u1.user_id)
207 perms = {
207 perms = {
208 'repositories_groups': {},
208 'repositories_groups': {},
209 'global': set([u'hg.create.repository', u'repository.read',
209 'global': set([u'hg.create.repository', u'repository.read',
210 u'hg.register.manual_activate']),
210 u'hg.register.manual_activate']),
211 'repositories': {u'vcs_test_hg': u'repository.write'}
211 'repositories': {u'vcs_test_hg': u'repository.write'}
212 }
212 }
213 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
213 self.assertEqual(u1_auth.permissions['repositories'][HG_REPO],
214 new_perm_h)
214 new_perm_h)
215 self.assertEqual(u1_auth.permissions['repositories_groups'],
215 self.assertEqual(u1_auth.permissions['repositories_groups'],
216 perms['repositories_groups'])
216 perms['repositories_groups'])
217
217
218 def test_repo_in_group_permissions(self):
218 def test_repo_in_group_permissions(self):
219 self.g1 = _make_group('group1', skip_if_exists=True)
219 self.g1 = _make_group('group1', skip_if_exists=True)
220 self.g2 = _make_group('group2', skip_if_exists=True)
220 self.g2 = _make_group('group2', skip_if_exists=True)
221 Session().commit()
221 Session().commit()
222 # both perms should be read !
222 # both perms should be read !
223 u1_auth = AuthUser(user_id=self.u1.user_id)
223 u1_auth = AuthUser(user_id=self.u1.user_id)
224 self.assertEqual(u1_auth.permissions['repositories_groups'],
224 self.assertEqual(u1_auth.permissions['repositories_groups'],
225 {u'group1': u'group.read', u'group2': u'group.read'})
225 {u'group1': u'group.read', u'group2': u'group.read'})
226
226
227 a1_auth = AuthUser(user_id=self.anon.user_id)
227 a1_auth = AuthUser(user_id=self.anon.user_id)
228 self.assertEqual(a1_auth.permissions['repositories_groups'],
228 self.assertEqual(a1_auth.permissions['repositories_groups'],
229 {u'group1': u'group.read', u'group2': u'group.read'})
229 {u'group1': u'group.read', u'group2': u'group.read'})
230
230
231 #Change perms to none for both groups
231 #Change perms to none for both groups
232 ReposGroupModel().grant_user_permission(repos_group=self.g1,
232 ReposGroupModel().grant_user_permission(repos_group=self.g1,
233 user=self.anon,
233 user=self.anon,
234 perm='group.none')
234 perm='group.none')
235 ReposGroupModel().grant_user_permission(repos_group=self.g2,
235 ReposGroupModel().grant_user_permission(repos_group=self.g2,
236 user=self.anon,
236 user=self.anon,
237 perm='group.none')
237 perm='group.none')
238
238
239 u1_auth = AuthUser(user_id=self.u1.user_id)
239 u1_auth = AuthUser(user_id=self.u1.user_id)
240 self.assertEqual(u1_auth.permissions['repositories_groups'],
240 self.assertEqual(u1_auth.permissions['repositories_groups'],
241 {u'group1': u'group.none', u'group2': u'group.none'})
241 {u'group1': u'group.none', u'group2': u'group.none'})
242
242
243 a1_auth = AuthUser(user_id=self.anon.user_id)
243 a1_auth = AuthUser(user_id=self.anon.user_id)
244 self.assertEqual(a1_auth.permissions['repositories_groups'],
244 self.assertEqual(a1_auth.permissions['repositories_groups'],
245 {u'group1': u'group.none', u'group2': u'group.none'})
245 {u'group1': u'group.none', u'group2': u'group.none'})
246
246
247 # add repo to group
247 # add repo to group
248 name = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm'])
248 name = RepoGroup.url_sep().join([self.g1.group_name, 'test_perm'])
249 self.test_repo = RepoModel().create_repo(
249 self.test_repo = RepoModel().create_repo(
250 repo_name=name,
250 repo_name=name,
251 repo_type='hg',
251 repo_type='hg',
252 description='',
252 description='',
253 repos_group=self.g1,
253 repos_group=self.g1,
254 owner=self.u1,
254 owner=self.u1,
255 )
255 )
256 Session().commit()
256 Session().commit()
257
257
258 u1_auth = AuthUser(user_id=self.u1.user_id)
258 u1_auth = AuthUser(user_id=self.u1.user_id)
259 self.assertEqual(u1_auth.permissions['repositories_groups'],
259 self.assertEqual(u1_auth.permissions['repositories_groups'],
260 {u'group1': u'group.none', u'group2': u'group.none'})
260 {u'group1': u'group.none', u'group2': u'group.none'})
261
261
262 a1_auth = AuthUser(user_id=self.anon.user_id)
262 a1_auth = AuthUser(user_id=self.anon.user_id)
263 self.assertEqual(a1_auth.permissions['repositories_groups'],
263 self.assertEqual(a1_auth.permissions['repositories_groups'],
264 {u'group1': u'group.none', u'group2': u'group.none'})
264 {u'group1': u'group.none', u'group2': u'group.none'})
265
265
266 #grant permission for u2 !
266 #grant permission for u2 !
267 ReposGroupModel().grant_user_permission(repos_group=self.g1,
267 ReposGroupModel().grant_user_permission(repos_group=self.g1,
268 user=self.u2,
268 user=self.u2,
269 perm='group.read')
269 perm='group.read')
270 ReposGroupModel().grant_user_permission(repos_group=self.g2,
270 ReposGroupModel().grant_user_permission(repos_group=self.g2,
271 user=self.u2,
271 user=self.u2,
272 perm='group.read')
272 perm='group.read')
273 Session().commit()
273 Session().commit()
274 self.assertNotEqual(self.u1, self.u2)
274 self.assertNotEqual(self.u1, self.u2)
275 #u1 and anon should have not change perms while u2 should !
275 #u1 and anon should have not change perms while u2 should !
276 u1_auth = AuthUser(user_id=self.u1.user_id)
276 u1_auth = AuthUser(user_id=self.u1.user_id)
277 self.assertEqual(u1_auth.permissions['repositories_groups'],
277 self.assertEqual(u1_auth.permissions['repositories_groups'],
278 {u'group1': u'group.none', u'group2': u'group.none'})
278 {u'group1': u'group.none', u'group2': u'group.none'})
279
279
280 u2_auth = AuthUser(user_id=self.u2.user_id)
280 u2_auth = AuthUser(user_id=self.u2.user_id)
281 self.assertEqual(u2_auth.permissions['repositories_groups'],
281 self.assertEqual(u2_auth.permissions['repositories_groups'],
282 {u'group1': u'group.read', u'group2': u'group.read'})
282 {u'group1': u'group.read', u'group2': u'group.read'})
283
283
284 a1_auth = AuthUser(user_id=self.anon.user_id)
284 a1_auth = AuthUser(user_id=self.anon.user_id)
285 self.assertEqual(a1_auth.permissions['repositories_groups'],
285 self.assertEqual(a1_auth.permissions['repositories_groups'],
286 {u'group1': u'group.none', u'group2': u'group.none'})
286 {u'group1': u'group.none', u'group2': u'group.none'})
287
287
288 def test_repo_group_user_as_user_group_member(self):
288 def test_repo_group_user_as_user_group_member(self):
289 # create Group1
289 # create Group1
290 self.g1 = _make_group('group1', skip_if_exists=True)
290 self.g1 = _make_group('group1', skip_if_exists=True)
291 Session().commit()
291 Session().commit()
292 a1_auth = AuthUser(user_id=self.anon.user_id)
292 a1_auth = AuthUser(user_id=self.anon.user_id)
293
293
294 self.assertEqual(a1_auth.permissions['repositories_groups'],
294 self.assertEqual(a1_auth.permissions['repositories_groups'],
295 {u'group1': u'group.read'})
295 {u'group1': u'group.read'})
296
296
297 # set default permission to none
297 # set default permission to none
298 ReposGroupModel().grant_user_permission(repos_group=self.g1,
298 ReposGroupModel().grant_user_permission(repos_group=self.g1,
299 user=self.anon,
299 user=self.anon,
300 perm='group.none')
300 perm='group.none')
301 # make group
301 # make group
302 self.ug1 = UsersGroupModel().create('G1')
302 self.ug1 = UsersGroupModel().create('G1')
303 # add user to group
303 # add user to group
304 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
304 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
305 Session().commit()
305 Session().commit()
306
306
307 # check if user is in the group
307 # check if user is in the group
308 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
308 membrs = [x.user_id for x in UsersGroupModel().get(self.ug1.users_group_id).members]
309 self.assertEqual(membrs, [self.u1.user_id])
309 self.assertEqual(membrs, [self.u1.user_id])
310 # add some user to that group
310 # add some user to that group
311
311
312 # check his permissions
312 # check his permissions
313 a1_auth = AuthUser(user_id=self.anon.user_id)
313 a1_auth = AuthUser(user_id=self.anon.user_id)
314 self.assertEqual(a1_auth.permissions['repositories_groups'],
314 self.assertEqual(a1_auth.permissions['repositories_groups'],
315 {u'group1': u'group.none'})
315 {u'group1': u'group.none'})
316
316
317 u1_auth = AuthUser(user_id=self.u1.user_id)
317 u1_auth = AuthUser(user_id=self.u1.user_id)
318 self.assertEqual(u1_auth.permissions['repositories_groups'],
318 self.assertEqual(u1_auth.permissions['repositories_groups'],
319 {u'group1': u'group.none'})
319 {u'group1': u'group.none'})
320
320
321 # grant ug1 read permissions for
321 # grant ug1 read permissions for
322 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
322 ReposGroupModel().grant_users_group_permission(repos_group=self.g1,
323 group_name=self.ug1,
323 group_name=self.ug1,
324 perm='group.read')
324 perm='group.read')
325 Session().commit()
325 Session().commit()
326 # check if the
326 # check if the
327 obj = Session().query(UsersGroupRepoGroupToPerm)\
327 obj = Session().query(UsersGroupRepoGroupToPerm)\
328 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
328 .filter(UsersGroupRepoGroupToPerm.group == self.g1)\
329 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
329 .filter(UsersGroupRepoGroupToPerm.users_group == self.ug1)\
330 .scalar()
330 .scalar()
331 self.assertEqual(obj.permission.permission_name, 'group.read')
331 self.assertEqual(obj.permission.permission_name, 'group.read')
332
332
333 a1_auth = AuthUser(user_id=self.anon.user_id)
333 a1_auth = AuthUser(user_id=self.anon.user_id)
334
334
335 self.assertEqual(a1_auth.permissions['repositories_groups'],
335 self.assertEqual(a1_auth.permissions['repositories_groups'],
336 {u'group1': u'group.none'})
336 {u'group1': u'group.none'})
337
337
338 u1_auth = AuthUser(user_id=self.u1.user_id)
338 u1_auth = AuthUser(user_id=self.u1.user_id)
339 self.assertEqual(u1_auth.permissions['repositories_groups'],
339 self.assertEqual(u1_auth.permissions['repositories_groups'],
340 {u'group1': u'group.read'})
340 {u'group1': u'group.read'})
341
341
342 def test_inherited_permissions_from_default_on_user_enabled(self):
342 def test_inherited_permissions_from_default_on_user_enabled(self):
343 user_model = UserModel()
343 user_model = UserModel()
344 # enable fork and create on default user
344 # enable fork and create on default user
345 usr = 'default'
345 usr = 'default'
346 user_model.revoke_perm(usr, 'hg.create.none')
346 user_model.revoke_perm(usr, 'hg.create.none')
347 user_model.grant_perm(usr, 'hg.create.repository')
347 user_model.grant_perm(usr, 'hg.create.repository')
348 user_model.revoke_perm(usr, 'hg.fork.none')
348 user_model.revoke_perm(usr, 'hg.fork.none')
349 user_model.grant_perm(usr, 'hg.fork.repository')
349 user_model.grant_perm(usr, 'hg.fork.repository')
350 # make sure inherit flag is turned on
350 # make sure inherit flag is turned on
351 self.u1.inherit_default_permissions = True
351 self.u1.inherit_default_permissions = True
352 Session().commit()
352 Session().commit()
353 u1_auth = AuthUser(user_id=self.u1.user_id)
353 u1_auth = AuthUser(user_id=self.u1.user_id)
354 # this user will have inherited permissions from default user
354 # this user will have inherited permissions from default user
355 self.assertEqual(u1_auth.permissions['global'],
355 self.assertEqual(u1_auth.permissions['global'],
356 set(['hg.create.repository', 'hg.fork.repository',
356 set(['hg.create.repository', 'hg.fork.repository',
357 'hg.register.manual_activate',
357 'hg.register.manual_activate',
358 'repository.read', 'group.read']))
358 'repository.read', 'group.read']))
359
359
360 def test_inherited_permissions_from_default_on_user_disabled(self):
360 def test_inherited_permissions_from_default_on_user_disabled(self):
361 user_model = UserModel()
361 user_model = UserModel()
362 # disable fork and create on default user
362 # disable fork and create on default user
363 usr = 'default'
363 usr = 'default'
364 user_model.revoke_perm(usr, 'hg.create.repository')
364 user_model.revoke_perm(usr, 'hg.create.repository')
365 user_model.grant_perm(usr, 'hg.create.none')
365 user_model.grant_perm(usr, 'hg.create.none')
366 user_model.revoke_perm(usr, 'hg.fork.repository')
366 user_model.revoke_perm(usr, 'hg.fork.repository')
367 user_model.grant_perm(usr, 'hg.fork.none')
367 user_model.grant_perm(usr, 'hg.fork.none')
368 # make sure inherit flag is turned on
368 # make sure inherit flag is turned on
369 self.u1.inherit_default_permissions = True
369 self.u1.inherit_default_permissions = True
370 Session().commit()
370 Session().commit()
371 u1_auth = AuthUser(user_id=self.u1.user_id)
371 u1_auth = AuthUser(user_id=self.u1.user_id)
372 # this user will have inherited permissions from default user
372 # this user will have inherited permissions from default user
373 self.assertEqual(u1_auth.permissions['global'],
373 self.assertEqual(u1_auth.permissions['global'],
374 set(['hg.create.none', 'hg.fork.none',
374 set(['hg.create.none', 'hg.fork.none',
375 'hg.register.manual_activate',
375 'hg.register.manual_activate',
376 'repository.read', 'group.read']))
376 'repository.read', 'group.read']))
377
377
378 def test_non_inherited_permissions_from_default_on_user_enabled(self):
378 def test_non_inherited_permissions_from_default_on_user_enabled(self):
379 user_model = UserModel()
379 user_model = UserModel()
380 # enable fork and create on default user
380 # enable fork and create on default user
381 usr = 'default'
381 usr = 'default'
382 user_model.revoke_perm(usr, 'hg.create.none')
382 user_model.revoke_perm(usr, 'hg.create.none')
383 user_model.grant_perm(usr, 'hg.create.repository')
383 user_model.grant_perm(usr, 'hg.create.repository')
384 user_model.revoke_perm(usr, 'hg.fork.none')
384 user_model.revoke_perm(usr, 'hg.fork.none')
385 user_model.grant_perm(usr, 'hg.fork.repository')
385 user_model.grant_perm(usr, 'hg.fork.repository')
386
386
387 #disable global perms on specific user
387 #disable global perms on specific user
388 user_model.revoke_perm(self.u1, 'hg.create.repository')
388 user_model.revoke_perm(self.u1, 'hg.create.repository')
389 user_model.grant_perm(self.u1, 'hg.create.none')
389 user_model.grant_perm(self.u1, 'hg.create.none')
390 user_model.revoke_perm(self.u1, 'hg.fork.repository')
390 user_model.revoke_perm(self.u1, 'hg.fork.repository')
391 user_model.grant_perm(self.u1, 'hg.fork.none')
391 user_model.grant_perm(self.u1, 'hg.fork.none')
392
392
393 # make sure inherit flag is turned off
393 # make sure inherit flag is turned off
394 self.u1.inherit_default_permissions = False
394 self.u1.inherit_default_permissions = False
395 Session().commit()
395 Session().commit()
396 u1_auth = AuthUser(user_id=self.u1.user_id)
396 u1_auth = AuthUser(user_id=self.u1.user_id)
397 # this user will have non inherited permissions from he's
397 # this user will have non inherited permissions from he's
398 # explicitly set permissions
398 # explicitly set permissions
399 self.assertEqual(u1_auth.permissions['global'],
399 self.assertEqual(u1_auth.permissions['global'],
400 set(['hg.create.none', 'hg.fork.none',
400 set(['hg.create.none', 'hg.fork.none',
401 'hg.register.manual_activate',
401 'hg.register.manual_activate',
402 'repository.read', 'group.read']))
402 'repository.read', 'group.read']))
403
403
404 def test_non_inherited_permissions_from_default_on_user_disabled(self):
404 def test_non_inherited_permissions_from_default_on_user_disabled(self):
405 user_model = UserModel()
405 user_model = UserModel()
406 # disable fork and create on default user
406 # disable fork and create on default user
407 usr = 'default'
407 usr = 'default'
408 user_model.revoke_perm(usr, 'hg.create.repository')
408 user_model.revoke_perm(usr, 'hg.create.repository')
409 user_model.grant_perm(usr, 'hg.create.none')
409 user_model.grant_perm(usr, 'hg.create.none')
410 user_model.revoke_perm(usr, 'hg.fork.repository')
410 user_model.revoke_perm(usr, 'hg.fork.repository')
411 user_model.grant_perm(usr, 'hg.fork.none')
411 user_model.grant_perm(usr, 'hg.fork.none')
412
412
413 #enable global perms on specific user
413 #enable global perms on specific user
414 user_model.revoke_perm(self.u1, 'hg.create.none')
414 user_model.revoke_perm(self.u1, 'hg.create.none')
415 user_model.grant_perm(self.u1, 'hg.create.repository')
415 user_model.grant_perm(self.u1, 'hg.create.repository')
416 user_model.revoke_perm(self.u1, 'hg.fork.none')
416 user_model.revoke_perm(self.u1, 'hg.fork.none')
417 user_model.grant_perm(self.u1, 'hg.fork.repository')
417 user_model.grant_perm(self.u1, 'hg.fork.repository')
418
418
419 # make sure inherit flag is turned off
419 # make sure inherit flag is turned off
420 self.u1.inherit_default_permissions = False
420 self.u1.inherit_default_permissions = False
421 Session().commit()
421 Session().commit()
422 u1_auth = AuthUser(user_id=self.u1.user_id)
422 u1_auth = AuthUser(user_id=self.u1.user_id)
423 # this user will have non inherited permissions from he's
423 # this user will have non inherited permissions from he's
424 # explicitly set permissions
424 # explicitly set permissions
425 self.assertEqual(u1_auth.permissions['global'],
425 self.assertEqual(u1_auth.permissions['global'],
426 set(['hg.create.repository', 'hg.fork.repository',
426 set(['hg.create.repository', 'hg.fork.repository',
427 'hg.register.manual_activate',
427 'hg.register.manual_activate',
428 'repository.read', 'group.read']))
428 'repository.read', 'group.read']))
429
429
430 def test_owner_permissions_doesnot_get_overwritten_by_group(self):
430 def test_owner_permissions_doesnot_get_overwritten_by_group(self):
431 #create repo as USER,
431 #create repo as USER,
432 self.test_repo = repo = RepoModel().create_repo(repo_name='myownrepo',
432 self.test_repo = repo = RepoModel().create_repo(repo_name='myownrepo',
433 repo_type='hg',
433 repo_type='hg',
434 description='desc',
434 description='desc',
435 owner=self.u1)
435 owner=self.u1)
436
436
437 Session().commit()
437 Session().commit()
438 #he has permissions of admin as owner
438 #he has permissions of admin as owner
439 u1_auth = AuthUser(user_id=self.u1.user_id)
439 u1_auth = AuthUser(user_id=self.u1.user_id)
440 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
440 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
441 'repository.admin')
441 'repository.admin')
442 #set his permission as users group, he should still be admin
442 #set his permission as user group, he should still be admin
443 self.ug1 = UsersGroupModel().create('G1')
443 self.ug1 = UsersGroupModel().create('G1')
444 # add user to group
444 # add user to group
445 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
445 UsersGroupModel().add_user_to_group(self.ug1, self.u1)
446 RepoModel().grant_users_group_permission(repo, group_name=self.ug1,
446 RepoModel().grant_users_group_permission(repo, group_name=self.ug1,
447 perm='repository.none')
447 perm='repository.none')
448
448
449 Session().commit()
449 Session().commit()
450 u1_auth = AuthUser(user_id=self.u1.user_id)
450 u1_auth = AuthUser(user_id=self.u1.user_id)
451 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
451 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
452 'repository.admin')
452 'repository.admin')
453
453
454 def test_owner_permissions_doesnot_get_overwritten_by_others(self):
454 def test_owner_permissions_doesnot_get_overwritten_by_others(self):
455 #create repo as USER,
455 #create repo as USER,
456 self.test_repo = repo = RepoModel().create_repo(repo_name='myownrepo',
456 self.test_repo = repo = RepoModel().create_repo(repo_name='myownrepo',
457 repo_type='hg',
457 repo_type='hg',
458 description='desc',
458 description='desc',
459 owner=self.u1)
459 owner=self.u1)
460
460
461 Session().commit()
461 Session().commit()
462 #he has permissions of admin as owner
462 #he has permissions of admin as owner
463 u1_auth = AuthUser(user_id=self.u1.user_id)
463 u1_auth = AuthUser(user_id=self.u1.user_id)
464 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
464 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
465 'repository.admin')
465 'repository.admin')
466 #set his permission as user, he should still be admin
466 #set his permission as user, he should still be admin
467 RepoModel().grant_user_permission(repo, user=self.u1,
467 RepoModel().grant_user_permission(repo, user=self.u1,
468 perm='repository.none')
468 perm='repository.none')
469 Session().commit()
469 Session().commit()
470 u1_auth = AuthUser(user_id=self.u1.user_id)
470 u1_auth = AuthUser(user_id=self.u1.user_id)
471 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
471 self.assertEqual(u1_auth.permissions['repositories']['myownrepo'],
472 'repository.admin')
472 'repository.admin')
@@ -1,124 +1,124 b''
1 import unittest
1 import unittest
2 from rhodecode.tests import *
2 from rhodecode.tests import *
3
3
4 from rhodecode.model.db import User, UsersGroup, UsersGroupMember, UserEmailMap,\
4 from rhodecode.model.db import User, UsersGroup, UsersGroupMember, UserEmailMap,\
5 Permission
5 Permission
6 from rhodecode.model.user import UserModel
6 from rhodecode.model.user import UserModel
7
7
8 from rhodecode.model.meta import Session
8 from rhodecode.model.meta import Session
9 from rhodecode.model.users_group import UsersGroupModel
9 from rhodecode.model.users_group import UsersGroupModel
10
10
11
11
12 class TestUser(unittest.TestCase):
12 class TestUser(unittest.TestCase):
13 def __init__(self, methodName='runTest'):
13 def __init__(self, methodName='runTest'):
14 Session.remove()
14 Session.remove()
15 super(TestUser, self).__init__(methodName=methodName)
15 super(TestUser, self).__init__(methodName=methodName)
16
16
17 def test_create_and_remove(self):
17 def test_create_and_remove(self):
18 usr = UserModel().create_or_update(username=u'test_user',
18 usr = UserModel().create_or_update(username=u'test_user',
19 password=u'qweqwe',
19 password=u'qweqwe',
20 email=u'u232@rhodecode.org',
20 email=u'u232@rhodecode.org',
21 firstname=u'u1', lastname=u'u1')
21 firstname=u'u1', lastname=u'u1')
22 Session().commit()
22 Session().commit()
23 self.assertEqual(User.get_by_username(u'test_user'), usr)
23 self.assertEqual(User.get_by_username(u'test_user'), usr)
24
24
25 # make users group
25 # make user group
26 users_group = UsersGroupModel().create('some_example_group')
26 users_group = UsersGroupModel().create('some_example_group')
27 Session().commit()
27 Session().commit()
28
28
29 UsersGroupModel().add_user_to_group(users_group, usr)
29 UsersGroupModel().add_user_to_group(users_group, usr)
30 Session().commit()
30 Session().commit()
31
31
32 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
32 self.assertEqual(UsersGroup.get(users_group.users_group_id), users_group)
33 self.assertEqual(UsersGroupMember.query().count(), 1)
33 self.assertEqual(UsersGroupMember.query().count(), 1)
34 UserModel().delete(usr.user_id)
34 UserModel().delete(usr.user_id)
35 Session().commit()
35 Session().commit()
36
36
37 self.assertEqual(UsersGroupMember.query().all(), [])
37 self.assertEqual(UsersGroupMember.query().all(), [])
38
38
39 def test_additonal_email_as_main(self):
39 def test_additonal_email_as_main(self):
40 usr = UserModel().create_or_update(username=u'test_user',
40 usr = UserModel().create_or_update(username=u'test_user',
41 password=u'qweqwe',
41 password=u'qweqwe',
42 email=u'main_email@rhodecode.org',
42 email=u'main_email@rhodecode.org',
43 firstname=u'u1', lastname=u'u1')
43 firstname=u'u1', lastname=u'u1')
44 Session().commit()
44 Session().commit()
45
45
46 def do():
46 def do():
47 m = UserEmailMap()
47 m = UserEmailMap()
48 m.email = u'main_email@rhodecode.org'
48 m.email = u'main_email@rhodecode.org'
49 m.user = usr
49 m.user = usr
50 Session().add(m)
50 Session().add(m)
51 Session().commit()
51 Session().commit()
52 self.assertRaises(AttributeError, do)
52 self.assertRaises(AttributeError, do)
53
53
54 UserModel().delete(usr.user_id)
54 UserModel().delete(usr.user_id)
55 Session().commit()
55 Session().commit()
56
56
57 def test_extra_email_map(self):
57 def test_extra_email_map(self):
58 usr = UserModel().create_or_update(username=u'test_user',
58 usr = UserModel().create_or_update(username=u'test_user',
59 password=u'qweqwe',
59 password=u'qweqwe',
60 email=u'main_email@rhodecode.org',
60 email=u'main_email@rhodecode.org',
61 firstname=u'u1', lastname=u'u1')
61 firstname=u'u1', lastname=u'u1')
62 Session().commit()
62 Session().commit()
63
63
64 m = UserEmailMap()
64 m = UserEmailMap()
65 m.email = u'main_email2@rhodecode.org'
65 m.email = u'main_email2@rhodecode.org'
66 m.user = usr
66 m.user = usr
67 Session().add(m)
67 Session().add(m)
68 Session().commit()
68 Session().commit()
69
69
70 u = User.get_by_email(email='main_email@rhodecode.org')
70 u = User.get_by_email(email='main_email@rhodecode.org')
71 self.assertEqual(usr.user_id, u.user_id)
71 self.assertEqual(usr.user_id, u.user_id)
72 self.assertEqual(usr.username, u.username)
72 self.assertEqual(usr.username, u.username)
73
73
74 u = User.get_by_email(email='main_email2@rhodecode.org')
74 u = User.get_by_email(email='main_email2@rhodecode.org')
75 self.assertEqual(usr.user_id, u.user_id)
75 self.assertEqual(usr.user_id, u.user_id)
76 self.assertEqual(usr.username, u.username)
76 self.assertEqual(usr.username, u.username)
77 u = User.get_by_email(email='main_email3@rhodecode.org')
77 u = User.get_by_email(email='main_email3@rhodecode.org')
78 self.assertEqual(None, u)
78 self.assertEqual(None, u)
79
79
80 UserModel().delete(usr.user_id)
80 UserModel().delete(usr.user_id)
81 Session().commit()
81 Session().commit()
82
82
83
83
84 class TestUsers(unittest.TestCase):
84 class TestUsers(unittest.TestCase):
85
85
86 def __init__(self, methodName='runTest'):
86 def __init__(self, methodName='runTest'):
87 super(TestUsers, self).__init__(methodName=methodName)
87 super(TestUsers, self).__init__(methodName=methodName)
88
88
89 def setUp(self):
89 def setUp(self):
90 self.u1 = UserModel().create_or_update(username=u'u1',
90 self.u1 = UserModel().create_or_update(username=u'u1',
91 password=u'qweqwe',
91 password=u'qweqwe',
92 email=u'u1@rhodecode.org',
92 email=u'u1@rhodecode.org',
93 firstname=u'u1', lastname=u'u1')
93 firstname=u'u1', lastname=u'u1')
94
94
95 def tearDown(self):
95 def tearDown(self):
96 perm = Permission.query().all()
96 perm = Permission.query().all()
97 for p in perm:
97 for p in perm:
98 UserModel().revoke_perm(self.u1, p)
98 UserModel().revoke_perm(self.u1, p)
99
99
100 UserModel().delete(self.u1)
100 UserModel().delete(self.u1)
101 Session().commit()
101 Session().commit()
102
102
103 def test_add_perm(self):
103 def test_add_perm(self):
104 perm = Permission.query().all()[0]
104 perm = Permission.query().all()[0]
105 UserModel().grant_perm(self.u1, perm)
105 UserModel().grant_perm(self.u1, perm)
106 Session().commit()
106 Session().commit()
107 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
107 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
108
108
109 def test_has_perm(self):
109 def test_has_perm(self):
110 perm = Permission.query().all()
110 perm = Permission.query().all()
111 for p in perm:
111 for p in perm:
112 has_p = UserModel().has_perm(self.u1, p)
112 has_p = UserModel().has_perm(self.u1, p)
113 self.assertEqual(False, has_p)
113 self.assertEqual(False, has_p)
114
114
115 def test_revoke_perm(self):
115 def test_revoke_perm(self):
116 perm = Permission.query().all()[0]
116 perm = Permission.query().all()[0]
117 UserModel().grant_perm(self.u1, perm)
117 UserModel().grant_perm(self.u1, perm)
118 Session().commit()
118 Session().commit()
119 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
119 self.assertEqual(UserModel().has_perm(self.u1, perm), True)
120
120
121 #revoke
121 #revoke
122 UserModel().revoke_perm(self.u1, perm)
122 UserModel().revoke_perm(self.u1, perm)
123 Session().commit()
123 Session().commit()
124 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
124 self.assertEqual(UserModel().has_perm(self.u1, perm), False)
General Comments 0
You need to be logged in to leave comments. Login now