##// END OF EJS Templates
more work on improving info logging
marcink -
r2025:7e979933 beta
parent child Browse files
Show More
@@ -1,92 +1,92 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2 """
2 """
3 rhodecode.__init__
3 rhodecode.__init__
4 ~~~~~~~~~~~~~~~~~~
4 ~~~~~~~~~~~~~~~~~~
5
5
6 RhodeCode, a web based repository management based on pylons
6 RhodeCode, a web based repository management based on pylons
7 versioning implementation: http://semver.org/
7 versioning implementation: http://semver.org/
8
8
9 :created_on: Apr 9, 2010
9 :created_on: Apr 9, 2010
10 :author: marcink
10 :author: marcink
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
11 :copyright: (C) 2010-2012 Marcin Kuzminski <marcin@python-works.com>
12 :license: GPLv3, see COPYING for more details.
12 :license: GPLv3, see COPYING for more details.
13 """
13 """
14 # This program is free software: you can redistribute it and/or modify
14 # This program is free software: you can redistribute it and/or modify
15 # it under the terms of the GNU General Public License as published by
15 # it under the terms of the GNU General Public License as published by
16 # the Free Software Foundation, either version 3 of the License, or
16 # the Free Software Foundation, either version 3 of the License, or
17 # (at your option) any later version.
17 # (at your option) any later version.
18 #
18 #
19 # This program is distributed in the hope that it will be useful,
19 # This program is distributed in the hope that it will be useful,
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # but WITHOUT ANY WARRANTY; without even the implied warranty of
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 # GNU General Public License for more details.
22 # GNU General Public License for more details.
23 #
23 #
24 # You should have received a copy of the GNU General Public License
24 # You should have received a copy of the GNU General Public License
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 # along with this program. If not, see <http://www.gnu.org/licenses/>.
26 import sys
26 import sys
27 import platform
27 import platform
28
28
29 VERSION = (1, 3, 0, 'beta')
29 VERSION = (1, 3, 0, 'beta')
30 __version__ = '.'.join((str(each) for each in VERSION[:4]))
30 __version__ = '.'.join((str(each) for each in VERSION[:4]))
31 __dbversion__ = 5 # defines current db version for migrations
31 __dbversion__ = 5 # defines current db version for migrations
32 __platform__ = platform.system()
32 __platform__ = platform.system()
33 __license__ = 'GPLv3'
33 __license__ = 'GPLv3'
34 __py_version__ = sys.version_info
34 __py_version__ = sys.version_info
35
35
36 PLATFORM_WIN = ('Windows')
36 PLATFORM_WIN = ('Windows')
37 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
37 PLATFORM_OTHERS = ('Linux', 'Darwin', 'FreeBSD', 'OpenBSD', 'SunOS')
38
38
39 requirements = [
39 requirements = [
40 "Pylons==1.0.0",
40 "Pylons==1.0.0",
41 "Beaker==1.6.2",
41 "Beaker==1.6.2",
42 "WebHelpers>=1.2",
42 "WebHelpers>=1.2",
43 "formencode==1.2.4",
43 "formencode==1.2.4",
44 "SQLAlchemy==0.7.4",
44 "SQLAlchemy==0.7.4",
45 "Mako==0.5.0",
45 "Mako==0.5.0",
46 "pygments>=1.4",
46 "pygments>=1.4",
47 "whoosh>=2.3.0,<2.4",
47 "whoosh>=2.3.0,<2.4",
48 "celery>=2.2.5,<2.3",
48 "celery>=2.2.5,<2.3",
49 "babel",
49 "babel",
50 "python-dateutil>=1.5.0,<2.0.0",
50 "python-dateutil>=1.5.0,<2.0.0",
51 "dulwich>=0.8.0,<0.9.0",
51 "dulwich>=0.8.0,<0.9.0",
52 "webob==1.0.8",
52 "webob==1.0.8",
53 "markdown==2.1.1",
53 "markdown==2.1.1",
54 "docutils==0.8.1",
54 "docutils==0.8.1",
55 ]
55 ]
56
56
57 if __py_version__ < (2, 6):
57 if __py_version__ < (2, 6):
58 requirements.append("simplejson")
58 requirements.append("simplejson")
59 requirements.append("pysqlite")
59 requirements.append("pysqlite")
60
60
61 if __platform__ in PLATFORM_WIN:
61 if __platform__ in PLATFORM_WIN:
62 requirements.append("mercurial>=2.1,<2.2")
62 requirements.append("mercurial>=2.1,<2.2")
63 else:
63 else:
64 requirements.append("py-bcrypt")
64 requirements.append("py-bcrypt")
65 requirements.append("mercurial>=2.1,<2.2")
65 requirements.append("mercurial>=2.1,<2.2")
66
66
67
67
68 try:
68 try:
69 from rhodecode.lib import get_current_revision
69 from rhodecode.lib import get_current_revision
70 _rev = get_current_revision()
70 _rev = get_current_revision()
71 except ImportError:
71 except ImportError:
72 # this is needed when doing some setup.py operations
72 # this is needed when doing some setup.py operations
73 _rev = False
73 _rev = False
74
74
75 if len(VERSION) > 3 and _rev:
75 if len(VERSION) > 3 and _rev:
76 __version__ += ' [rev:%s]' % _rev[0]
76 __version__ += ' [rev:%s]' % _rev[0]
77
77
78
78
79 def get_version():
79 def get_version():
80 """Returns shorter version (digit parts only) as string."""
80 """Returns shorter version (digit parts only) as string."""
81
81
82 return '.'.join((str(each) for each in VERSION[:3]))
82 return '.'.join((str(each) for each in VERSION[:3]))
83
83
84 BACKENDS = {
84 BACKENDS = {
85 'hg': 'Mercurial repository',
85 'hg': 'Mercurial repository',
86 'git': 'Git repository',
86 'git': 'Git repository',
87 }
87 }
88
88
89 CELERY_ON = False
89 CELERY_ON = False
90
90
91 # link to config for pylons
91 # link to config for pylons
92 CONFIG = None
92 CONFIG = {}
@@ -1,781 +1,785 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
37
38 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
39 from rhodecode.model.meta import Session
39 from rhodecode.model.meta import Session
40
40
41 if __platform__ in PLATFORM_WIN:
41 if __platform__ in PLATFORM_WIN:
42 from hashlib import sha256
42 from hashlib import sha256
43 if __platform__ in PLATFORM_OTHERS:
43 if __platform__ in PLATFORM_OTHERS:
44 import bcrypt
44 import bcrypt
45
45
46 from rhodecode.lib import str2bool, safe_unicode
46 from rhodecode.lib import str2bool, safe_unicode
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
47 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
48 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
48 from rhodecode.lib.utils import get_repo_slug, get_repos_group_slug
49 from rhodecode.lib.auth_ldap import AuthLdap
49 from rhodecode.lib.auth_ldap import AuthLdap
50
50
51 from rhodecode.model import meta
51 from rhodecode.model import meta
52 from rhodecode.model.user import UserModel
52 from rhodecode.model.user import UserModel
53 from rhodecode.model.db import Permission, RhodeCodeSetting, User
53 from rhodecode.model.db import Permission, RhodeCodeSetting, User
54
54
55 log = logging.getLogger(__name__)
55 log = logging.getLogger(__name__)
56
56
57
57
58 class PasswordGenerator(object):
58 class PasswordGenerator(object):
59 """
59 """
60 This is a simple class for generating password from different sets of
60 This is a simple class for generating password from different sets of
61 characters
61 characters
62 usage::
62 usage::
63
63
64 passwd_gen = PasswordGenerator()
64 passwd_gen = PasswordGenerator()
65 #print 8-letter password containing only big and small letters
65 #print 8-letter password containing only big and small letters
66 of alphabet
66 of alphabet
67 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
67 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
68 """
68 """
69 ALPHABETS_NUM = r'''1234567890'''
69 ALPHABETS_NUM = r'''1234567890'''
70 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
70 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
71 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
71 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
72 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
72 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
73 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
73 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
74 + ALPHABETS_NUM + ALPHABETS_SPECIAL
74 + ALPHABETS_NUM + ALPHABETS_SPECIAL
75 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
75 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
76 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
76 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
77 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
77 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
78 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
78 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
79
79
80 def __init__(self, passwd=''):
80 def __init__(self, passwd=''):
81 self.passwd = passwd
81 self.passwd = passwd
82
82
83 def gen_password(self, length, type_=None):
83 def gen_password(self, length, type_=None):
84 if type_ is None:
84 if type_ is None:
85 type_ = self.ALPHABETS_FULL
85 type_ = self.ALPHABETS_FULL
86 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
86 self.passwd = ''.join([random.choice(type_) for _ in xrange(length)])
87 return self.passwd
87 return self.passwd
88
88
89
89
90 class RhodeCodeCrypto(object):
90 class RhodeCodeCrypto(object):
91
91
92 @classmethod
92 @classmethod
93 def hash_string(cls, str_):
93 def hash_string(cls, str_):
94 """
94 """
95 Cryptographic function used for password hashing based on pybcrypt
95 Cryptographic function used for password hashing based on pybcrypt
96 or pycrypto in windows
96 or pycrypto in windows
97
97
98 :param password: password to hash
98 :param password: password to hash
99 """
99 """
100 if __platform__ in PLATFORM_WIN:
100 if __platform__ in PLATFORM_WIN:
101 return sha256(str_).hexdigest()
101 return sha256(str_).hexdigest()
102 elif __platform__ in PLATFORM_OTHERS:
102 elif __platform__ in PLATFORM_OTHERS:
103 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
103 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
104 else:
104 else:
105 raise Exception('Unknown or unsupported platform %s' \
105 raise Exception('Unknown or unsupported platform %s' \
106 % __platform__)
106 % __platform__)
107
107
108 @classmethod
108 @classmethod
109 def hash_check(cls, password, hashed):
109 def hash_check(cls, password, hashed):
110 """
110 """
111 Checks matching password with it's hashed value, runs different
111 Checks matching password with it's hashed value, runs different
112 implementation based on platform it runs on
112 implementation based on platform it runs on
113
113
114 :param password: password
114 :param password: password
115 :param hashed: password in hashed form
115 :param hashed: password in hashed form
116 """
116 """
117
117
118 if __platform__ in PLATFORM_WIN:
118 if __platform__ in PLATFORM_WIN:
119 return sha256(password).hexdigest() == hashed
119 return sha256(password).hexdigest() == hashed
120 elif __platform__ in PLATFORM_OTHERS:
120 elif __platform__ in PLATFORM_OTHERS:
121 return bcrypt.hashpw(password, hashed) == hashed
121 return bcrypt.hashpw(password, hashed) == hashed
122 else:
122 else:
123 raise Exception('Unknown or unsupported platform %s' \
123 raise Exception('Unknown or unsupported platform %s' \
124 % __platform__)
124 % __platform__)
125
125
126
126
127 def get_crypt_password(password):
127 def get_crypt_password(password):
128 return RhodeCodeCrypto.hash_string(password)
128 return RhodeCodeCrypto.hash_string(password)
129
129
130
130
131 def check_password(password, hashed):
131 def check_password(password, hashed):
132 return RhodeCodeCrypto.hash_check(password, hashed)
132 return RhodeCodeCrypto.hash_check(password, hashed)
133
133
134
134
135 def generate_api_key(str_, salt=None):
135 def generate_api_key(str_, salt=None):
136 """
136 """
137 Generates API KEY from given string
137 Generates API KEY from given string
138
138
139 :param str_:
139 :param str_:
140 :param salt:
140 :param salt:
141 """
141 """
142
142
143 if salt is None:
143 if salt is None:
144 salt = _RandomNameSequence().next()
144 salt = _RandomNameSequence().next()
145
145
146 return hashlib.sha1(str_ + salt).hexdigest()
146 return hashlib.sha1(str_ + salt).hexdigest()
147
147
148
148
149 def authfunc(environ, username, password):
149 def authfunc(environ, username, password):
150 """
150 """
151 Dummy authentication wrapper function used in Mercurial and Git for
151 Dummy authentication wrapper function used in Mercurial and Git for
152 access control.
152 access control.
153
153
154 :param environ: needed only for using in Basic auth
154 :param environ: needed only for using in Basic auth
155 """
155 """
156 return authenticate(username, password)
156 return authenticate(username, password)
157
157
158
158
159 def authenticate(username, password):
159 def authenticate(username, password):
160 """
160 """
161 Authentication function used for access control,
161 Authentication function used for access control,
162 firstly checks for db authentication then if ldap is enabled for ldap
162 firstly checks for db authentication then if ldap is enabled for ldap
163 authentication, also creates ldap user if not in database
163 authentication, also creates ldap user if not in database
164
164
165 :param username: username
165 :param username: username
166 :param password: password
166 :param password: password
167 """
167 """
168
168
169 user_model = UserModel()
169 user_model = UserModel()
170 user = User.get_by_username(username)
170 user = User.get_by_username(username)
171
171
172 log.debug('Authenticating user using RhodeCode account')
172 log.debug('Authenticating user using RhodeCode account')
173 if user is not None and not user.ldap_dn:
173 if user is not None and not user.ldap_dn:
174 if user.active:
174 if user.active:
175 if user.username == 'default' and user.active:
175 if user.username == 'default' and user.active:
176 log.info('user %s authenticated correctly as anonymous user',
176 log.info('user %s authenticated correctly as anonymous user' %
177 username)
177 username)
178 return True
178 return True
179
179
180 elif user.username == username and check_password(password,
180 elif user.username == username and check_password(password,
181 user.password):
181 user.password):
182 log.info('user %s authenticated correctly' % username)
182 log.info('user %s authenticated correctly' % username)
183 return True
183 return True
184 else:
184 else:
185 log.warning('user %s is disabled' % username)
185 log.warning('user %s tried auth but is disabled' % username)
186
186
187 else:
187 else:
188 log.debug('Regular authentication failed')
188 log.debug('Regular authentication failed')
189 user_obj = User.get_by_username(username, case_insensitive=True)
189 user_obj = User.get_by_username(username, case_insensitive=True)
190
190
191 if user_obj is not None and not user_obj.ldap_dn:
191 if user_obj is not None and not user_obj.ldap_dn:
192 log.debug('this user already exists as non ldap')
192 log.debug('this user already exists as non ldap')
193 return False
193 return False
194
194
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
195 ldap_settings = RhodeCodeSetting.get_ldap_settings()
196 #======================================================================
196 #======================================================================
197 # FALLBACK TO LDAP AUTH IF ENABLE
197 # FALLBACK TO LDAP AUTH IF ENABLE
198 #======================================================================
198 #======================================================================
199 if str2bool(ldap_settings.get('ldap_active')):
199 if str2bool(ldap_settings.get('ldap_active')):
200 log.debug("Authenticating user using ldap")
200 log.debug("Authenticating user using ldap")
201 kwargs = {
201 kwargs = {
202 'server': ldap_settings.get('ldap_host', ''),
202 'server': ldap_settings.get('ldap_host', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
203 'base_dn': ldap_settings.get('ldap_base_dn', ''),
204 'port': ldap_settings.get('ldap_port'),
204 'port': ldap_settings.get('ldap_port'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
205 'bind_dn': ldap_settings.get('ldap_dn_user'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
206 'bind_pass': ldap_settings.get('ldap_dn_pass'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
207 'tls_kind': ldap_settings.get('ldap_tls_kind'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
208 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
209 'ldap_filter': ldap_settings.get('ldap_filter'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
210 'search_scope': ldap_settings.get('ldap_search_scope'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
211 'attr_login': ldap_settings.get('ldap_attr_login'),
212 'ldap_version': 3,
212 'ldap_version': 3,
213 }
213 }
214 log.debug('Checking for ldap authentication')
214 log.debug('Checking for ldap authentication')
215 try:
215 try:
216 aldap = AuthLdap(**kwargs)
216 aldap = AuthLdap(**kwargs)
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
217 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
218 password)
218 password)
219 log.debug('Got ldap DN response %s' % user_dn)
219 log.debug('Got ldap DN response %s' % user_dn)
220
220
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
221 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
222 .get(k), [''])[0]
222 .get(k), [''])[0]
223
223
224 user_attrs = {
224 user_attrs = {
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
225 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
226 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
227 'email': get_ldap_attr('ldap_attr_email'),
227 'email': get_ldap_attr('ldap_attr_email'),
228 }
228 }
229
229
230 # don't store LDAP password since we don't need it. Override
230 # don't store LDAP password since we don't need it. Override
231 # with some random generated password
231 # with some random generated password
232 _password = PasswordGenerator().gen_password(length=8)
232 _password = PasswordGenerator().gen_password(length=8)
233 # create this user on the fly if it doesn't exist in rhodecode
233 # create this user on the fly if it doesn't exist in rhodecode
234 # database
234 # database
235 if user_model.create_ldap(username, _password, user_dn,
235 if user_model.create_ldap(username, _password, user_dn,
236 user_attrs):
236 user_attrs):
237 log.info('created new ldap user %s' % username)
237 log.info('created new ldap user %s' % username)
238
238
239 Session.commit()
239 Session.commit()
240 return True
240 return True
241 except (LdapUsernameError, LdapPasswordError,):
241 except (LdapUsernameError, LdapPasswordError,):
242 pass
242 pass
243 except (Exception,):
243 except (Exception,):
244 log.error(traceback.format_exc())
244 log.error(traceback.format_exc())
245 pass
245 pass
246 return False
246 return False
247
247
248
248
249 def login_container_auth(username):
249 def login_container_auth(username):
250 user = User.get_by_username(username)
250 user = User.get_by_username(username)
251 if user is None:
251 if user is None:
252 user_attrs = {
252 user_attrs = {
253 'name': username,
253 'name': username,
254 'lastname': None,
254 'lastname': None,
255 'email': None,
255 'email': None,
256 }
256 }
257 user = UserModel().create_for_container_auth(username, user_attrs)
257 user = UserModel().create_for_container_auth(username, user_attrs)
258 if not user:
258 if not user:
259 return None
259 return None
260 log.info('User %s was created by container authentication' % username)
260 log.info('User %s was created by container authentication' % username)
261
261
262 if not user.active:
262 if not user.active:
263 return None
263 return None
264
264
265 user.update_lastlogin()
265 user.update_lastlogin()
266 Session.commit()
266 Session.commit()
267
267
268 log.debug('User %s is now logged in by container authentication',
268 log.debug('User %s is now logged in by container authentication',
269 user.username)
269 user.username)
270 return user
270 return user
271
271
272
272
273 def get_container_username(environ, config):
273 def get_container_username(environ, config):
274 username = None
274 username = None
275
275
276 if str2bool(config.get('container_auth_enabled', False)):
276 if str2bool(config.get('container_auth_enabled', False)):
277 from paste.httpheaders import REMOTE_USER
277 from paste.httpheaders import REMOTE_USER
278 username = REMOTE_USER(environ)
278 username = REMOTE_USER(environ)
279
279
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
280 if not username and str2bool(config.get('proxypass_auth_enabled', False)):
281 username = environ.get('HTTP_X_FORWARDED_USER')
281 username = environ.get('HTTP_X_FORWARDED_USER')
282
282
283 if username:
283 if username:
284 # Removing realm and domain from username
284 # Removing realm and domain from username
285 username = username.partition('@')[0]
285 username = username.partition('@')[0]
286 username = username.rpartition('\\')[2]
286 username = username.rpartition('\\')[2]
287 log.debug('Received username %s from container' % username)
287 log.debug('Received username %s from container' % username)
288
288
289 return username
289 return username
290
290
291
291
292 class AuthUser(object):
292 class AuthUser(object):
293 """
293 """
294 A simple object that handles all attributes of user in RhodeCode
294 A simple object that handles all attributes of user in RhodeCode
295
295
296 It does lookup based on API key,given user, or user present in session
296 It does lookup based on API key,given user, or user present in session
297 Then it fills all required information for such user. It also checks if
297 Then it fills all required information for such user. It also checks if
298 anonymous access is enabled and if so, it returns default user as logged
298 anonymous access is enabled and if so, it returns default user as logged
299 in
299 in
300 """
300 """
301
301
302 def __init__(self, user_id=None, api_key=None, username=None):
302 def __init__(self, user_id=None, api_key=None, username=None):
303
303
304 self.user_id = user_id
304 self.user_id = user_id
305 self.api_key = None
305 self.api_key = None
306 self.username = username
306 self.username = username
307
307
308 self.name = ''
308 self.name = ''
309 self.lastname = ''
309 self.lastname = ''
310 self.email = ''
310 self.email = ''
311 self.is_authenticated = False
311 self.is_authenticated = False
312 self.admin = False
312 self.admin = False
313 self.permissions = {}
313 self.permissions = {}
314 self._api_key = api_key
314 self._api_key = api_key
315 self.propagate_data()
315 self.propagate_data()
316 self._instance = None
316 self._instance = None
317
317
318 def propagate_data(self):
318 def propagate_data(self):
319 user_model = UserModel()
319 user_model = UserModel()
320 self.anonymous_user = User.get_by_username('default', cache=True)
320 self.anonymous_user = User.get_by_username('default', cache=True)
321 is_user_loaded = False
321 is_user_loaded = False
322
322
323 # try go get user by api key
323 # try go get user by api key
324 if self._api_key and self._api_key != self.anonymous_user.api_key:
324 if self._api_key and self._api_key != self.anonymous_user.api_key:
325 log.debug('Auth User lookup by API KEY %s' % self._api_key)
325 log.debug('Auth User lookup by API KEY %s' % self._api_key)
326 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
326 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
327 # lookup by userid
327 # lookup by userid
328 elif (self.user_id is not None and
328 elif (self.user_id is not None and
329 self.user_id != self.anonymous_user.user_id):
329 self.user_id != self.anonymous_user.user_id):
330 log.debug('Auth User lookup by USER ID %s' % self.user_id)
330 log.debug('Auth User lookup by USER ID %s' % self.user_id)
331 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
331 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
332 # lookup by username
332 # lookup by username
333 elif self.username and \
333 elif self.username and \
334 str2bool(config.get('container_auth_enabled', False)):
334 str2bool(config.get('container_auth_enabled', False)):
335
335
336 log.debug('Auth User lookup by USER NAME %s' % self.username)
336 log.debug('Auth User lookup by USER NAME %s' % self.username)
337 dbuser = login_container_auth(self.username)
337 dbuser = login_container_auth(self.username)
338 if dbuser is not None:
338 if dbuser is not None:
339 for k, v in dbuser.get_dict().items():
339 for k, v in dbuser.get_dict().items():
340 setattr(self, k, v)
340 setattr(self, k, v)
341 self.set_authenticated()
341 self.set_authenticated()
342 is_user_loaded = True
342 is_user_loaded = True
343
343
344 if not is_user_loaded:
344 if not is_user_loaded:
345 # if we cannot authenticate user try anonymous
345 # if we cannot authenticate user try anonymous
346 if self.anonymous_user.active is True:
346 if self.anonymous_user.active is True:
347 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
347 user_model.fill_data(self, user_id=self.anonymous_user.user_id)
348 # then we set this user is logged in
348 # then we set this user is logged in
349 self.is_authenticated = True
349 self.is_authenticated = True
350 else:
350 else:
351 self.user_id = None
351 self.user_id = None
352 self.username = None
352 self.username = None
353 self.is_authenticated = False
353 self.is_authenticated = False
354
354
355 if not self.username:
355 if not self.username:
356 self.username = 'None'
356 self.username = 'None'
357
357
358 log.debug('Auth User is now %s' % self)
358 log.debug('Auth User is now %s' % self)
359 user_model.fill_perms(self)
359 user_model.fill_perms(self)
360
360
361 @property
361 @property
362 def is_admin(self):
362 def is_admin(self):
363 return self.admin
363 return self.admin
364
364
365 def __repr__(self):
365 def __repr__(self):
366 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
366 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
367 self.is_authenticated)
367 self.is_authenticated)
368
368
369 def set_authenticated(self, authenticated=True):
369 def set_authenticated(self, authenticated=True):
370 if self.user_id != self.anonymous_user.user_id:
370 if self.user_id != self.anonymous_user.user_id:
371 self.is_authenticated = authenticated
371 self.is_authenticated = authenticated
372
372
373 def get_cookie_store(self):
373 def get_cookie_store(self):
374 return {'username': self.username,
374 return {'username': self.username,
375 'user_id': self.user_id,
375 'user_id': self.user_id,
376 'is_authenticated': self.is_authenticated}
376 'is_authenticated': self.is_authenticated}
377
377
378 @classmethod
378 @classmethod
379 def from_cookie_store(cls, cookie_store):
379 def from_cookie_store(cls, cookie_store):
380 user_id = cookie_store.get('user_id')
380 user_id = cookie_store.get('user_id')
381 username = cookie_store.get('username')
381 username = cookie_store.get('username')
382 api_key = cookie_store.get('api_key')
382 api_key = cookie_store.get('api_key')
383 return AuthUser(user_id, api_key, username)
383 return AuthUser(user_id, api_key, username)
384
384
385
385
386 def set_available_permissions(config):
386 def set_available_permissions(config):
387 """
387 """
388 This function will propagate pylons globals with all available defined
388 This function will propagate pylons globals with all available defined
389 permission given in db. We don't want to check each time from db for new
389 permission given in db. We don't want to check each time from db for new
390 permissions since adding a new permission also requires application restart
390 permissions since adding a new permission also requires application restart
391 ie. to decorate new views with the newly created permission
391 ie. to decorate new views with the newly created permission
392
392
393 :param config: current pylons config instance
393 :param config: current pylons config instance
394
394
395 """
395 """
396 log.info('getting information about all available permissions')
396 log.info('getting information about all available permissions')
397 try:
397 try:
398 sa = meta.Session
398 sa = meta.Session
399 all_perms = sa.query(Permission).all()
399 all_perms = sa.query(Permission).all()
400 except Exception:
400 except Exception:
401 pass
401 pass
402 finally:
402 finally:
403 meta.Session.remove()
403 meta.Session.remove()
404
404
405 config['available_permissions'] = [x.permission_name for x in all_perms]
405 config['available_permissions'] = [x.permission_name for x in all_perms]
406
406
407
407
408 #==============================================================================
408 #==============================================================================
409 # CHECK DECORATORS
409 # CHECK DECORATORS
410 #==============================================================================
410 #==============================================================================
411 class LoginRequired(object):
411 class LoginRequired(object):
412 """
412 """
413 Must be logged in to execute this function else
413 Must be logged in to execute this function else
414 redirect to login page
414 redirect to login page
415
415
416 :param api_access: if enabled this checks only for valid auth token
416 :param api_access: if enabled this checks only for valid auth token
417 and grants access based on valid token
417 and grants access based on valid token
418 """
418 """
419
419
420 def __init__(self, api_access=False):
420 def __init__(self, api_access=False):
421 self.api_access = api_access
421 self.api_access = api_access
422
422
423 def __call__(self, func):
423 def __call__(self, func):
424 return decorator(self.__wrapper, func)
424 return decorator(self.__wrapper, func)
425
425
426 def __wrapper(self, func, *fargs, **fkwargs):
426 def __wrapper(self, func, *fargs, **fkwargs):
427 cls = fargs[0]
427 cls = fargs[0]
428 user = cls.rhodecode_user
428 user = cls.rhodecode_user
429
429
430 api_access_ok = False
430 api_access_ok = False
431 if self.api_access:
431 if self.api_access:
432 log.debug('Checking API KEY access for %s' % cls)
432 log.debug('Checking API KEY access for %s' % cls)
433 if user.api_key == request.GET.get('api_key'):
433 if user.api_key == request.GET.get('api_key'):
434 api_access_ok = True
434 api_access_ok = True
435 else:
435 else:
436 log.debug("API KEY token not valid")
436 log.debug("API KEY token not valid")
437
437 loc = "%s:%s" % (cls.__class__.__name__, func.__name__)
438 log.debug('Checking if %s is authenticated @ %s' % (user.username, cls))
438 log.debug('Checking if %s is authenticated @ %s' % (user.username, loc))
439 if user.is_authenticated or api_access_ok:
439 if user.is_authenticated or api_access_ok:
440 log.debug('user %s is authenticated' % user.username)
440 log.info('user %s is authenticated and granted access to %s' % (
441 user.username, loc)
442 )
441 return func(*fargs, **fkwargs)
443 return func(*fargs, **fkwargs)
442 else:
444 else:
443 log.warn('user %s NOT authenticated' % user.username)
445 log.warn('user %s NOT authenticated on func: %s' % (
446 user, loc)
447 )
444 p = url.current()
448 p = url.current()
445
449
446 log.debug('redirecting to login page with %s' % p)
450 log.debug('redirecting to login page with %s' % p)
447 return redirect(url('login_home', came_from=p))
451 return redirect(url('login_home', came_from=p))
448
452
449
453
450 class NotAnonymous(object):
454 class NotAnonymous(object):
451 """
455 """
452 Must be logged in to execute this function else
456 Must be logged in to execute this function else
453 redirect to login page"""
457 redirect to login page"""
454
458
455 def __call__(self, func):
459 def __call__(self, func):
456 return decorator(self.__wrapper, func)
460 return decorator(self.__wrapper, func)
457
461
458 def __wrapper(self, func, *fargs, **fkwargs):
462 def __wrapper(self, func, *fargs, **fkwargs):
459 cls = fargs[0]
463 cls = fargs[0]
460 self.user = cls.rhodecode_user
464 self.user = cls.rhodecode_user
461
465
462 log.debug('Checking if user is not anonymous @%s' % cls)
466 log.debug('Checking if user is not anonymous @%s' % cls)
463
467
464 anonymous = self.user.username == 'default'
468 anonymous = self.user.username == 'default'
465
469
466 if anonymous:
470 if anonymous:
467 p = url.current()
471 p = url.current()
468
472
469 import rhodecode.lib.helpers as h
473 import rhodecode.lib.helpers as h
470 h.flash(_('You need to be a registered user to '
474 h.flash(_('You need to be a registered user to '
471 'perform this action'),
475 'perform this action'),
472 category='warning')
476 category='warning')
473 return redirect(url('login_home', came_from=p))
477 return redirect(url('login_home', came_from=p))
474 else:
478 else:
475 return func(*fargs, **fkwargs)
479 return func(*fargs, **fkwargs)
476
480
477
481
478 class PermsDecorator(object):
482 class PermsDecorator(object):
479 """Base class for controller decorators"""
483 """Base class for controller decorators"""
480
484
481 def __init__(self, *required_perms):
485 def __init__(self, *required_perms):
482 available_perms = config['available_permissions']
486 available_perms = config['available_permissions']
483 for perm in required_perms:
487 for perm in required_perms:
484 if perm not in available_perms:
488 if perm not in available_perms:
485 raise Exception("'%s' permission is not defined" % perm)
489 raise Exception("'%s' permission is not defined" % perm)
486 self.required_perms = set(required_perms)
490 self.required_perms = set(required_perms)
487 self.user_perms = None
491 self.user_perms = None
488
492
489 def __call__(self, func):
493 def __call__(self, func):
490 return decorator(self.__wrapper, func)
494 return decorator(self.__wrapper, func)
491
495
492 def __wrapper(self, func, *fargs, **fkwargs):
496 def __wrapper(self, func, *fargs, **fkwargs):
493 cls = fargs[0]
497 cls = fargs[0]
494 self.user = cls.rhodecode_user
498 self.user = cls.rhodecode_user
495 self.user_perms = self.user.permissions
499 self.user_perms = self.user.permissions
496 log.debug('checking %s permissions %s for %s %s',
500 log.debug('checking %s permissions %s for %s %s',
497 self.__class__.__name__, self.required_perms, cls,
501 self.__class__.__name__, self.required_perms, cls,
498 self.user)
502 self.user)
499
503
500 if self.check_permissions():
504 if self.check_permissions():
501 log.debug('Permission granted for %s %s' % (cls, self.user))
505 log.debug('Permission granted for %s %s' % (cls, self.user))
502 return func(*fargs, **fkwargs)
506 return func(*fargs, **fkwargs)
503
507
504 else:
508 else:
505 log.warning('Permission denied for %s %s' % (cls, self.user))
509 log.debug('Permission denied for %s %s' % (cls, self.user))
506 anonymous = self.user.username == 'default'
510 anonymous = self.user.username == 'default'
507
511
508 if anonymous:
512 if anonymous:
509 p = url.current()
513 p = url.current()
510
514
511 import rhodecode.lib.helpers as h
515 import rhodecode.lib.helpers as h
512 h.flash(_('You need to be a signed in to '
516 h.flash(_('You need to be a signed in to '
513 'view this page'),
517 'view this page'),
514 category='warning')
518 category='warning')
515 return redirect(url('login_home', came_from=p))
519 return redirect(url('login_home', came_from=p))
516
520
517 else:
521 else:
518 # redirect with forbidden ret code
522 # redirect with forbidden ret code
519 return abort(403)
523 return abort(403)
520
524
521 def check_permissions(self):
525 def check_permissions(self):
522 """Dummy function for overriding"""
526 """Dummy function for overriding"""
523 raise Exception('You have to write this function in child class')
527 raise Exception('You have to write this function in child class')
524
528
525
529
526 class HasPermissionAllDecorator(PermsDecorator):
530 class HasPermissionAllDecorator(PermsDecorator):
527 """
531 """
528 Checks for access permission for all given predicates. All of them
532 Checks for access permission for all given predicates. All of them
529 have to be meet in order to fulfill the request
533 have to be meet in order to fulfill the request
530 """
534 """
531
535
532 def check_permissions(self):
536 def check_permissions(self):
533 if self.required_perms.issubset(self.user_perms.get('global')):
537 if self.required_perms.issubset(self.user_perms.get('global')):
534 return True
538 return True
535 return False
539 return False
536
540
537
541
538 class HasPermissionAnyDecorator(PermsDecorator):
542 class HasPermissionAnyDecorator(PermsDecorator):
539 """
543 """
540 Checks for access permission for any of given predicates. In order to
544 Checks for access permission for any of given predicates. In order to
541 fulfill the request any of predicates must be meet
545 fulfill the request any of predicates must be meet
542 """
546 """
543
547
544 def check_permissions(self):
548 def check_permissions(self):
545 if self.required_perms.intersection(self.user_perms.get('global')):
549 if self.required_perms.intersection(self.user_perms.get('global')):
546 return True
550 return True
547 return False
551 return False
548
552
549
553
550 class HasRepoPermissionAllDecorator(PermsDecorator):
554 class HasRepoPermissionAllDecorator(PermsDecorator):
551 """
555 """
552 Checks for access permission for all given predicates for specific
556 Checks for access permission for all given predicates for specific
553 repository. All of them have to be meet in order to fulfill the request
557 repository. All of them have to be meet in order to fulfill the request
554 """
558 """
555
559
556 def check_permissions(self):
560 def check_permissions(self):
557 repo_name = get_repo_slug(request)
561 repo_name = get_repo_slug(request)
558 try:
562 try:
559 user_perms = set([self.user_perms['repositories'][repo_name]])
563 user_perms = set([self.user_perms['repositories'][repo_name]])
560 except KeyError:
564 except KeyError:
561 return False
565 return False
562 if self.required_perms.issubset(user_perms):
566 if self.required_perms.issubset(user_perms):
563 return True
567 return True
564 return False
568 return False
565
569
566
570
567 class HasRepoPermissionAnyDecorator(PermsDecorator):
571 class HasRepoPermissionAnyDecorator(PermsDecorator):
568 """
572 """
569 Checks for access permission for any of given predicates for specific
573 Checks for access permission for any of given predicates for specific
570 repository. In order to fulfill the request any of predicates must be meet
574 repository. In order to fulfill the request any of predicates must be meet
571 """
575 """
572
576
573 def check_permissions(self):
577 def check_permissions(self):
574 repo_name = get_repo_slug(request)
578 repo_name = get_repo_slug(request)
575
579
576 try:
580 try:
577 user_perms = set([self.user_perms['repositories'][repo_name]])
581 user_perms = set([self.user_perms['repositories'][repo_name]])
578 except KeyError:
582 except KeyError:
579 return False
583 return False
580 if self.required_perms.intersection(user_perms):
584 if self.required_perms.intersection(user_perms):
581 return True
585 return True
582 return False
586 return False
583
587
584
588
585 class HasReposGroupPermissionAllDecorator(PermsDecorator):
589 class HasReposGroupPermissionAllDecorator(PermsDecorator):
586 """
590 """
587 Checks for access permission for all given predicates for specific
591 Checks for access permission for all given predicates for specific
588 repository. All of them have to be meet in order to fulfill the request
592 repository. All of them have to be meet in order to fulfill the request
589 """
593 """
590
594
591 def check_permissions(self):
595 def check_permissions(self):
592 group_name = get_repos_group_slug(request)
596 group_name = get_repos_group_slug(request)
593 try:
597 try:
594 user_perms = set([self.user_perms['repositories_groups'][group_name]])
598 user_perms = set([self.user_perms['repositories_groups'][group_name]])
595 except KeyError:
599 except KeyError:
596 return False
600 return False
597 if self.required_perms.issubset(user_perms):
601 if self.required_perms.issubset(user_perms):
598 return True
602 return True
599 return False
603 return False
600
604
601
605
602 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
606 class HasReposGroupPermissionAnyDecorator(PermsDecorator):
603 """
607 """
604 Checks for access permission for any of given predicates for specific
608 Checks for access permission for any of given predicates for specific
605 repository. In order to fulfill the request any of predicates must be meet
609 repository. In order to fulfill the request any of predicates must be meet
606 """
610 """
607
611
608 def check_permissions(self):
612 def check_permissions(self):
609 group_name = get_repos_group_slug(request)
613 group_name = get_repos_group_slug(request)
610
614
611 try:
615 try:
612 user_perms = set([self.user_perms['repositories_groups'][group_name]])
616 user_perms = set([self.user_perms['repositories_groups'][group_name]])
613 except KeyError:
617 except KeyError:
614 return False
618 return False
615 if self.required_perms.intersection(user_perms):
619 if self.required_perms.intersection(user_perms):
616 return True
620 return True
617 return False
621 return False
618
622
619
623
620 #==============================================================================
624 #==============================================================================
621 # CHECK FUNCTIONS
625 # CHECK FUNCTIONS
622 #==============================================================================
626 #==============================================================================
623 class PermsFunction(object):
627 class PermsFunction(object):
624 """Base function for other check functions"""
628 """Base function for other check functions"""
625
629
626 def __init__(self, *perms):
630 def __init__(self, *perms):
627 available_perms = config['available_permissions']
631 available_perms = config['available_permissions']
628
632
629 for perm in perms:
633 for perm in perms:
630 if perm not in available_perms:
634 if perm not in available_perms:
631 raise Exception("'%s' permission in not defined" % perm)
635 raise Exception("'%s' permission in not defined" % perm)
632 self.required_perms = set(perms)
636 self.required_perms = set(perms)
633 self.user_perms = None
637 self.user_perms = None
634 self.granted_for = ''
638 self.granted_for = ''
635 self.repo_name = None
639 self.repo_name = None
636
640
637 def __call__(self, check_Location=''):
641 def __call__(self, check_Location=''):
638 user = request.user
642 user = request.user
639 if not user:
643 if not user:
640 return False
644 return False
641 self.user_perms = user.permissions
645 self.user_perms = user.permissions
642 self.granted_for = user
646 self.granted_for = user
643 log.debug('checking %s %s %s', self.__class__.__name__,
647 log.debug('checking %s %s %s', self.__class__.__name__,
644 self.required_perms, user)
648 self.required_perms, user)
645
649
646 if self.check_permissions():
650 if self.check_permissions():
647 log.debug('Permission granted %s @ %s', self.granted_for,
651 log.debug('Permission granted %s @ %s', self.granted_for,
648 check_Location or 'unspecified location')
652 check_Location or 'unspecified location')
649 return True
653 return True
650
654
651 else:
655 else:
652 log.warning('Permission denied for %s @ %s', self.granted_for,
656 log.debug('Permission denied for %s @ %s', self.granted_for,
653 check_Location or 'unspecified location')
657 check_Location or 'unspecified location')
654 return False
658 return False
655
659
656 def check_permissions(self):
660 def check_permissions(self):
657 """Dummy function for overriding"""
661 """Dummy function for overriding"""
658 raise Exception('You have to write this function in child class')
662 raise Exception('You have to write this function in child class')
659
663
660
664
661 class HasPermissionAll(PermsFunction):
665 class HasPermissionAll(PermsFunction):
662 def check_permissions(self):
666 def check_permissions(self):
663 if self.required_perms.issubset(self.user_perms.get('global')):
667 if self.required_perms.issubset(self.user_perms.get('global')):
664 return True
668 return True
665 return False
669 return False
666
670
667
671
668 class HasPermissionAny(PermsFunction):
672 class HasPermissionAny(PermsFunction):
669 def check_permissions(self):
673 def check_permissions(self):
670 if self.required_perms.intersection(self.user_perms.get('global')):
674 if self.required_perms.intersection(self.user_perms.get('global')):
671 return True
675 return True
672 return False
676 return False
673
677
674
678
675 class HasRepoPermissionAll(PermsFunction):
679 class HasRepoPermissionAll(PermsFunction):
676
680
677 def __call__(self, repo_name=None, check_Location=''):
681 def __call__(self, repo_name=None, check_Location=''):
678 self.repo_name = repo_name
682 self.repo_name = repo_name
679 return super(HasRepoPermissionAll, self).__call__(check_Location)
683 return super(HasRepoPermissionAll, self).__call__(check_Location)
680
684
681 def check_permissions(self):
685 def check_permissions(self):
682 if not self.repo_name:
686 if not self.repo_name:
683 self.repo_name = get_repo_slug(request)
687 self.repo_name = get_repo_slug(request)
684
688
685 try:
689 try:
686 self.user_perms = set(
690 self.user_perms = set(
687 [self.user_perms['repositories'][self.repo_name]]
691 [self.user_perms['repositories'][self.repo_name]]
688 )
692 )
689 except KeyError:
693 except KeyError:
690 return False
694 return False
691 self.granted_for = self.repo_name
695 self.granted_for = self.repo_name
692 if self.required_perms.issubset(self.user_perms):
696 if self.required_perms.issubset(self.user_perms):
693 return True
697 return True
694 return False
698 return False
695
699
696
700
697 class HasRepoPermissionAny(PermsFunction):
701 class HasRepoPermissionAny(PermsFunction):
698
702
699 def __call__(self, repo_name=None, check_Location=''):
703 def __call__(self, repo_name=None, check_Location=''):
700 self.repo_name = repo_name
704 self.repo_name = repo_name
701 return super(HasRepoPermissionAny, self).__call__(check_Location)
705 return super(HasRepoPermissionAny, self).__call__(check_Location)
702
706
703 def check_permissions(self):
707 def check_permissions(self):
704 if not self.repo_name:
708 if not self.repo_name:
705 self.repo_name = get_repo_slug(request)
709 self.repo_name = get_repo_slug(request)
706
710
707 try:
711 try:
708 self.user_perms = set(
712 self.user_perms = set(
709 [self.user_perms['repositories'][self.repo_name]]
713 [self.user_perms['repositories'][self.repo_name]]
710 )
714 )
711 except KeyError:
715 except KeyError:
712 return False
716 return False
713 self.granted_for = self.repo_name
717 self.granted_for = self.repo_name
714 if self.required_perms.intersection(self.user_perms):
718 if self.required_perms.intersection(self.user_perms):
715 return True
719 return True
716 return False
720 return False
717
721
718
722
719 class HasReposGroupPermissionAny(PermsFunction):
723 class HasReposGroupPermissionAny(PermsFunction):
720 def __call__(self, group_name=None, check_Location=''):
724 def __call__(self, group_name=None, check_Location=''):
721 self.group_name = group_name
725 self.group_name = group_name
722 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
726 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
723
727
724 def check_permissions(self):
728 def check_permissions(self):
725 try:
729 try:
726 self.user_perms = set(
730 self.user_perms = set(
727 [self.user_perms['repositories_groups'][self.group_name]]
731 [self.user_perms['repositories_groups'][self.group_name]]
728 )
732 )
729 except KeyError:
733 except KeyError:
730 return False
734 return False
731 self.granted_for = self.repo_name
735 self.granted_for = self.repo_name
732 if self.required_perms.intersection(self.user_perms):
736 if self.required_perms.intersection(self.user_perms):
733 return True
737 return True
734 return False
738 return False
735
739
736
740
737 class HasReposGroupPermissionAll(PermsFunction):
741 class HasReposGroupPermissionAll(PermsFunction):
738 def __call__(self, group_name=None, check_Location=''):
742 def __call__(self, group_name=None, check_Location=''):
739 self.group_name = group_name
743 self.group_name = group_name
740 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
744 return super(HasReposGroupPermissionAny, self).__call__(check_Location)
741
745
742 def check_permissions(self):
746 def check_permissions(self):
743 try:
747 try:
744 self.user_perms = set(
748 self.user_perms = set(
745 [self.user_perms['repositories_groups'][self.group_name]]
749 [self.user_perms['repositories_groups'][self.group_name]]
746 )
750 )
747 except KeyError:
751 except KeyError:
748 return False
752 return False
749 self.granted_for = self.repo_name
753 self.granted_for = self.repo_name
750 if self.required_perms.issubset(self.user_perms):
754 if self.required_perms.issubset(self.user_perms):
751 return True
755 return True
752 return False
756 return False
753
757
754
758
755 #==============================================================================
759 #==============================================================================
756 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
760 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
757 #==============================================================================
761 #==============================================================================
758 class HasPermissionAnyMiddleware(object):
762 class HasPermissionAnyMiddleware(object):
759 def __init__(self, *perms):
763 def __init__(self, *perms):
760 self.required_perms = set(perms)
764 self.required_perms = set(perms)
761
765
762 def __call__(self, user, repo_name):
766 def __call__(self, user, repo_name):
763 usr = AuthUser(user.user_id)
767 usr = AuthUser(user.user_id)
764 try:
768 try:
765 self.user_perms = set([usr.permissions['repositories'][repo_name]])
769 self.user_perms = set([usr.permissions['repositories'][repo_name]])
766 except:
770 except:
767 self.user_perms = set()
771 self.user_perms = set()
768 self.granted_for = ''
772 self.granted_for = ''
769 self.username = user.username
773 self.username = user.username
770 self.repo_name = repo_name
774 self.repo_name = repo_name
771 return self.check_permissions()
775 return self.check_permissions()
772
776
773 def check_permissions(self):
777 def check_permissions(self):
774 log.debug('checking mercurial protocol '
778 log.debug('checking mercurial protocol '
775 'permissions %s for user:%s repository:%s', self.user_perms,
779 'permissions %s for user:%s repository:%s', self.user_perms,
776 self.username, self.repo_name)
780 self.username, self.repo_name)
777 if self.required_perms.intersection(self.user_perms):
781 if self.required_perms.intersection(self.user_perms):
778 log.debug('permission granted')
782 log.debug('permission granted')
779 return True
783 return True
780 log.debug('permission denied')
784 log.debug('permission denied')
781 return False
785 return False
@@ -1,181 +1,184 b''
1 """The base Controller API
1 """The base Controller API
2
2
3 Provides the BaseController class for subclassing.
3 Provides the BaseController class for subclassing.
4 """
4 """
5 import logging
5 import logging
6 import time
6 import time
7 import traceback
7 import traceback
8
8
9 from paste.auth.basic import AuthBasicAuthenticator
9 from paste.auth.basic import AuthBasicAuthenticator
10
10
11 from pylons import config, tmpl_context as c, request, session, url
11 from pylons import config, tmpl_context as c, request, session, url
12 from pylons.controllers import WSGIController
12 from pylons.controllers import WSGIController
13 from pylons.controllers.util import redirect
13 from pylons.controllers.util import redirect
14 from pylons.templating import render_mako as render
14 from pylons.templating import render_mako as render
15
15
16 from rhodecode import __version__, BACKENDS
16 from rhodecode import __version__, BACKENDS
17
17
18 from rhodecode.lib import str2bool
18 from rhodecode.lib import str2bool
19 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
19 from rhodecode.lib.auth import AuthUser, get_container_username, authfunc,\
20 HasPermissionAnyMiddleware
20 HasPermissionAnyMiddleware
21 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
21 from rhodecode.lib.utils import get_repo_slug, invalidate_cache
22 from rhodecode.model import meta
22 from rhodecode.model import meta
23
23
24 from rhodecode.model.db import Repository
24 from rhodecode.model.db import Repository
25 from rhodecode.model.notification import NotificationModel
25 from rhodecode.model.notification import NotificationModel
26 from rhodecode.model.scm import ScmModel
26 from rhodecode.model.scm import ScmModel
27
27
28 log = logging.getLogger(__name__)
28 log = logging.getLogger(__name__)
29
29
30
30
31 class BaseVCSController(object):
31 class BaseVCSController(object):
32
32
33 def __init__(self, application, config):
33 def __init__(self, application, config):
34 self.application = application
34 self.application = application
35 self.config = config
35 self.config = config
36 # base path of repo locations
36 # base path of repo locations
37 self.basepath = self.config['base_path']
37 self.basepath = self.config['base_path']
38 #authenticate this mercurial request using authfunc
38 #authenticate this mercurial request using authfunc
39 self.authenticate = AuthBasicAuthenticator('', authfunc)
39 self.authenticate = AuthBasicAuthenticator('', authfunc)
40 self.ipaddr = '0.0.0.0'
40 self.ipaddr = '0.0.0.0'
41
41
42 def _handle_request(self, environ, start_response):
42 def _handle_request(self, environ, start_response):
43 raise NotImplementedError()
43 raise NotImplementedError()
44
44
45 def _get_by_id(self, repo_name):
45 def _get_by_id(self, repo_name):
46 """
46 """
47 Get's a special pattern _<ID> from clone url and tries to replace it
47 Get's a special pattern _<ID> from clone url and tries to replace it
48 with a repository_name for support of _<ID> non changable urls
48 with a repository_name for support of _<ID> non changable urls
49
49
50 :param repo_name:
50 :param repo_name:
51 """
51 """
52 try:
52 try:
53 data = repo_name.split('/')
53 data = repo_name.split('/')
54 if len(data) >= 2:
54 if len(data) >= 2:
55 by_id = data[1].split('_')
55 by_id = data[1].split('_')
56 if len(by_id) == 2 and by_id[1].isdigit():
56 if len(by_id) == 2 and by_id[1].isdigit():
57 _repo_name = Repository.get(by_id[1]).repo_name
57 _repo_name = Repository.get(by_id[1]).repo_name
58 data[1] = _repo_name
58 data[1] = _repo_name
59 except:
59 except:
60 log.debug('Failed to extract repo_name from id %s' % (
60 log.debug('Failed to extract repo_name from id %s' % (
61 traceback.format_exc()
61 traceback.format_exc()
62 )
62 )
63 )
63 )
64
64
65 return '/'.join(data)
65 return '/'.join(data)
66
66
67 def _invalidate_cache(self, repo_name):
67 def _invalidate_cache(self, repo_name):
68 """
68 """
69 Set's cache for this repository for invalidation on next access
69 Set's cache for this repository for invalidation on next access
70
70
71 :param repo_name: full repo name, also a cache key
71 :param repo_name: full repo name, also a cache key
72 """
72 """
73 invalidate_cache('get_repo_cached_%s' % repo_name)
73 invalidate_cache('get_repo_cached_%s' % repo_name)
74
74
75 def _check_permission(self, action, user, repo_name):
75 def _check_permission(self, action, user, repo_name):
76 """
76 """
77 Checks permissions using action (push/pull) user and repository
77 Checks permissions using action (push/pull) user and repository
78 name
78 name
79
79
80 :param action: push or pull action
80 :param action: push or pull action
81 :param user: user instance
81 :param user: user instance
82 :param repo_name: repository name
82 :param repo_name: repository name
83 """
83 """
84 if action == 'push':
84 if action == 'push':
85 if not HasPermissionAnyMiddleware('repository.write',
85 if not HasPermissionAnyMiddleware('repository.write',
86 'repository.admin')(user,
86 'repository.admin')(user,
87 repo_name):
87 repo_name):
88 return False
88 return False
89
89
90 else:
90 else:
91 #any other action need at least read permission
91 #any other action need at least read permission
92 if not HasPermissionAnyMiddleware('repository.read',
92 if not HasPermissionAnyMiddleware('repository.read',
93 'repository.write',
93 'repository.write',
94 'repository.admin')(user,
94 'repository.admin')(user,
95 repo_name):
95 repo_name):
96 return False
96 return False
97
97
98 return True
98 return True
99
99
100 def __call__(self, environ, start_response):
100 def __call__(self, environ, start_response):
101 start = time.time()
101 start = time.time()
102 try:
102 try:
103 return self._handle_request(environ, start_response)
103 return self._handle_request(environ, start_response)
104 finally:
104 finally:
105 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
105 log = logging.getLogger('rhodecode.' + self.__class__.__name__)
106 log.debug('Request time: %.3fs' % (time.time() - start))
106 log.debug('Request time: %.3fs' % (time.time() - start))
107 meta.Session.remove()
107 meta.Session.remove()
108
108
109
109
110 class BaseController(WSGIController):
110 class BaseController(WSGIController):
111
111
112 def __before__(self):
112 def __before__(self):
113 c.rhodecode_version = __version__
113 c.rhodecode_version = __version__
114 c.rhodecode_instanceid = config.get('instance_id')
114 c.rhodecode_instanceid = config.get('instance_id')
115 c.rhodecode_name = config.get('rhodecode_title')
115 c.rhodecode_name = config.get('rhodecode_title')
116 c.use_gravatar = str2bool(config.get('use_gravatar'))
116 c.use_gravatar = str2bool(config.get('use_gravatar'))
117 c.ga_code = config.get('rhodecode_ga_code')
117 c.ga_code = config.get('rhodecode_ga_code')
118 c.repo_name = get_repo_slug(request)
118 c.repo_name = get_repo_slug(request)
119 c.backends = BACKENDS.keys()
119 c.backends = BACKENDS.keys()
120 c.unread_notifications = NotificationModel()\
120 c.unread_notifications = NotificationModel()\
121 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
121 .get_unread_cnt_for_user(c.rhodecode_user.user_id)
122 self.cut_off_limit = int(config.get('cut_off_limit'))
122 self.cut_off_limit = int(config.get('cut_off_limit'))
123
123
124 self.sa = meta.Session
124 self.sa = meta.Session
125 self.scm_model = ScmModel(self.sa)
125 self.scm_model = ScmModel(self.sa)
126
126
127 def __call__(self, environ, start_response):
127 def __call__(self, environ, start_response):
128 """Invoke the Controller"""
128 """Invoke the Controller"""
129 # WSGIController.__call__ dispatches to the Controller method
129 # WSGIController.__call__ dispatches to the Controller method
130 # the request is routed to. This routing information is
130 # the request is routed to. This routing information is
131 # available in environ['pylons.routes_dict']
131 # available in environ['pylons.routes_dict']
132 start = time.time()
132 start = time.time()
133 try:
133 try:
134 # make sure that we update permissions each time we call controller
134 # make sure that we update permissions each time we call controller
135 api_key = request.GET.get('api_key')
135 api_key = request.GET.get('api_key')
136 cookie_store = session.get('rhodecode_user') or {}
136 cookie_store = session.get('rhodecode_user') or {}
137 user_id = cookie_store.get('user_id', None)
137 user_id = cookie_store.get('user_id', None)
138 username = get_container_username(environ, config)
138 username = get_container_username(environ, config)
139
139
140 auth_user = AuthUser(user_id, api_key, username)
140 auth_user = AuthUser(user_id, api_key, username)
141 request.user = auth_user
141 request.user = auth_user
142 self.rhodecode_user = c.rhodecode_user = auth_user
142 self.rhodecode_user = c.rhodecode_user = auth_user
143 if not self.rhodecode_user.is_authenticated and \
143 if not self.rhodecode_user.is_authenticated and \
144 self.rhodecode_user.user_id is not None:
144 self.rhodecode_user.user_id is not None:
145 self.rhodecode_user\
145 self.rhodecode_user\
146 .set_authenticated(cookie_store.get('is_authenticated'))
146 .set_authenticated(cookie_store.get('is_authenticated'))
147
147
148 session['rhodecode_user'] = self.rhodecode_user.get_cookie_store()
148 session['rhodecode_user'] = self.rhodecode_user.get_cookie_store()
149 session.save()
149 session.save()
150 log.info('User: %s accessed %s' % (auth_user,
151 environ.get('PATH_INFO')))
150 return WSGIController.__call__(self, environ, start_response)
152 return WSGIController.__call__(self, environ, start_response)
151 finally:
153 finally:
152 log.debug('Request time: %.3fs' % (time.time() - start))
154 log.info('Request to %s time: %.3fs' % (environ.get('PATH_INFO'),
155 time.time() - start))
153 meta.Session.remove()
156 meta.Session.remove()
154
157
155
158
156 class BaseRepoController(BaseController):
159 class BaseRepoController(BaseController):
157 """
160 """
158 Base class for controllers responsible for loading all needed data for
161 Base class for controllers responsible for loading all needed data for
159 repository loaded items are
162 repository loaded items are
160
163
161 c.rhodecode_repo: instance of scm repository
164 c.rhodecode_repo: instance of scm repository
162 c.rhodecode_db_repo: instance of db
165 c.rhodecode_db_repo: instance of db
163 c.repository_followers: number of followers
166 c.repository_followers: number of followers
164 c.repository_forks: number of forks
167 c.repository_forks: number of forks
165 """
168 """
166
169
167 def __before__(self):
170 def __before__(self):
168 super(BaseRepoController, self).__before__()
171 super(BaseRepoController, self).__before__()
169 if c.repo_name:
172 if c.repo_name:
170
173
171 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
174 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
172 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
175 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
173
176
174 if c.rhodecode_repo is None:
177 if c.rhodecode_repo is None:
175 log.error('%s this repository is present in database but it '
178 log.error('%s this repository is present in database but it '
176 'cannot be created as an scm instance', c.repo_name)
179 'cannot be created as an scm instance', c.repo_name)
177
180
178 redirect(url('home'))
181 redirect(url('home'))
179
182
180 c.repository_followers = self.scm_model.get_followers(c.repo_name)
183 c.repository_followers = self.scm_model.get_followers(c.repo_name)
181 c.repository_forks = self.scm_model.get_forks(c.repo_name)
184 c.repository_forks = self.scm_model.get_forks(c.repo_name)
@@ -1,139 +1,139 b''
1 """
1 """
2 This module provides some useful tools for ``vcs`` like annotate/diff html
2 This module provides some useful tools for ``vcs`` like annotate/diff html
3 output. It also includes some internal helpers.
3 output. It also includes some internal helpers.
4 """
4 """
5 import sys
5 import sys
6 import time
6 import time
7 import datetime
7 import datetime
8
8
9
9
10 def makedate():
10 def makedate():
11 lt = time.localtime()
11 lt = time.localtime()
12 if lt[8] == 1 and time.daylight:
12 if lt[8] == 1 and time.daylight:
13 tz = time.altzone
13 tz = time.altzone
14 else:
14 else:
15 tz = time.timezone
15 tz = time.timezone
16 return time.mktime(lt), tz
16 return time.mktime(lt), tz
17
17
18
18
19 def date_fromtimestamp(unixts, tzoffset=0):
19 def date_fromtimestamp(unixts, tzoffset=0):
20 """
20 """
21 Makes a local datetime object out of unix timestamp
21 Makes a local datetime object out of unix timestamp
22
22
23 :param unixts:
23 :param unixts:
24 :param tzoffset:
24 :param tzoffset:
25 """
25 """
26
26
27 return datetime.datetime.fromtimestamp(float(unixts))
27 return datetime.datetime.fromtimestamp(float(unixts))
28
28
29
29
30 def safe_unicode(str_, from_encoding=None):
30 def safe_unicode(str_, from_encoding=None):
31 """
31 """
32 safe unicode function. Does few trick to turn str_ into unicode
32 safe unicode function. Does few trick to turn str_ into unicode
33
33
34 In case of UnicodeDecode error we try to return it with encoding detected
34 In case of UnicodeDecode error we try to return it with encoding detected
35 by chardet library if it fails fallback to unicode with errors replaced
35 by chardet library if it fails fallback to unicode with errors replaced
36
36
37 :param str_: string to decode
37 :param str_: string to decode
38 :rtype: unicode
38 :rtype: unicode
39 :returns: unicode object
39 :returns: unicode object
40 """
40 """
41 if isinstance(str_, unicode):
41 if isinstance(str_, unicode):
42 return str_
42 return str_
43 if not from_encoding:
43 if not from_encoding:
44 import rhodecode
44 import rhodecode
45 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
45 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding', 'utf8')
46 from_encoding = DEFAULT_ENCODING
46 from_encoding = DEFAULT_ENCODING
47 try:
47 try:
48 return unicode(str_)
48 return unicode(str_)
49 except UnicodeDecodeError:
49 except UnicodeDecodeError:
50 pass
50 pass
51
51
52 try:
52 try:
53 return unicode(str_, from_encoding)
53 return unicode(str_, from_encoding)
54 except UnicodeDecodeError:
54 except UnicodeDecodeError:
55 pass
55 pass
56
56
57 try:
57 try:
58 import chardet
58 import chardet
59 encoding = chardet.detect(str_)['encoding']
59 encoding = chardet.detect(str_)['encoding']
60 if encoding is None:
60 if encoding is None:
61 raise Exception()
61 raise Exception()
62 return str_.decode(encoding)
62 return str_.decode(encoding)
63 except (ImportError, UnicodeDecodeError, Exception):
63 except (ImportError, UnicodeDecodeError, Exception):
64 return unicode(str_, from_encoding, 'replace')
64 return unicode(str_, from_encoding, 'replace')
65
65
66
66
67 def safe_str(unicode_, to_encoding=None):
67 def safe_str(unicode_, to_encoding=None):
68 """
68 """
69 safe str function. Does few trick to turn unicode_ into string
69 safe str function. Does few trick to turn unicode_ into string
70
70
71 In case of UnicodeEncodeError we try to return it with encoding detected
71 In case of UnicodeEncodeError we try to return it with encoding detected
72 by chardet library if it fails fallback to string with errors replaced
72 by chardet library if it fails fallback to string with errors replaced
73
73
74 :param unicode_: unicode to encode
74 :param unicode_: unicode to encode
75 :rtype: str
75 :rtype: str
76 :returns: str object
76 :returns: str object
77 """
77 """
78
78
79 if isinstance(unicode_, str):
79 if isinstance(unicode_, str):
80 return unicode_
80 return unicode_
81 if not to_encoding:
81 if not to_encoding:
82 import rhodecode
82 import rhodecode
83 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding','utf8')
83 DEFAULT_ENCODING = rhodecode.CONFIG.get('default_encoding', 'utf8')
84 to_encoding = DEFAULT_ENCODING
84 to_encoding = DEFAULT_ENCODING
85 try:
85 try:
86 return unicode_.encode(to_encoding)
86 return unicode_.encode(to_encoding)
87 except UnicodeEncodeError:
87 except UnicodeEncodeError:
88 pass
88 pass
89
89
90 try:
90 try:
91 import chardet
91 import chardet
92 encoding = chardet.detect(unicode_)['encoding']
92 encoding = chardet.detect(unicode_)['encoding']
93 print encoding
93 print encoding
94 if encoding is None:
94 if encoding is None:
95 raise UnicodeEncodeError()
95 raise UnicodeEncodeError()
96
96
97 return unicode_.encode(encoding)
97 return unicode_.encode(encoding)
98 except (ImportError, UnicodeEncodeError):
98 except (ImportError, UnicodeEncodeError):
99 return unicode_.encode(to_encoding, 'replace')
99 return unicode_.encode(to_encoding, 'replace')
100
100
101 return safe_str
101 return safe_str
102
102
103
103
104 def author_email(author):
104 def author_email(author):
105 """
105 """
106 returns email address of given author.
106 returns email address of given author.
107 If any of <,> sign are found, it fallbacks to regex findall()
107 If any of <,> sign are found, it fallbacks to regex findall()
108 and returns first found result or empty string
108 and returns first found result or empty string
109
109
110 Regex taken from http://www.regular-expressions.info/email.html
110 Regex taken from http://www.regular-expressions.info/email.html
111 """
111 """
112 import re
112 import re
113 r = author.find('>')
113 r = author.find('>')
114 l = author.find('<')
114 l = author.find('<')
115
115
116 if l == -1 or r == -1:
116 if l == -1 or r == -1:
117 # fallback to regex match of email out of a string
117 # fallback to regex match of email out of a string
118 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
118 email_re = re.compile(r"""[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!"""
119 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
119 r"""#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z"""
120 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
120 r"""0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]"""
121 r"""*[a-z0-9])?""", re.IGNORECASE)
121 r"""*[a-z0-9])?""", re.IGNORECASE)
122 m = re.findall(email_re, author)
122 m = re.findall(email_re, author)
123 return m[0] if m else ''
123 return m[0] if m else ''
124
124
125 return author[l + 1:r].strip()
125 return author[l + 1:r].strip()
126
126
127
127
128 def author_name(author):
128 def author_name(author):
129 """
129 """
130 get name of author, or else username.
130 get name of author, or else username.
131 It'll try to find an email in the author string and just cut it off
131 It'll try to find an email in the author string and just cut it off
132 to get the username
132 to get the username
133 """
133 """
134
134
135 if not '@' in author:
135 if not '@' in author:
136 return author
136 return author
137 else:
137 else:
138 return author.replace(author_email(author), '').replace('<', '')\
138 return author.replace(author_email(author), '').replace('<', '')\
139 .replace('>', '').strip()
139 .replace('>', '').strip()
@@ -1,758 +1,758 b''
1 """ this is forms validation classes
1 """ this is forms validation classes
2 http://formencode.org/module-formencode.validators.html
2 http://formencode.org/module-formencode.validators.html
3 for list off all availible validators
3 for list off all availible validators
4
4
5 we can create our own validators
5 we can create our own validators
6
6
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 pre_validators [] These validators will be applied before the schema
8 pre_validators [] These validators will be applied before the schema
9 chained_validators [] These validators will be applied after the schema
9 chained_validators [] These validators will be applied after the schema
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
10 allow_extra_fields False If True, then it is not an error when keys that aren't associated with a validator are present
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
12 if_key_missing NoDefault If this is given, then any keys that aren't available but are expected will be replaced with this value (and then validated). This does not override a present .if_missing attribute on validators. NoDefault is a special FormEncode class to mean that no default values has been specified and therefore missing keys shouldn't take a default value.
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
13 ignore_key_missing False If True, then missing keys will be missing in the result, if the validator doesn't have .if_missing on it already
14
14
15
15
16 <name> = formencode.validators.<name of validator>
16 <name> = formencode.validators.<name of validator>
17 <name> must equal form name
17 <name> must equal form name
18 list=[1,2,3,4,5]
18 list=[1,2,3,4,5]
19 for SELECT use formencode.All(OneOf(list), Int())
19 for SELECT use formencode.All(OneOf(list), Int())
20
20
21 """
21 """
22 import os
22 import os
23 import re
23 import re
24 import logging
24 import logging
25 import traceback
25 import traceback
26
26
27 import formencode
27 import formencode
28 from formencode import All
28 from formencode import All
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 Email, Bool, StringBoolean, Set
30 Email, Bool, StringBoolean, Set
31
31
32 from pylons.i18n.translation import _
32 from pylons.i18n.translation import _
33 from webhelpers.pylonslib.secure_form import authentication_token
33 from webhelpers.pylonslib.secure_form import authentication_token
34
34
35 from rhodecode.config.routing import ADMIN_PREFIX
35 from rhodecode.config.routing import ADMIN_PREFIX
36 from rhodecode.lib.utils import repo_name_slug
36 from rhodecode.lib.utils import repo_name_slug
37 from rhodecode.lib.auth import authenticate, get_crypt_password
37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 from rhodecode.lib.exceptions import LdapImportError
38 from rhodecode.lib.exceptions import LdapImportError
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
39 from rhodecode.model.db import User, UsersGroup, RepoGroup, Repository
40 from rhodecode import BACKENDS
40 from rhodecode import BACKENDS
41
41
42 log = logging.getLogger(__name__)
42 log = logging.getLogger(__name__)
43
43
44
44
45 #this is needed to translate the messages using _() in validators
45 #this is needed to translate the messages using _() in validators
46 class State_obj(object):
46 class State_obj(object):
47 _ = staticmethod(_)
47 _ = staticmethod(_)
48
48
49
49
50 #==============================================================================
50 #==============================================================================
51 # VALIDATORS
51 # VALIDATORS
52 #==============================================================================
52 #==============================================================================
53 class ValidAuthToken(formencode.validators.FancyValidator):
53 class ValidAuthToken(formencode.validators.FancyValidator):
54 messages = {'invalid_token': _('Token mismatch')}
54 messages = {'invalid_token': _('Token mismatch')}
55
55
56 def validate_python(self, value, state):
56 def validate_python(self, value, state):
57
57
58 if value != authentication_token():
58 if value != authentication_token():
59 raise formencode.Invalid(
59 raise formencode.Invalid(
60 self.message('invalid_token',
60 self.message('invalid_token',
61 state, search_number=value),
61 state, search_number=value),
62 value,
62 value,
63 state
63 state
64 )
64 )
65
65
66
66
67 def ValidUsername(edit, old_data):
67 def ValidUsername(edit, old_data):
68 class _ValidUsername(formencode.validators.FancyValidator):
68 class _ValidUsername(formencode.validators.FancyValidator):
69
69
70 def validate_python(self, value, state):
70 def validate_python(self, value, state):
71 if value in ['default', 'new_user']:
71 if value in ['default', 'new_user']:
72 raise formencode.Invalid(_('Invalid username'), value, state)
72 raise formencode.Invalid(_('Invalid username'), value, state)
73 #check if user is unique
73 #check if user is unique
74 old_un = None
74 old_un = None
75 if edit:
75 if edit:
76 old_un = User.get(old_data.get('user_id')).username
76 old_un = User.get(old_data.get('user_id')).username
77
77
78 if old_un != value or not edit:
78 if old_un != value or not edit:
79 if User.get_by_username(value, case_insensitive=True):
79 if User.get_by_username(value, case_insensitive=True):
80 raise formencode.Invalid(_('This username already '
80 raise formencode.Invalid(_('This username already '
81 'exists') , value, state)
81 'exists') , value, state)
82
82
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
83 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
84 raise formencode.Invalid(
84 raise formencode.Invalid(
85 _('Username may only contain alphanumeric characters '
85 _('Username may only contain alphanumeric characters '
86 'underscores, periods or dashes and must begin with '
86 'underscores, periods or dashes and must begin with '
87 'alphanumeric character'),
87 'alphanumeric character'),
88 value,
88 value,
89 state
89 state
90 )
90 )
91
91
92 return _ValidUsername
92 return _ValidUsername
93
93
94
94
95 def ValidUsersGroup(edit, old_data):
95 def ValidUsersGroup(edit, old_data):
96
96
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
97 class _ValidUsersGroup(formencode.validators.FancyValidator):
98
98
99 def validate_python(self, value, state):
99 def validate_python(self, value, state):
100 if value in ['default']:
100 if value in ['default']:
101 raise formencode.Invalid(_('Invalid group name'), value, state)
101 raise formencode.Invalid(_('Invalid group name'), value, state)
102 #check if group is unique
102 #check if group is unique
103 old_ugname = None
103 old_ugname = None
104 if edit:
104 if edit:
105 old_ugname = UsersGroup.get(
105 old_ugname = UsersGroup.get(
106 old_data.get('users_group_id')).users_group_name
106 old_data.get('users_group_id')).users_group_name
107
107
108 if old_ugname != value or not edit:
108 if old_ugname != value or not edit:
109 if UsersGroup.get_by_group_name(value, cache=False,
109 if UsersGroup.get_by_group_name(value, cache=False,
110 case_insensitive=True):
110 case_insensitive=True):
111 raise formencode.Invalid(_('This users group '
111 raise formencode.Invalid(_('This users group '
112 'already exists'), value,
112 'already exists'), value,
113 state)
113 state)
114
114
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
115 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
116 raise formencode.Invalid(
116 raise formencode.Invalid(
117 _('RepoGroup name may only contain alphanumeric characters '
117 _('RepoGroup name may only contain alphanumeric characters '
118 'underscores, periods or dashes and must begin with '
118 'underscores, periods or dashes and must begin with '
119 'alphanumeric character'),
119 'alphanumeric character'),
120 value,
120 value,
121 state
121 state
122 )
122 )
123
123
124 return _ValidUsersGroup
124 return _ValidUsersGroup
125
125
126
126
127 def ValidReposGroup(edit, old_data):
127 def ValidReposGroup(edit, old_data):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
128 class _ValidReposGroup(formencode.validators.FancyValidator):
129
129
130 def validate_python(self, value, state):
130 def validate_python(self, value, state):
131 # TODO WRITE VALIDATIONS
131 # TODO WRITE VALIDATIONS
132 group_name = value.get('group_name')
132 group_name = value.get('group_name')
133 group_parent_id = value.get('group_parent_id')
133 group_parent_id = value.get('group_parent_id')
134
134
135 # slugify repo group just in case :)
135 # slugify repo group just in case :)
136 slug = repo_name_slug(group_name)
136 slug = repo_name_slug(group_name)
137
137
138 # check for parent of self
138 # check for parent of self
139 parent_of_self = lambda: (
139 parent_of_self = lambda: (
140 old_data['group_id'] == int(group_parent_id)
140 old_data['group_id'] == int(group_parent_id)
141 if group_parent_id else False
141 if group_parent_id else False
142 )
142 )
143 if edit and parent_of_self():
143 if edit and parent_of_self():
144 e_dict = {
144 e_dict = {
145 'group_parent_id': _('Cannot assign this group as parent')
145 'group_parent_id': _('Cannot assign this group as parent')
146 }
146 }
147 raise formencode.Invalid('', value, state,
147 raise formencode.Invalid('', value, state,
148 error_dict=e_dict)
148 error_dict=e_dict)
149
149
150 old_gname = None
150 old_gname = None
151 if edit:
151 if edit:
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
152 old_gname = RepoGroup.get(old_data.get('group_id')).group_name
153
153
154 if old_gname != group_name or not edit:
154 if old_gname != group_name or not edit:
155
155
156 # check group
156 # check group
157 gr = RepoGroup.query()\
157 gr = RepoGroup.query()\
158 .filter(RepoGroup.group_name == slug)\
158 .filter(RepoGroup.group_name == slug)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
159 .filter(RepoGroup.group_parent_id == group_parent_id)\
160 .scalar()
160 .scalar()
161
161
162 if gr:
162 if gr:
163 e_dict = {
163 e_dict = {
164 'group_name': _('This group already exists')
164 'group_name': _('This group already exists')
165 }
165 }
166 raise formencode.Invalid('', value, state,
166 raise formencode.Invalid('', value, state,
167 error_dict=e_dict)
167 error_dict=e_dict)
168
168
169 # check for same repo
169 # check for same repo
170 repo = Repository.query()\
170 repo = Repository.query()\
171 .filter(Repository.repo_name == slug)\
171 .filter(Repository.repo_name == slug)\
172 .scalar()
172 .scalar()
173
173
174 if repo:
174 if repo:
175 e_dict = {
175 e_dict = {
176 'group_name': _('Repository with this name already exists')
176 'group_name': _('Repository with this name already exists')
177 }
177 }
178 raise formencode.Invalid('', value, state,
178 raise formencode.Invalid('', value, state,
179 error_dict=e_dict)
179 error_dict=e_dict)
180
180
181 return _ValidReposGroup
181 return _ValidReposGroup
182
182
183
183
184 class ValidPassword(formencode.validators.FancyValidator):
184 class ValidPassword(formencode.validators.FancyValidator):
185
185
186 def to_python(self, value, state):
186 def to_python(self, value, state):
187
187
188 if not value:
188 if not value:
189 return
189 return
190
190
191 if value.get('password'):
191 if value.get('password'):
192 try:
192 try:
193 value['password'] = get_crypt_password(value['password'])
193 value['password'] = get_crypt_password(value['password'])
194 except UnicodeEncodeError:
194 except UnicodeEncodeError:
195 e_dict = {'password': _('Invalid characters in password')}
195 e_dict = {'password': _('Invalid characters in password')}
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
196 raise formencode.Invalid('', value, state, error_dict=e_dict)
197
197
198 if value.get('password_confirmation'):
198 if value.get('password_confirmation'):
199 try:
199 try:
200 value['password_confirmation'] = \
200 value['password_confirmation'] = \
201 get_crypt_password(value['password_confirmation'])
201 get_crypt_password(value['password_confirmation'])
202 except UnicodeEncodeError:
202 except UnicodeEncodeError:
203 e_dict = {
203 e_dict = {
204 'password_confirmation': _('Invalid characters in password')
204 'password_confirmation': _('Invalid characters in password')
205 }
205 }
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
206 raise formencode.Invalid('', value, state, error_dict=e_dict)
207
207
208 if value.get('new_password'):
208 if value.get('new_password'):
209 try:
209 try:
210 value['new_password'] = \
210 value['new_password'] = \
211 get_crypt_password(value['new_password'])
211 get_crypt_password(value['new_password'])
212 except UnicodeEncodeError:
212 except UnicodeEncodeError:
213 e_dict = {'new_password': _('Invalid characters in password')}
213 e_dict = {'new_password': _('Invalid characters in password')}
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
214 raise formencode.Invalid('', value, state, error_dict=e_dict)
215
215
216 return value
216 return value
217
217
218
218
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
219 class ValidPasswordsMatch(formencode.validators.FancyValidator):
220
220
221 def validate_python(self, value, state):
221 def validate_python(self, value, state):
222
222
223 pass_val = value.get('password') or value.get('new_password')
223 pass_val = value.get('password') or value.get('new_password')
224 if pass_val != value['password_confirmation']:
224 if pass_val != value['password_confirmation']:
225 e_dict = {'password_confirmation':
225 e_dict = {'password_confirmation':
226 _('Passwords do not match')}
226 _('Passwords do not match')}
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
227 raise formencode.Invalid('', value, state, error_dict=e_dict)
228
228
229
229
230 class ValidAuth(formencode.validators.FancyValidator):
230 class ValidAuth(formencode.validators.FancyValidator):
231 messages = {
231 messages = {
232 'invalid_password':_('invalid password'),
232 'invalid_password':_('invalid password'),
233 'invalid_login':_('invalid user name'),
233 'invalid_login':_('invalid user name'),
234 'disabled_account':_('Your account is disabled')
234 'disabled_account':_('Your account is disabled')
235 }
235 }
236
236
237 # error mapping
237 # error mapping
238 e_dict = {'username': messages['invalid_login'],
238 e_dict = {'username': messages['invalid_login'],
239 'password': messages['invalid_password']}
239 'password': messages['invalid_password']}
240 e_dict_disable = {'username': messages['disabled_account']}
240 e_dict_disable = {'username': messages['disabled_account']}
241
241
242 def validate_python(self, value, state):
242 def validate_python(self, value, state):
243 password = value['password']
243 password = value['password']
244 username = value['username']
244 username = value['username']
245 user = User.get_by_username(username)
245 user = User.get_by_username(username)
246
246
247 if authenticate(username, password):
247 if authenticate(username, password):
248 return value
248 return value
249 else:
249 else:
250 if user and user.active is False:
250 if user and user.active is False:
251 log.warning('user %s is disabled' % username)
251 log.warning('user %s is disabled' % username)
252 raise formencode.Invalid(
252 raise formencode.Invalid(
253 self.message('disabled_account',
253 self.message('disabled_account',
254 state=State_obj),
254 state=State_obj),
255 value, state,
255 value, state,
256 error_dict=self.e_dict_disable
256 error_dict=self.e_dict_disable
257 )
257 )
258 else:
258 else:
259 log.warning('user %s not authenticated' % username)
259 log.warning('user %s failed to authenticate' % username)
260 raise formencode.Invalid(
260 raise formencode.Invalid(
261 self.message('invalid_password',
261 self.message('invalid_password',
262 state=State_obj), value, state,
262 state=State_obj), value, state,
263 error_dict=self.e_dict
263 error_dict=self.e_dict
264 )
264 )
265
265
266
266
267 class ValidRepoUser(formencode.validators.FancyValidator):
267 class ValidRepoUser(formencode.validators.FancyValidator):
268
268
269 def to_python(self, value, state):
269 def to_python(self, value, state):
270 try:
270 try:
271 User.query().filter(User.active == True)\
271 User.query().filter(User.active == True)\
272 .filter(User.username == value).one()
272 .filter(User.username == value).one()
273 except Exception:
273 except Exception:
274 raise formencode.Invalid(_('This username is not valid'),
274 raise formencode.Invalid(_('This username is not valid'),
275 value, state)
275 value, state)
276 return value
276 return value
277
277
278
278
279 def ValidRepoName(edit, old_data):
279 def ValidRepoName(edit, old_data):
280 class _ValidRepoName(formencode.validators.FancyValidator):
280 class _ValidRepoName(formencode.validators.FancyValidator):
281 def to_python(self, value, state):
281 def to_python(self, value, state):
282
282
283 repo_name = value.get('repo_name')
283 repo_name = value.get('repo_name')
284
284
285 slug = repo_name_slug(repo_name)
285 slug = repo_name_slug(repo_name)
286 if slug in [ADMIN_PREFIX, '']:
286 if slug in [ADMIN_PREFIX, '']:
287 e_dict = {'repo_name': _('This repository name is disallowed')}
287 e_dict = {'repo_name': _('This repository name is disallowed')}
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
288 raise formencode.Invalid('', value, state, error_dict=e_dict)
289
289
290 if value.get('repo_group'):
290 if value.get('repo_group'):
291 gr = RepoGroup.get(value.get('repo_group'))
291 gr = RepoGroup.get(value.get('repo_group'))
292 group_path = gr.full_path
292 group_path = gr.full_path
293 # value needs to be aware of group name in order to check
293 # value needs to be aware of group name in order to check
294 # db key This is an actual just the name to store in the
294 # db key This is an actual just the name to store in the
295 # database
295 # database
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
296 repo_name_full = group_path + RepoGroup.url_sep() + repo_name
297
297
298 else:
298 else:
299 group_path = ''
299 group_path = ''
300 repo_name_full = repo_name
300 repo_name_full = repo_name
301
301
302 value['repo_name_full'] = repo_name_full
302 value['repo_name_full'] = repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
303 rename = old_data.get('repo_name') != repo_name_full
304 create = not edit
304 create = not edit
305 if rename or create:
305 if rename or create:
306
306
307 if group_path != '':
307 if group_path != '':
308 if Repository.get_by_repo_name(repo_name_full):
308 if Repository.get_by_repo_name(repo_name_full):
309 e_dict = {
309 e_dict = {
310 'repo_name': _('This repository already exists in '
310 'repo_name': _('This repository already exists in '
311 'a group "%s"') % gr.group_name
311 'a group "%s"') % gr.group_name
312 }
312 }
313 raise formencode.Invalid('', value, state,
313 raise formencode.Invalid('', value, state,
314 error_dict=e_dict)
314 error_dict=e_dict)
315 elif RepoGroup.get_by_group_name(repo_name_full):
315 elif RepoGroup.get_by_group_name(repo_name_full):
316 e_dict = {
316 e_dict = {
317 'repo_name': _('There is a group with this name '
317 'repo_name': _('There is a group with this name '
318 'already "%s"') % repo_name_full
318 'already "%s"') % repo_name_full
319 }
319 }
320 raise formencode.Invalid('', value, state,
320 raise formencode.Invalid('', value, state,
321 error_dict=e_dict)
321 error_dict=e_dict)
322
322
323 elif Repository.get_by_repo_name(repo_name_full):
323 elif Repository.get_by_repo_name(repo_name_full):
324 e_dict = {'repo_name': _('This repository '
324 e_dict = {'repo_name': _('This repository '
325 'already exists')}
325 'already exists')}
326 raise formencode.Invalid('', value, state,
326 raise formencode.Invalid('', value, state,
327 error_dict=e_dict)
327 error_dict=e_dict)
328
328
329 return value
329 return value
330
330
331 return _ValidRepoName
331 return _ValidRepoName
332
332
333
333
334 def ValidForkName(*args, **kwargs):
334 def ValidForkName(*args, **kwargs):
335 return ValidRepoName(*args, **kwargs)
335 return ValidRepoName(*args, **kwargs)
336
336
337
337
338 def SlugifyName():
338 def SlugifyName():
339 class _SlugifyName(formencode.validators.FancyValidator):
339 class _SlugifyName(formencode.validators.FancyValidator):
340
340
341 def to_python(self, value, state):
341 def to_python(self, value, state):
342 return repo_name_slug(value)
342 return repo_name_slug(value)
343
343
344 return _SlugifyName
344 return _SlugifyName
345
345
346
346
347 def ValidCloneUri():
347 def ValidCloneUri():
348 from mercurial.httprepo import httprepository, httpsrepository
348 from mercurial.httprepo import httprepository, httpsrepository
349 from rhodecode.lib.utils import make_ui
349 from rhodecode.lib.utils import make_ui
350
350
351 class _ValidCloneUri(formencode.validators.FancyValidator):
351 class _ValidCloneUri(formencode.validators.FancyValidator):
352
352
353 def to_python(self, value, state):
353 def to_python(self, value, state):
354 if not value:
354 if not value:
355 pass
355 pass
356 elif value.startswith('https'):
356 elif value.startswith('https'):
357 try:
357 try:
358 httpsrepository(make_ui('db'), value).capabilities
358 httpsrepository(make_ui('db'), value).capabilities
359 except Exception:
359 except Exception:
360 log.error(traceback.format_exc())
360 log.error(traceback.format_exc())
361 raise formencode.Invalid(_('invalid clone url'), value,
361 raise formencode.Invalid(_('invalid clone url'), value,
362 state)
362 state)
363 elif value.startswith('http'):
363 elif value.startswith('http'):
364 try:
364 try:
365 httprepository(make_ui('db'), value).capabilities
365 httprepository(make_ui('db'), value).capabilities
366 except Exception:
366 except Exception:
367 log.error(traceback.format_exc())
367 log.error(traceback.format_exc())
368 raise formencode.Invalid(_('invalid clone url'), value,
368 raise formencode.Invalid(_('invalid clone url'), value,
369 state)
369 state)
370 else:
370 else:
371 raise formencode.Invalid(_('Invalid clone url, provide a '
371 raise formencode.Invalid(_('Invalid clone url, provide a '
372 'valid clone http\s url'), value,
372 'valid clone http\s url'), value,
373 state)
373 state)
374 return value
374 return value
375
375
376 return _ValidCloneUri
376 return _ValidCloneUri
377
377
378
378
379 def ValidForkType(old_data):
379 def ValidForkType(old_data):
380 class _ValidForkType(formencode.validators.FancyValidator):
380 class _ValidForkType(formencode.validators.FancyValidator):
381
381
382 def to_python(self, value, state):
382 def to_python(self, value, state):
383 if old_data['repo_type'] != value:
383 if old_data['repo_type'] != value:
384 raise formencode.Invalid(_('Fork have to be the same '
384 raise formencode.Invalid(_('Fork have to be the same '
385 'type as original'), value, state)
385 'type as original'), value, state)
386
386
387 return value
387 return value
388 return _ValidForkType
388 return _ValidForkType
389
389
390
390
391 def ValidPerms(type_='repo'):
391 def ValidPerms(type_='repo'):
392 if type_ == 'group':
392 if type_ == 'group':
393 EMPTY_PERM = 'group.none'
393 EMPTY_PERM = 'group.none'
394 elif type_ == 'repo':
394 elif type_ == 'repo':
395 EMPTY_PERM = 'repository.none'
395 EMPTY_PERM = 'repository.none'
396
396
397 class _ValidPerms(formencode.validators.FancyValidator):
397 class _ValidPerms(formencode.validators.FancyValidator):
398 messages = {
398 messages = {
399 'perm_new_member_name':
399 'perm_new_member_name':
400 _('This username or users group name is not valid')
400 _('This username or users group name is not valid')
401 }
401 }
402
402
403 def to_python(self, value, state):
403 def to_python(self, value, state):
404 perms_update = []
404 perms_update = []
405 perms_new = []
405 perms_new = []
406 # build a list of permission to update and new permission to create
406 # build a list of permission to update and new permission to create
407 for k, v in value.items():
407 for k, v in value.items():
408 # means new added member to permissions
408 # means new added member to permissions
409 if k.startswith('perm_new_member'):
409 if k.startswith('perm_new_member'):
410 new_perm = value.get('perm_new_member', False)
410 new_perm = value.get('perm_new_member', False)
411 new_member = value.get('perm_new_member_name', False)
411 new_member = value.get('perm_new_member_name', False)
412 new_type = value.get('perm_new_member_type')
412 new_type = value.get('perm_new_member_type')
413
413
414 if new_member and new_perm:
414 if new_member and new_perm:
415 if (new_member, new_perm, new_type) not in perms_new:
415 if (new_member, new_perm, new_type) not in perms_new:
416 perms_new.append((new_member, new_perm, new_type))
416 perms_new.append((new_member, new_perm, new_type))
417 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
417 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
418 member = k[7:]
418 member = k[7:]
419 t = {'u': 'user',
419 t = {'u': 'user',
420 'g': 'users_group'
420 'g': 'users_group'
421 }[k[0]]
421 }[k[0]]
422 if member == 'default':
422 if member == 'default':
423 if value.get('private'):
423 if value.get('private'):
424 # set none for default when updating to private repo
424 # set none for default when updating to private repo
425 v = EMPTY_PERM
425 v = EMPTY_PERM
426 perms_update.append((member, v, t))
426 perms_update.append((member, v, t))
427
427
428 value['perms_updates'] = perms_update
428 value['perms_updates'] = perms_update
429 value['perms_new'] = perms_new
429 value['perms_new'] = perms_new
430
430
431 # update permissions
431 # update permissions
432 for k, v, t in perms_new:
432 for k, v, t in perms_new:
433 try:
433 try:
434 if t is 'user':
434 if t is 'user':
435 self.user_db = User.query()\
435 self.user_db = User.query()\
436 .filter(User.active == True)\
436 .filter(User.active == True)\
437 .filter(User.username == k).one()
437 .filter(User.username == k).one()
438 if t is 'users_group':
438 if t is 'users_group':
439 self.user_db = UsersGroup.query()\
439 self.user_db = UsersGroup.query()\
440 .filter(UsersGroup.users_group_active == True)\
440 .filter(UsersGroup.users_group_active == True)\
441 .filter(UsersGroup.users_group_name == k).one()
441 .filter(UsersGroup.users_group_name == k).one()
442
442
443 except Exception:
443 except Exception:
444 msg = self.message('perm_new_member_name',
444 msg = self.message('perm_new_member_name',
445 state=State_obj)
445 state=State_obj)
446 raise formencode.Invalid(
446 raise formencode.Invalid(
447 msg, value, state, error_dict={'perm_new_member_name': msg}
447 msg, value, state, error_dict={'perm_new_member_name': msg}
448 )
448 )
449 return value
449 return value
450 return _ValidPerms
450 return _ValidPerms
451
451
452
452
453 class ValidSettings(formencode.validators.FancyValidator):
453 class ValidSettings(formencode.validators.FancyValidator):
454
454
455 def to_python(self, value, state):
455 def to_python(self, value, state):
456 # settings form can't edit user
456 # settings form can't edit user
457 if 'user' in value:
457 if 'user' in value:
458 del['value']['user']
458 del['value']['user']
459 return value
459 return value
460
460
461
461
462 class ValidPath(formencode.validators.FancyValidator):
462 class ValidPath(formencode.validators.FancyValidator):
463 def to_python(self, value, state):
463 def to_python(self, value, state):
464
464
465 if not os.path.isdir(value):
465 if not os.path.isdir(value):
466 msg = _('This is not a valid path')
466 msg = _('This is not a valid path')
467 raise formencode.Invalid(msg, value, state,
467 raise formencode.Invalid(msg, value, state,
468 error_dict={'paths_root_path': msg})
468 error_dict={'paths_root_path': msg})
469 return value
469 return value
470
470
471
471
472 def UniqSystemEmail(old_data):
472 def UniqSystemEmail(old_data):
473 class _UniqSystemEmail(formencode.validators.FancyValidator):
473 class _UniqSystemEmail(formencode.validators.FancyValidator):
474 def to_python(self, value, state):
474 def to_python(self, value, state):
475 value = value.lower()
475 value = value.lower()
476 if old_data.get('email', '').lower() != value:
476 if old_data.get('email', '').lower() != value:
477 user = User.get_by_email(value, case_insensitive=True)
477 user = User.get_by_email(value, case_insensitive=True)
478 if user:
478 if user:
479 raise formencode.Invalid(
479 raise formencode.Invalid(
480 _("This e-mail address is already taken"), value, state
480 _("This e-mail address is already taken"), value, state
481 )
481 )
482 return value
482 return value
483
483
484 return _UniqSystemEmail
484 return _UniqSystemEmail
485
485
486
486
487 class ValidSystemEmail(formencode.validators.FancyValidator):
487 class ValidSystemEmail(formencode.validators.FancyValidator):
488 def to_python(self, value, state):
488 def to_python(self, value, state):
489 value = value.lower()
489 value = value.lower()
490 user = User.get_by_email(value, case_insensitive=True)
490 user = User.get_by_email(value, case_insensitive=True)
491 if user is None:
491 if user is None:
492 raise formencode.Invalid(
492 raise formencode.Invalid(
493 _("This e-mail address doesn't exist."), value, state
493 _("This e-mail address doesn't exist."), value, state
494 )
494 )
495
495
496 return value
496 return value
497
497
498
498
499 class LdapLibValidator(formencode.validators.FancyValidator):
499 class LdapLibValidator(formencode.validators.FancyValidator):
500
500
501 def to_python(self, value, state):
501 def to_python(self, value, state):
502
502
503 try:
503 try:
504 import ldap
504 import ldap
505 except ImportError:
505 except ImportError:
506 raise LdapImportError
506 raise LdapImportError
507 return value
507 return value
508
508
509
509
510 class AttrLoginValidator(formencode.validators.FancyValidator):
510 class AttrLoginValidator(formencode.validators.FancyValidator):
511
511
512 def to_python(self, value, state):
512 def to_python(self, value, state):
513
513
514 if not value or not isinstance(value, (str, unicode)):
514 if not value or not isinstance(value, (str, unicode)):
515 raise formencode.Invalid(
515 raise formencode.Invalid(
516 _("The LDAP Login attribute of the CN must be specified - "
516 _("The LDAP Login attribute of the CN must be specified - "
517 "this is the name of the attribute that is equivalent "
517 "this is the name of the attribute that is equivalent "
518 "to 'username'"), value, state
518 "to 'username'"), value, state
519 )
519 )
520
520
521 return value
521 return value
522
522
523
523
524 #==============================================================================
524 #==============================================================================
525 # FORMS
525 # FORMS
526 #==============================================================================
526 #==============================================================================
527 class LoginForm(formencode.Schema):
527 class LoginForm(formencode.Schema):
528 allow_extra_fields = True
528 allow_extra_fields = True
529 filter_extra_fields = True
529 filter_extra_fields = True
530 username = UnicodeString(
530 username = UnicodeString(
531 strip=True,
531 strip=True,
532 min=1,
532 min=1,
533 not_empty=True,
533 not_empty=True,
534 messages={
534 messages={
535 'empty': _('Please enter a login'),
535 'empty': _('Please enter a login'),
536 'tooShort': _('Enter a value %(min)i characters long or more')}
536 'tooShort': _('Enter a value %(min)i characters long or more')}
537 )
537 )
538
538
539 password = UnicodeString(
539 password = UnicodeString(
540 strip=True,
540 strip=True,
541 min=3,
541 min=3,
542 not_empty=True,
542 not_empty=True,
543 messages={
543 messages={
544 'empty': _('Please enter a password'),
544 'empty': _('Please enter a password'),
545 'tooShort': _('Enter %(min)i characters or more')}
545 'tooShort': _('Enter %(min)i characters or more')}
546 )
546 )
547
547
548 remember = StringBoolean(if_missing=False)
548 remember = StringBoolean(if_missing=False)
549
549
550 chained_validators = [ValidAuth]
550 chained_validators = [ValidAuth]
551
551
552
552
553 def UserForm(edit=False, old_data={}):
553 def UserForm(edit=False, old_data={}):
554 class _UserForm(formencode.Schema):
554 class _UserForm(formencode.Schema):
555 allow_extra_fields = True
555 allow_extra_fields = True
556 filter_extra_fields = True
556 filter_extra_fields = True
557 username = All(UnicodeString(strip=True, min=1, not_empty=True),
557 username = All(UnicodeString(strip=True, min=1, not_empty=True),
558 ValidUsername(edit, old_data))
558 ValidUsername(edit, old_data))
559 if edit:
559 if edit:
560 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
560 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
561 password_confirmation = All(UnicodeString(strip=True, min=6,
561 password_confirmation = All(UnicodeString(strip=True, min=6,
562 not_empty=False))
562 not_empty=False))
563 admin = StringBoolean(if_missing=False)
563 admin = StringBoolean(if_missing=False)
564 else:
564 else:
565 password = All(UnicodeString(strip=True, min=6, not_empty=True))
565 password = All(UnicodeString(strip=True, min=6, not_empty=True))
566 password_confirmation = All(UnicodeString(strip=True, min=6,
566 password_confirmation = All(UnicodeString(strip=True, min=6,
567 not_empty=False))
567 not_empty=False))
568
568
569 active = StringBoolean(if_missing=False)
569 active = StringBoolean(if_missing=False)
570 name = UnicodeString(strip=True, min=1, not_empty=False)
570 name = UnicodeString(strip=True, min=1, not_empty=False)
571 lastname = UnicodeString(strip=True, min=1, not_empty=False)
571 lastname = UnicodeString(strip=True, min=1, not_empty=False)
572 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
572 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
573
573
574 chained_validators = [ValidPasswordsMatch, ValidPassword]
574 chained_validators = [ValidPasswordsMatch, ValidPassword]
575
575
576 return _UserForm
576 return _UserForm
577
577
578
578
579 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
579 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
580 class _UsersGroupForm(formencode.Schema):
580 class _UsersGroupForm(formencode.Schema):
581 allow_extra_fields = True
581 allow_extra_fields = True
582 filter_extra_fields = True
582 filter_extra_fields = True
583
583
584 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
584 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
585 ValidUsersGroup(edit, old_data))
585 ValidUsersGroup(edit, old_data))
586
586
587 users_group_active = StringBoolean(if_missing=False)
587 users_group_active = StringBoolean(if_missing=False)
588
588
589 if edit:
589 if edit:
590 users_group_members = OneOf(available_members, hideList=False,
590 users_group_members = OneOf(available_members, hideList=False,
591 testValueList=True,
591 testValueList=True,
592 if_missing=None, not_empty=False)
592 if_missing=None, not_empty=False)
593
593
594 return _UsersGroupForm
594 return _UsersGroupForm
595
595
596
596
597 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
597 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
598 class _ReposGroupForm(formencode.Schema):
598 class _ReposGroupForm(formencode.Schema):
599 allow_extra_fields = True
599 allow_extra_fields = True
600 filter_extra_fields = False
600 filter_extra_fields = False
601
601
602 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
602 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
603 SlugifyName())
603 SlugifyName())
604 group_description = UnicodeString(strip=True, min=1,
604 group_description = UnicodeString(strip=True, min=1,
605 not_empty=True)
605 not_empty=True)
606 group_parent_id = OneOf(available_groups, hideList=False,
606 group_parent_id = OneOf(available_groups, hideList=False,
607 testValueList=True,
607 testValueList=True,
608 if_missing=None, not_empty=False)
608 if_missing=None, not_empty=False)
609
609
610 chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
610 chained_validators = [ValidReposGroup(edit, old_data), ValidPerms('group')]
611
611
612 return _ReposGroupForm
612 return _ReposGroupForm
613
613
614
614
615 def RegisterForm(edit=False, old_data={}):
615 def RegisterForm(edit=False, old_data={}):
616 class _RegisterForm(formencode.Schema):
616 class _RegisterForm(formencode.Schema):
617 allow_extra_fields = True
617 allow_extra_fields = True
618 filter_extra_fields = True
618 filter_extra_fields = True
619 username = All(ValidUsername(edit, old_data),
619 username = All(ValidUsername(edit, old_data),
620 UnicodeString(strip=True, min=1, not_empty=True))
620 UnicodeString(strip=True, min=1, not_empty=True))
621 password = All(UnicodeString(strip=True, min=6, not_empty=True))
621 password = All(UnicodeString(strip=True, min=6, not_empty=True))
622 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
622 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
623 active = StringBoolean(if_missing=False)
623 active = StringBoolean(if_missing=False)
624 name = UnicodeString(strip=True, min=1, not_empty=False)
624 name = UnicodeString(strip=True, min=1, not_empty=False)
625 lastname = UnicodeString(strip=True, min=1, not_empty=False)
625 lastname = UnicodeString(strip=True, min=1, not_empty=False)
626 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
626 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
627
627
628 chained_validators = [ValidPasswordsMatch, ValidPassword]
628 chained_validators = [ValidPasswordsMatch, ValidPassword]
629
629
630 return _RegisterForm
630 return _RegisterForm
631
631
632
632
633 def PasswordResetForm():
633 def PasswordResetForm():
634 class _PasswordResetForm(formencode.Schema):
634 class _PasswordResetForm(formencode.Schema):
635 allow_extra_fields = True
635 allow_extra_fields = True
636 filter_extra_fields = True
636 filter_extra_fields = True
637 email = All(ValidSystemEmail(), Email(not_empty=True))
637 email = All(ValidSystemEmail(), Email(not_empty=True))
638 return _PasswordResetForm
638 return _PasswordResetForm
639
639
640
640
641 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
641 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
642 repo_groups=[]):
642 repo_groups=[]):
643 class _RepoForm(formencode.Schema):
643 class _RepoForm(formencode.Schema):
644 allow_extra_fields = True
644 allow_extra_fields = True
645 filter_extra_fields = False
645 filter_extra_fields = False
646 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
646 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
647 SlugifyName())
647 SlugifyName())
648 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
648 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
649 ValidCloneUri()())
649 ValidCloneUri()())
650 repo_group = OneOf(repo_groups, hideList=True)
650 repo_group = OneOf(repo_groups, hideList=True)
651 repo_type = OneOf(supported_backends)
651 repo_type = OneOf(supported_backends)
652 description = UnicodeString(strip=True, min=1, not_empty=True)
652 description = UnicodeString(strip=True, min=1, not_empty=True)
653 private = StringBoolean(if_missing=False)
653 private = StringBoolean(if_missing=False)
654 enable_statistics = StringBoolean(if_missing=False)
654 enable_statistics = StringBoolean(if_missing=False)
655 enable_downloads = StringBoolean(if_missing=False)
655 enable_downloads = StringBoolean(if_missing=False)
656
656
657 if edit:
657 if edit:
658 #this is repo owner
658 #this is repo owner
659 user = All(UnicodeString(not_empty=True), ValidRepoUser)
659 user = All(UnicodeString(not_empty=True), ValidRepoUser)
660
660
661 chained_validators = [ValidRepoName(edit, old_data), ValidPerms()]
661 chained_validators = [ValidRepoName(edit, old_data), ValidPerms()]
662 return _RepoForm
662 return _RepoForm
663
663
664
664
665 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
665 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
666 repo_groups=[]):
666 repo_groups=[]):
667 class _RepoForkForm(formencode.Schema):
667 class _RepoForkForm(formencode.Schema):
668 allow_extra_fields = True
668 allow_extra_fields = True
669 filter_extra_fields = False
669 filter_extra_fields = False
670 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
670 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
671 SlugifyName())
671 SlugifyName())
672 repo_group = OneOf(repo_groups, hideList=True)
672 repo_group = OneOf(repo_groups, hideList=True)
673 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
673 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
674 description = UnicodeString(strip=True, min=1, not_empty=True)
674 description = UnicodeString(strip=True, min=1, not_empty=True)
675 private = StringBoolean(if_missing=False)
675 private = StringBoolean(if_missing=False)
676 copy_permissions = StringBoolean(if_missing=False)
676 copy_permissions = StringBoolean(if_missing=False)
677 update_after_clone = StringBoolean(if_missing=False)
677 update_after_clone = StringBoolean(if_missing=False)
678 fork_parent_id = UnicodeString()
678 fork_parent_id = UnicodeString()
679 chained_validators = [ValidForkName(edit, old_data)]
679 chained_validators = [ValidForkName(edit, old_data)]
680
680
681 return _RepoForkForm
681 return _RepoForkForm
682
682
683
683
684 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
684 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
685 repo_groups=[]):
685 repo_groups=[]):
686 class _RepoForm(formencode.Schema):
686 class _RepoForm(formencode.Schema):
687 allow_extra_fields = True
687 allow_extra_fields = True
688 filter_extra_fields = False
688 filter_extra_fields = False
689 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
689 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
690 SlugifyName())
690 SlugifyName())
691 description = UnicodeString(strip=True, min=1, not_empty=True)
691 description = UnicodeString(strip=True, min=1, not_empty=True)
692 repo_group = OneOf(repo_groups, hideList=True)
692 repo_group = OneOf(repo_groups, hideList=True)
693 private = StringBoolean(if_missing=False)
693 private = StringBoolean(if_missing=False)
694
694
695 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
695 chained_validators = [ValidRepoName(edit, old_data), ValidPerms(),
696 ValidSettings]
696 ValidSettings]
697 return _RepoForm
697 return _RepoForm
698
698
699
699
700 def ApplicationSettingsForm():
700 def ApplicationSettingsForm():
701 class _ApplicationSettingsForm(formencode.Schema):
701 class _ApplicationSettingsForm(formencode.Schema):
702 allow_extra_fields = True
702 allow_extra_fields = True
703 filter_extra_fields = False
703 filter_extra_fields = False
704 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
704 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
705 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
705 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
706 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
706 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
707
707
708 return _ApplicationSettingsForm
708 return _ApplicationSettingsForm
709
709
710
710
711 def ApplicationUiSettingsForm():
711 def ApplicationUiSettingsForm():
712 class _ApplicationUiSettingsForm(formencode.Schema):
712 class _ApplicationUiSettingsForm(formencode.Schema):
713 allow_extra_fields = True
713 allow_extra_fields = True
714 filter_extra_fields = False
714 filter_extra_fields = False
715 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
715 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
716 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
716 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
717 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
717 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
718 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
718 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
719 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
719 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
720 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
720 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
721
721
722 return _ApplicationUiSettingsForm
722 return _ApplicationUiSettingsForm
723
723
724
724
725 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
725 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
726 class _DefaultPermissionsForm(formencode.Schema):
726 class _DefaultPermissionsForm(formencode.Schema):
727 allow_extra_fields = True
727 allow_extra_fields = True
728 filter_extra_fields = True
728 filter_extra_fields = True
729 overwrite_default = StringBoolean(if_missing=False)
729 overwrite_default = StringBoolean(if_missing=False)
730 anonymous = OneOf(['True', 'False'], if_missing=False)
730 anonymous = OneOf(['True', 'False'], if_missing=False)
731 default_perm = OneOf(perms_choices)
731 default_perm = OneOf(perms_choices)
732 default_register = OneOf(register_choices)
732 default_register = OneOf(register_choices)
733 default_create = OneOf(create_choices)
733 default_create = OneOf(create_choices)
734
734
735 return _DefaultPermissionsForm
735 return _DefaultPermissionsForm
736
736
737
737
738 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
738 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
739 class _LdapSettingsForm(formencode.Schema):
739 class _LdapSettingsForm(formencode.Schema):
740 allow_extra_fields = True
740 allow_extra_fields = True
741 filter_extra_fields = True
741 filter_extra_fields = True
742 pre_validators = [LdapLibValidator]
742 pre_validators = [LdapLibValidator]
743 ldap_active = StringBoolean(if_missing=False)
743 ldap_active = StringBoolean(if_missing=False)
744 ldap_host = UnicodeString(strip=True,)
744 ldap_host = UnicodeString(strip=True,)
745 ldap_port = Number(strip=True,)
745 ldap_port = Number(strip=True,)
746 ldap_tls_kind = OneOf(tls_kind_choices)
746 ldap_tls_kind = OneOf(tls_kind_choices)
747 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
747 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
748 ldap_dn_user = UnicodeString(strip=True,)
748 ldap_dn_user = UnicodeString(strip=True,)
749 ldap_dn_pass = UnicodeString(strip=True,)
749 ldap_dn_pass = UnicodeString(strip=True,)
750 ldap_base_dn = UnicodeString(strip=True,)
750 ldap_base_dn = UnicodeString(strip=True,)
751 ldap_filter = UnicodeString(strip=True,)
751 ldap_filter = UnicodeString(strip=True,)
752 ldap_search_scope = OneOf(search_scope_choices)
752 ldap_search_scope = OneOf(search_scope_choices)
753 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
753 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
754 ldap_attr_firstname = UnicodeString(strip=True,)
754 ldap_attr_firstname = UnicodeString(strip=True,)
755 ldap_attr_lastname = UnicodeString(strip=True,)
755 ldap_attr_lastname = UnicodeString(strip=True,)
756 ldap_attr_email = UnicodeString(strip=True,)
756 ldap_attr_email = UnicodeString(strip=True,)
757
757
758 return _LdapSettingsForm
758 return _LdapSettingsForm
General Comments 0
You need to be logged in to leave comments. Login now