##// END OF EJS Templates
fixes #77 and adds extendable base Dn with custom uid specification
marcink -
r775:aaf2fc59 beta
parent child Browse files
Show More
@@ -1,233 +1,237 b''
1 .. _setup:
1 .. _setup:
2
2
3 Setup
3 Setup
4 =====
4 =====
5
5
6
6
7 Setting up the application
7 Setting up the application
8 --------------------------
8 --------------------------
9
9
10 ::
10 ::
11
11
12 paster make-config RhodeCode production.ini
12 paster make-config RhodeCode production.ini
13
13
14 - This will create `production.ini` config inside the directory
14 - This will create `production.ini` config inside the directory
15 this config contains various settings for RhodeCode, e.g proxy port,
15 this config contains various settings for RhodeCode, e.g proxy port,
16 email settings,static files, cache and logging.
16 email settings,static files, cache and logging.
17
17
18 ::
18 ::
19
19
20 paster setup-app production.ini
20 paster setup-app production.ini
21
21
22 - This command will create all needed tables and an admin account.
22 - This command will create all needed tables and an admin account.
23 When asked for a path You can either use a new location of one with already
23 When asked for a path You can either use a new location of one with already
24 existing ones. RhodeCode will simply add all new found repositories to
24 existing ones. RhodeCode will simply add all new found repositories to
25 it's database. Also make sure You specify correct path to repositories.
25 it's database. Also make sure You specify correct path to repositories.
26 - Remember that the given path for mercurial_ repositories must be write
26 - Remember that the given path for mercurial_ repositories must be write
27 accessible for the application. It's very important since RhodeCode web interface
27 accessible for the application. It's very important since RhodeCode web interface
28 will work even without such an access but, when trying to do a push it'll
28 will work even without such an access but, when trying to do a push it'll
29 eventually fail with permission denied errors.
29 eventually fail with permission denied errors.
30 - Run
30 - Run
31
31
32 ::
32 ::
33
33
34 paster serve production.ini
34 paster serve production.ini
35
35
36 - This command runs the RhodeCode server the app should be available at the
36 - This command runs the RhodeCode server the app should be available at the
37 127.0.0.1:5000. This ip and port is configurable via the production.ini
37 127.0.0.1:5000. This ip and port is configurable via the production.ini
38 file created in previous step
38 file created in previous step
39 - Use admin account you created to login.
39 - Use admin account you created to login.
40 - Default permissions on each repository is read, and owner is admin. So
40 - Default permissions on each repository is read, and owner is admin. So
41 remember to update these if needed.
41 remember to update these if needed.
42
42
43
43
44 Setting up Whoosh full text search
44 Setting up Whoosh full text search
45 ----------------------------------
45 ----------------------------------
46
46
47 Index for whoosh can be build starting from version 1.1 using paster command
47 Index for whoosh can be build starting from version 1.1 using paster command
48 passing repo locations to index, as well as Your config file that stores
48 passing repo locations to index, as well as Your config file that stores
49 whoosh index files locations. There is possible to pass `-f` to the options
49 whoosh index files locations. There is possible to pass `-f` to the options
50 to enable full index rebuild. Without that indexing will run always in in
50 to enable full index rebuild. Without that indexing will run always in in
51 incremental mode.
51 incremental mode.
52
52
53 ::
53 ::
54
54
55 paster make-index --repo-location=<location for repos> production.ini
55 paster make-index --repo-location=<location for repos> production.ini
56
56
57 for full index rebuild You can use
57 for full index rebuild You can use
58
58
59 ::
59 ::
60
60
61 paster make-index -f --repo-location=<location for repos> production.ini
61 paster make-index -f --repo-location=<location for repos> production.ini
62
62
63 - For full text search You can either put crontab entry for
63 - For full text search You can either put crontab entry for
64
64
65 This command can be run even from crontab in order to do periodical
65 This command can be run even from crontab in order to do periodical
66 index builds and keep Your index always up to date. An example entry might
66 index builds and keep Your index always up to date. An example entry might
67 look like this
67 look like this
68
68
69 ::
69 ::
70
70
71 /path/to/python/bin/paster --repo-location=<location for repos> /path/to/rhodecode/production.ini
71 /path/to/python/bin/paster --repo-location=<location for repos> /path/to/rhodecode/production.ini
72
72
73 When using incremental(default) mode whoosh will check last modification date
73 When using incremental(default) mode whoosh will check last modification date
74 of each file and add it to reindex if newer file is available. Also indexing
74 of each file and add it to reindex if newer file is available. Also indexing
75 daemon checks for removed files and removes them from index.
75 daemon checks for removed files and removes them from index.
76
76
77 Sometime You might want to rebuild index from scratch. You can do that using
77 Sometime You might want to rebuild index from scratch. You can do that using
78 the `-f` flag passed to paster command or, in admin panel You can check
78 the `-f` flag passed to paster command or, in admin panel You can check
79 `build from scratch` flag.
79 `build from scratch` flag.
80
80
81
81
82 Setting up LDAP support
82 Setting up LDAP support
83 -----------------------
83 -----------------------
84
84
85 RhodeCode starting from version 1.1 supports ldap authentication. In order
85 RhodeCode starting from version 1.1 supports ldap authentication. In order
86 to use ldap, You have to install python-ldap package. This package is available
86 to use ldap, You have to install python-ldap package. This package is available
87 via pypi, so You can install it by running
87 via pypi, so You can install it by running
88
88
89 ::
89 ::
90
90
91 easy_install python-ldap
91 easy_install python-ldap
92
92
93 ::
93 ::
94
94
95 pip install python-ldap
95 pip install python-ldap
96
96
97 .. note::
97 .. note::
98 python-ldap requires some certain libs on Your system, so before installing
98 python-ldap requires some certain libs on Your system, so before installing
99 it check that You have at least `openldap`, and `sasl` libraries.
99 it check that You have at least `openldap`, and `sasl` libraries.
100
100
101 ldap settings are located in admin->ldap section,
101 ldap settings are located in admin->ldap section,
102
102
103 Here's a typical ldap setup::
103 Here's a typical ldap setup::
104
104
105 Enable ldap = checked #controls if ldap access is enabled
105 Enable ldap = checked #controls if ldap access is enabled
106 Host = host.domain.org #actual ldap server to connect
106 Host = host.domain.org #actual ldap server to connect
107 Port = 389 or 689 for ldaps #ldap server ports
107 Port = 389 or 689 for ldaps #ldap server ports
108 Enable LDAPS = unchecked #enable disable ldaps
108 Enable LDAPS = unchecked #enable disable ldaps
109 Account = <account> #access for ldap server(if required)
109 Account = <account> #access for ldap server(if required)
110 Password = <password> #password for ldap server(if required)
110 Password = <password> #password for ldap server(if required)
111 Base DN = uid=%(user)s,CN=users,DC=host,DC=domain,DC=org
111 Base DN = uid=%(user)s,CN=users,DC=host,DC=domain,DC=org
112
112
113
113
114 `Account` and `Password` are optional, and used for two-phase ldap
114 `Account` and `Password` are optional, and used for two-phase ldap
115 authentication so those are credentials to access Your ldap, if it doesn't
115 authentication so those are credentials to access Your ldap, if it doesn't
116 support anonymous search/user lookups.
116 support anonymous search/user lookups.
117
118 Base DN must have %(user)s template inside, it's a placer where Your uid used
119 to login would go, it allows admins to specify not standard schema for uid
120 variable
117
121
118 If all data are entered correctly, and `python-ldap` is properly installed
122 If all data are entered correctly, and `python-ldap` is properly installed
119 Users should be granted to access RhodeCode wit ldap accounts. When
123 Users should be granted to access RhodeCode wit ldap accounts. When
120 logging at the first time an special ldap account is created inside RhodeCode,
124 logging at the first time an special ldap account is created inside RhodeCode,
121 so You can control over permissions even on ldap users. If such user exists
125 so You can control over permissions even on ldap users. If such user exists
122 already in RhodeCode database ldap user with the same username would be not
126 already in RhodeCode database ldap user with the same username would be not
123 able to access RhodeCode.
127 able to access RhodeCode.
124
128
125 If You have problems with ldap access and believe You entered correct
129 If You have problems with ldap access and believe You entered correct
126 information check out the RhodeCode logs,any error messages sent from
130 information check out the RhodeCode logs,any error messages sent from
127 ldap will be saved there.
131 ldap will be saved there.
128
132
129
133
130 Nginx virtual host example
134 Nginx virtual host example
131 --------------------------
135 --------------------------
132
136
133 Sample config for nginx using proxy::
137 Sample config for nginx using proxy::
134
138
135 server {
139 server {
136 listen 80;
140 listen 80;
137 server_name hg.myserver.com;
141 server_name hg.myserver.com;
138 access_log /var/log/nginx/rhodecode.access.log;
142 access_log /var/log/nginx/rhodecode.access.log;
139 error_log /var/log/nginx/rhodecode.error.log;
143 error_log /var/log/nginx/rhodecode.error.log;
140 location / {
144 location / {
141 root /var/www/rhodecode/rhodecode/public/;
145 root /var/www/rhodecode/rhodecode/public/;
142 if (!-f $request_filename){
146 if (!-f $request_filename){
143 proxy_pass http://127.0.0.1:5000;
147 proxy_pass http://127.0.0.1:5000;
144 }
148 }
145 #this is important for https !!!
149 #this is important for https !!!
146 proxy_set_header X-Url-Scheme $scheme;
150 proxy_set_header X-Url-Scheme $scheme;
147 include /etc/nginx/proxy.conf;
151 include /etc/nginx/proxy.conf;
148 }
152 }
149 }
153 }
150
154
151 Here's the proxy.conf. It's tuned so it'll not timeout on long
155 Here's the proxy.conf. It's tuned so it'll not timeout on long
152 pushes and also on large pushes::
156 pushes and also on large pushes::
153
157
154 proxy_redirect off;
158 proxy_redirect off;
155 proxy_set_header Host $host;
159 proxy_set_header Host $host;
156 proxy_set_header X-Host $http_host;
160 proxy_set_header X-Host $http_host;
157 proxy_set_header X-Real-IP $remote_addr;
161 proxy_set_header X-Real-IP $remote_addr;
158 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
162 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
159 proxy_set_header Proxy-host $proxy_host;
163 proxy_set_header Proxy-host $proxy_host;
160 client_max_body_size 400m;
164 client_max_body_size 400m;
161 client_body_buffer_size 128k;
165 client_body_buffer_size 128k;
162 proxy_buffering off;
166 proxy_buffering off;
163 proxy_connect_timeout 3600;
167 proxy_connect_timeout 3600;
164 proxy_send_timeout 3600;
168 proxy_send_timeout 3600;
165 proxy_read_timeout 3600;
169 proxy_read_timeout 3600;
166 proxy_buffer_size 8k;
170 proxy_buffer_size 8k;
167 proxy_buffers 8 32k;
171 proxy_buffers 8 32k;
168 proxy_busy_buffers_size 64k;
172 proxy_busy_buffers_size 64k;
169 proxy_temp_file_write_size 64k;
173 proxy_temp_file_write_size 64k;
170
174
171 Also when using root path with nginx You might set the static files to false
175 Also when using root path with nginx You might set the static files to false
172 in production.ini file::
176 in production.ini file::
173
177
174 [app:main]
178 [app:main]
175 use = egg:rhodecode
179 use = egg:rhodecode
176 full_stack = true
180 full_stack = true
177 static_files = false
181 static_files = false
178 lang=en
182 lang=en
179 cache_dir = %(here)s/data
183 cache_dir = %(here)s/data
180
184
181 To not have the statics served by the application. And improve speed.
185 To not have the statics served by the application. And improve speed.
182
186
183 Apache reverse proxy
187 Apache reverse proxy
184 --------------------
188 --------------------
185 Tutorial can be found here
189 Tutorial can be found here
186 http://wiki.pylonshq.com/display/pylonscookbook/Apache+as+a+reverse+proxy+for+Pylons
190 http://wiki.pylonshq.com/display/pylonscookbook/Apache+as+a+reverse+proxy+for+Pylons
187
191
188
192
189 Apache's example FCGI config
193 Apache's example FCGI config
190 ----------------------------
194 ----------------------------
191
195
192 TODO !
196 TODO !
193
197
194 Other configuration files
198 Other configuration files
195 -------------------------
199 -------------------------
196
200
197 Some extra configuration files and examples can be found here:
201 Some extra configuration files and examples can be found here:
198 http://hg.python-works.com/rhodecode/files/tip/init.d
202 http://hg.python-works.com/rhodecode/files/tip/init.d
199
203
200 and also an celeryconfig file can be use from here:
204 and also an celeryconfig file can be use from here:
201 http://hg.python-works.com/rhodecode/files/tip/celeryconfig.py
205 http://hg.python-works.com/rhodecode/files/tip/celeryconfig.py
202
206
203 Troubleshooting
207 Troubleshooting
204 ---------------
208 ---------------
205
209
206 - missing static files ?
210 - missing static files ?
207
211
208 - make sure either to set the `static_files = true` in the .ini file or
212 - make sure either to set the `static_files = true` in the .ini file or
209 double check the root path for Your http setup. It should point to
213 double check the root path for Your http setup. It should point to
210 for example:
214 for example:
211 /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
215 /home/my-virtual-python/lib/python2.6/site-packages/rhodecode/public
212
216
213 - can't install celery/rabbitmq
217 - can't install celery/rabbitmq
214
218
215 - don't worry RhodeCode works without them too. No extra setup required
219 - don't worry RhodeCode works without them too. No extra setup required
216
220
217
221
218 - long lasting push timeouts ?
222 - long lasting push timeouts ?
219
223
220 - make sure You set a longer timeouts in Your proxy/fcgi settings, timeouts
224 - make sure You set a longer timeouts in Your proxy/fcgi settings, timeouts
221 are caused by https server and not RhodeCode
225 are caused by https server and not RhodeCode
222
226
223 - large pushes timeouts ?
227 - large pushes timeouts ?
224
228
225 - make sure You set a proper max_body_size for the http server
229 - make sure You set a proper max_body_size for the http server
226
230
227
231
228
232
229 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
233 .. _virtualenv: http://pypi.python.org/pypi/virtualenv
230 .. _python: http://www.python.org/
234 .. _python: http://www.python.org/
231 .. _mercurial: http://mercurial.selenic.com/
235 .. _mercurial: http://mercurial.selenic.com/
232 .. _celery: http://celeryproject.org/
236 .. _celery: http://celeryproject.org/
233 .. _rabbitmq: http://www.rabbitmq.com/ No newline at end of file
237 .. _rabbitmq: http://www.rabbitmq.com/
@@ -1,105 +1,105 b''
1 #!/usr/bin/env python
1 #!/usr/bin/env python
2 # encoding: utf-8
2 # encoding: utf-8
3 # ldap authentication lib
3 # ldap authentication lib
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
4 # Copyright (C) 2009-2010 Marcin Kuzminski <marcin@python-works.com>
5 #
5 #
6 # This program is free software; you can redistribute it and/or
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; version 2
8 # as published by the Free Software Foundation; version 2
9 # of the License or (at your opinion) any later version of the license.
9 # of the License or (at your opinion) any later version of the license.
10 #
10 #
11 # This program is distributed in the hope that it will be useful,
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
14 # GNU General Public License for more details.
15 #
15 #
16 # You should have received a copy of the GNU General Public License
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19 # MA 02110-1301, USA.
19 # MA 02110-1301, USA.
20 """
20 """
21 Created on Nov 17, 2010
21 Created on Nov 17, 2010
22
22
23 @author: marcink
23 @author: marcink
24 """
24 """
25
25
26 from rhodecode.lib.exceptions import *
26 from rhodecode.lib.exceptions import *
27 import logging
27 import logging
28
28
29 log = logging.getLogger(__name__)
29 log = logging.getLogger(__name__)
30
30
31 try:
31 try:
32 import ldap
32 import ldap
33 except ImportError:
33 except ImportError:
34 pass
34 pass
35
35
36 class AuthLdap(object):
36 class AuthLdap(object):
37
37
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
38 def __init__(self, server, base_dn, port=389, bind_dn='', bind_pass='',
39 use_ldaps=False, ldap_version=3):
39 use_ldaps=False, ldap_version=3):
40 self.ldap_version = ldap_version
40 self.ldap_version = ldap_version
41 if use_ldaps:
41 if use_ldaps:
42 port = port or 689
42 port = port or 689
43 self.LDAP_USE_LDAPS = use_ldaps
43 self.LDAP_USE_LDAPS = use_ldaps
44 self.LDAP_SERVER_ADDRESS = server
44 self.LDAP_SERVER_ADDRESS = server
45 self.LDAP_SERVER_PORT = port
45 self.LDAP_SERVER_PORT = port
46
46
47 #USE FOR READ ONLY BIND TO LDAP SERVER
47 #USE FOR READ ONLY BIND TO LDAP SERVER
48 self.LDAP_BIND_DN = bind_dn
48 self.LDAP_BIND_DN = bind_dn
49 self.LDAP_BIND_PASS = bind_pass
49 self.LDAP_BIND_PASS = bind_pass
50
50
51 ldap_server_type = 'ldap'
51 ldap_server_type = 'ldap'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
52 if self.LDAP_USE_LDAPS:ldap_server_type = ldap_server_type + 's'
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
53 self.LDAP_SERVER = "%s://%s:%s" % (ldap_server_type,
54 self.LDAP_SERVER_ADDRESS,
54 self.LDAP_SERVER_ADDRESS,
55 self.LDAP_SERVER_PORT)
55 self.LDAP_SERVER_PORT)
56
56
57 self.BASE_DN = base_dn
57 self.BASE_DN = base_dn
58 self.AUTH_DN = "uid=%s,%s"
59
58
60 def authenticate_ldap(self, username, password):
59 def authenticate_ldap(self, username, password):
61 """Authenticate a user via LDAP and return his/her LDAP properties.
60 """Authenticate a user via LDAP and return his/her LDAP properties.
62
61
63 Raises AuthenticationError if the credentials are rejected, or
62 Raises AuthenticationError if the credentials are rejected, or
64 EnvironmentError if the LDAP server can't be reached.
63 EnvironmentError if the LDAP server can't be reached.
65
64
66 :param username: username
65 :param username: username
67 :param password: password
66 :param password: password
68 """
67 """
69
68
70 from rhodecode.lib.helpers import chop_at
69 from rhodecode.lib.helpers import chop_at
71
70
72 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
71 uid = chop_at(username, "@%s" % self.LDAP_SERVER_ADDRESS)
73 dn = self.AUTH_DN % (uid, self.BASE_DN)
72
74 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
75 if "," in username:
73 if "," in username:
76 raise LdapUsernameError("invalid character in username: ,")
74 raise LdapUsernameError("invalid character in username: ,")
77 try:
75 try:
78 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
76 ldap.set_option(ldap.OPT_X_TLS_CACERTDIR, '/etc/openldap/cacerts')
79 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
77 ldap.set_option(ldap.OPT_NETWORK_TIMEOUT, 10)
80 server = ldap.initialize(self.LDAP_SERVER)
78 server = ldap.initialize(self.LDAP_SERVER)
81 if self.ldap_version == 2:
79 if self.ldap_version == 2:
82 server.protocol = ldap.VERSION2
80 server.protocol = ldap.VERSION2
83 else:
81 else:
84 server.protocol = ldap.VERSION3
82 server.protocol = ldap.VERSION3
85
83
86 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
84 if self.LDAP_BIND_DN and self.LDAP_BIND_PASS:
87 server.simple_bind_s(self.AUTH_DN % (self.LDAP_BIND_DN,
85 login_dn = self.BASE_DN % {'user':uid}
88 self.BASE_DN),
86 server.simple_bind_s(login_dn, self.LDAP_BIND_PASS)
89 self.LDAP_BIND_PASS)
90
87
88 dn = self.BASE_DN % {'user':uid}
89 log.debug("Authenticating %r at %s", dn, self.LDAP_SERVER)
91 server.simple_bind_s(dn, password)
90 server.simple_bind_s(dn, password)
91
92 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
92 properties = server.search_s(dn, ldap.SCOPE_SUBTREE)
93 if not properties:
93 if not properties:
94 raise ldap.NO_SUCH_OBJECT()
94 raise ldap.NO_SUCH_OBJECT()
95 except ldap.NO_SUCH_OBJECT, e:
95 except ldap.NO_SUCH_OBJECT, e:
96 log.debug("LDAP says no such user '%s' (%s)", uid, username)
96 log.debug("LDAP says no such user '%s' (%s)", uid, username)
97 raise LdapUsernameError()
97 raise LdapUsernameError()
98 except ldap.INVALID_CREDENTIALS, e:
98 except ldap.INVALID_CREDENTIALS, e:
99 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
99 log.debug("LDAP rejected password for user '%s' (%s)", uid, username)
100 raise LdapPasswordError()
100 raise LdapPasswordError()
101 except ldap.SERVER_DOWN, e:
101 except ldap.SERVER_DOWN, e:
102 raise LdapConnectionError("LDAP can't access authentication server")
102 raise LdapConnectionError("LDAP can't access authentication server")
103
103
104 return properties[0]
104 return properties[0]
105
105
@@ -1,462 +1,482 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
25
26 import formencode
26 import formencode
27 from formencode import All
27 from formencode import All
28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
28 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
29 Email, Bool, StringBoolean
29 Email, Bool, StringBoolean
30
30
31 from pylons.i18n.translation import _
31 from pylons.i18n.translation import _
32
32
33 import rhodecode.lib.helpers as h
33 import rhodecode.lib.helpers as h
34 from rhodecode.lib.auth import authenticate, get_crypt_password
34 from rhodecode.lib.auth import authenticate, get_crypt_password
35 from rhodecode.lib.exceptions import LdapImportError
35 from rhodecode.lib.exceptions import LdapImportError
36 from rhodecode.model import meta
36 from rhodecode.model import meta
37 from rhodecode.model.user import UserModel
37 from rhodecode.model.user import UserModel
38 from rhodecode.model.repo import RepoModel
38 from rhodecode.model.repo import RepoModel
39 from rhodecode.model.db import User
39 from rhodecode.model.db import User
40 from rhodecode import BACKENDS
40 from rhodecode import BACKENDS
41
41
42 from webhelpers.pylonslib.secure_form import authentication_token
42 from webhelpers.pylonslib.secure_form import authentication_token
43
43
44 log = logging.getLogger(__name__)
44 log = logging.getLogger(__name__)
45
45
46 #this is needed to translate the messages using _() in validators
46 #this is needed to translate the messages using _() in validators
47 class State_obj(object):
47 class State_obj(object):
48 _ = staticmethod(_)
48 _ = staticmethod(_)
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(self.message('invalid_token', state,
59 raise formencode.Invalid(self.message('invalid_token', state,
60 search_number=value), value, state)
60 search_number=value), value, state)
61
61
62 def ValidUsername(edit, old_data):
62 def ValidUsername(edit, old_data):
63 class _ValidUsername(formencode.validators.FancyValidator):
63 class _ValidUsername(formencode.validators.FancyValidator):
64
64
65 def validate_python(self, value, state):
65 def validate_python(self, value, state):
66 if value in ['default', 'new_user']:
66 if value in ['default', 'new_user']:
67 raise formencode.Invalid(_('Invalid username'), value, state)
67 raise formencode.Invalid(_('Invalid username'), value, state)
68 #check if user is unique
68 #check if user is unique
69 old_un = None
69 old_un = None
70 if edit:
70 if edit:
71 old_un = UserModel().get(old_data.get('user_id')).username
71 old_un = UserModel().get(old_data.get('user_id')).username
72
72
73 if old_un != value or not edit:
73 if old_un != value or not edit:
74 if UserModel().get_by_username(value, cache=False,
74 if UserModel().get_by_username(value, cache=False,
75 case_insensitive=True):
75 case_insensitive=True):
76 raise formencode.Invalid(_('This username already exists') ,
76 raise formencode.Invalid(_('This username already exists') ,
77 value, state)
77 value, state)
78
78
79
79
80 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_]+$', value) is None:
80 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_]+$', value) is None:
81 raise formencode.Invalid(_('Username may only contain '
81 raise formencode.Invalid(_('Username may only contain '
82 'alphanumeric characters underscores '
82 'alphanumeric characters underscores '
83 'or dashes and must begin with '
83 'or dashes and must begin with '
84 'alphanumeric character'),
84 'alphanumeric character'),
85 value, state)
85 value, state)
86
86
87
87
88
88
89 return _ValidUsername
89 return _ValidUsername
90
90
91 class ValidPassword(formencode.validators.FancyValidator):
91 class ValidPassword(formencode.validators.FancyValidator):
92
92
93 def to_python(self, value, state):
93 def to_python(self, value, state):
94
94
95 if value:
95 if value:
96
96
97 if value.get('password'):
97 if value.get('password'):
98 try:
98 try:
99 value['password'] = get_crypt_password(value['password'])
99 value['password'] = get_crypt_password(value['password'])
100 except UnicodeEncodeError:
100 except UnicodeEncodeError:
101 e_dict = {'password':_('Invalid characters in password')}
101 e_dict = {'password':_('Invalid characters in password')}
102 raise formencode.Invalid('', value, state, error_dict=e_dict)
102 raise formencode.Invalid('', value, state, error_dict=e_dict)
103
103
104 if value.get('password_confirmation'):
104 if value.get('password_confirmation'):
105 try:
105 try:
106 value['password_confirmation'] = \
106 value['password_confirmation'] = \
107 get_crypt_password(value['password_confirmation'])
107 get_crypt_password(value['password_confirmation'])
108 except UnicodeEncodeError:
108 except UnicodeEncodeError:
109 e_dict = {'password_confirmation':_('Invalid characters in password')}
109 e_dict = {'password_confirmation':_('Invalid characters in password')}
110 raise formencode.Invalid('', value, state, error_dict=e_dict)
110 raise formencode.Invalid('', value, state, error_dict=e_dict)
111
111
112 if value.get('new_password'):
112 if value.get('new_password'):
113 try:
113 try:
114 value['new_password'] = \
114 value['new_password'] = \
115 get_crypt_password(value['new_password'])
115 get_crypt_password(value['new_password'])
116 except UnicodeEncodeError:
116 except UnicodeEncodeError:
117 e_dict = {'new_password':_('Invalid characters in password')}
117 e_dict = {'new_password':_('Invalid characters in password')}
118 raise formencode.Invalid('', value, state, error_dict=e_dict)
118 raise formencode.Invalid('', value, state, error_dict=e_dict)
119
119
120 return value
120 return value
121
121
122 class ValidPasswordsMatch(formencode.validators.FancyValidator):
122 class ValidPasswordsMatch(formencode.validators.FancyValidator):
123
123
124 def validate_python(self, value, state):
124 def validate_python(self, value, state):
125
125
126 if value['password'] != value['password_confirmation']:
126 if value['password'] != value['password_confirmation']:
127 e_dict = {'password_confirmation':
127 e_dict = {'password_confirmation':
128 _('Password do not match')}
128 _('Password do not match')}
129 raise formencode.Invalid('', value, state, error_dict=e_dict)
129 raise formencode.Invalid('', value, state, error_dict=e_dict)
130
130
131 class ValidAuth(formencode.validators.FancyValidator):
131 class ValidAuth(formencode.validators.FancyValidator):
132 messages = {
132 messages = {
133 'invalid_password':_('invalid password'),
133 'invalid_password':_('invalid password'),
134 'invalid_login':_('invalid user name'),
134 'invalid_login':_('invalid user name'),
135 'disabled_account':_('Your account is disabled')
135 'disabled_account':_('Your account is disabled')
136
136
137 }
137 }
138 #error mapping
138 #error mapping
139 e_dict = {'username':messages['invalid_login'],
139 e_dict = {'username':messages['invalid_login'],
140 'password':messages['invalid_password']}
140 'password':messages['invalid_password']}
141 e_dict_disable = {'username':messages['disabled_account']}
141 e_dict_disable = {'username':messages['disabled_account']}
142
142
143 def validate_python(self, value, state):
143 def validate_python(self, value, state):
144 password = value['password']
144 password = value['password']
145 username = value['username']
145 username = value['username']
146 user = UserModel().get_by_username(username)
146 user = UserModel().get_by_username(username)
147
147
148 if authenticate(username, password):
148 if authenticate(username, password):
149 return value
149 return value
150 else:
150 else:
151 if user and user.active is False:
151 if user and user.active is False:
152 log.warning('user %s is disabled', username)
152 log.warning('user %s is disabled', username)
153 raise formencode.Invalid(self.message('disabled_account',
153 raise formencode.Invalid(self.message('disabled_account',
154 state=State_obj),
154 state=State_obj),
155 value, state,
155 value, state,
156 error_dict=self.e_dict_disable)
156 error_dict=self.e_dict_disable)
157 else:
157 else:
158 log.warning('user %s not authenticated', username)
158 log.warning('user %s not authenticated', username)
159 raise formencode.Invalid(self.message('invalid_password',
159 raise formencode.Invalid(self.message('invalid_password',
160 state=State_obj), value, state,
160 state=State_obj), value, state,
161 error_dict=self.e_dict)
161 error_dict=self.e_dict)
162
162
163 class ValidRepoUser(formencode.validators.FancyValidator):
163 class ValidRepoUser(formencode.validators.FancyValidator):
164
164
165 def to_python(self, value, state):
165 def to_python(self, value, state):
166 sa = meta.Session()
166 sa = meta.Session()
167 try:
167 try:
168 self.user_db = sa.query(User)\
168 self.user_db = sa.query(User)\
169 .filter(User.active == True)\
169 .filter(User.active == True)\
170 .filter(User.username == value).one()
170 .filter(User.username == value).one()
171 except Exception:
171 except Exception:
172 raise formencode.Invalid(_('This username is not valid'),
172 raise formencode.Invalid(_('This username is not valid'),
173 value, state)
173 value, state)
174 finally:
174 finally:
175 meta.Session.remove()
175 meta.Session.remove()
176
176
177 return self.user_db.user_id
177 return self.user_db.user_id
178
178
179 def ValidRepoName(edit, old_data):
179 def ValidRepoName(edit, old_data):
180 class _ValidRepoName(formencode.validators.FancyValidator):
180 class _ValidRepoName(formencode.validators.FancyValidator):
181
181
182 def to_python(self, value, state):
182 def to_python(self, value, state):
183 slug = h.repo_name_slug(value)
183 slug = h.repo_name_slug(value)
184 if slug in ['_admin']:
184 if slug in ['_admin']:
185 raise formencode.Invalid(_('This repository name is disallowed'),
185 raise formencode.Invalid(_('This repository name is disallowed'),
186 value, state)
186 value, state)
187 if old_data.get('repo_name') != value or not edit:
187 if old_data.get('repo_name') != value or not edit:
188 if RepoModel().get_by_repo_name(slug, cache=False):
188 if RepoModel().get_by_repo_name(slug, cache=False):
189 raise formencode.Invalid(_('This repository already exists') ,
189 raise formencode.Invalid(_('This repository already exists') ,
190 value, state)
190 value, state)
191 return slug
191 return slug
192
192
193
193
194 return _ValidRepoName
194 return _ValidRepoName
195
195
196 def ValidForkType(old_data):
196 def ValidForkType(old_data):
197 class _ValidForkType(formencode.validators.FancyValidator):
197 class _ValidForkType(formencode.validators.FancyValidator):
198
198
199 def to_python(self, value, state):
199 def to_python(self, value, state):
200 if old_data['repo_type'] != value:
200 if old_data['repo_type'] != value:
201 raise formencode.Invalid(_('Fork have to be the same type as original'),
201 raise formencode.Invalid(_('Fork have to be the same type as original'),
202 value, state)
202 value, state)
203 return value
203 return value
204 return _ValidForkType
204 return _ValidForkType
205
205
206 class ValidPerms(formencode.validators.FancyValidator):
206 class ValidPerms(formencode.validators.FancyValidator):
207 messages = {'perm_new_user_name':_('This username is not valid')}
207 messages = {'perm_new_user_name':_('This username is not valid')}
208
208
209 def to_python(self, value, state):
209 def to_python(self, value, state):
210 perms_update = []
210 perms_update = []
211 perms_new = []
211 perms_new = []
212 #build a list of permission to update and new permission to create
212 #build a list of permission to update and new permission to create
213 for k, v in value.items():
213 for k, v in value.items():
214 if k.startswith('perm_'):
214 if k.startswith('perm_'):
215 if k.startswith('perm_new_user'):
215 if k.startswith('perm_new_user'):
216 new_perm = value.get('perm_new_user', False)
216 new_perm = value.get('perm_new_user', False)
217 new_user = value.get('perm_new_user_name', False)
217 new_user = value.get('perm_new_user_name', False)
218 if new_user and new_perm:
218 if new_user and new_perm:
219 if (new_user, new_perm) not in perms_new:
219 if (new_user, new_perm) not in perms_new:
220 perms_new.append((new_user, new_perm))
220 perms_new.append((new_user, new_perm))
221 else:
221 else:
222 usr = k[5:]
222 usr = k[5:]
223 if usr == 'default':
223 if usr == 'default':
224 if value['private']:
224 if value['private']:
225 #set none for default when updating to private repo
225 #set none for default when updating to private repo
226 v = 'repository.none'
226 v = 'repository.none'
227 perms_update.append((usr, v))
227 perms_update.append((usr, v))
228 value['perms_updates'] = perms_update
228 value['perms_updates'] = perms_update
229 value['perms_new'] = perms_new
229 value['perms_new'] = perms_new
230 sa = meta.Session
230 sa = meta.Session
231 for k, v in perms_new:
231 for k, v in perms_new:
232 try:
232 try:
233 self.user_db = sa.query(User)\
233 self.user_db = sa.query(User)\
234 .filter(User.active == True)\
234 .filter(User.active == True)\
235 .filter(User.username == k).one()
235 .filter(User.username == k).one()
236 except Exception:
236 except Exception:
237 msg = self.message('perm_new_user_name',
237 msg = self.message('perm_new_user_name',
238 state=State_obj)
238 state=State_obj)
239 raise formencode.Invalid(msg, value, state,
239 raise formencode.Invalid(msg, value, state,
240 error_dict={'perm_new_user_name':msg})
240 error_dict={'perm_new_user_name':msg})
241 return value
241 return value
242
242
243 class ValidSettings(formencode.validators.FancyValidator):
243 class ValidSettings(formencode.validators.FancyValidator):
244
244
245 def to_python(self, value, state):
245 def to_python(self, value, state):
246 #settings form can't edit user
246 #settings form can't edit user
247 if value.has_key('user'):
247 if value.has_key('user'):
248 del['value']['user']
248 del['value']['user']
249
249
250 return value
250 return value
251
251
252 class ValidPath(formencode.validators.FancyValidator):
252 class ValidPath(formencode.validators.FancyValidator):
253 def to_python(self, value, state):
253 def to_python(self, value, state):
254
254
255 if not os.path.isdir(value):
255 if not os.path.isdir(value):
256 msg = _('This is not a valid path')
256 msg = _('This is not a valid path')
257 raise formencode.Invalid(msg, value, state,
257 raise formencode.Invalid(msg, value, state,
258 error_dict={'paths_root_path':msg})
258 error_dict={'paths_root_path':msg})
259 return value
259 return value
260
260
261 def UniqSystemEmail(old_data):
261 def UniqSystemEmail(old_data):
262 class _UniqSystemEmail(formencode.validators.FancyValidator):
262 class _UniqSystemEmail(formencode.validators.FancyValidator):
263 def to_python(self, value, state):
263 def to_python(self, value, state):
264 value = value.lower()
264 value = value.lower()
265 if old_data.get('email') != value:
265 if old_data.get('email') != value:
266 sa = meta.Session()
266 sa = meta.Session()
267 try:
267 try:
268 user = sa.query(User).filter(User.email == value).scalar()
268 user = sa.query(User).filter(User.email == value).scalar()
269 if user:
269 if user:
270 raise formencode.Invalid(_("This e-mail address is already taken") ,
270 raise formencode.Invalid(_("This e-mail address is already taken") ,
271 value, state)
271 value, state)
272 finally:
272 finally:
273 meta.Session.remove()
273 meta.Session.remove()
274
274
275 return value
275 return value
276
276
277 return _UniqSystemEmail
277 return _UniqSystemEmail
278
278
279 class ValidSystemEmail(formencode.validators.FancyValidator):
279 class ValidSystemEmail(formencode.validators.FancyValidator):
280 def to_python(self, value, state):
280 def to_python(self, value, state):
281 value = value.lower()
281 value = value.lower()
282 sa = meta.Session
282 sa = meta.Session
283 try:
283 try:
284 user = sa.query(User).filter(User.email == value).scalar()
284 user = sa.query(User).filter(User.email == value).scalar()
285 if user is None:
285 if user is None:
286 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
286 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
287 value, state)
287 value, state)
288 finally:
288 finally:
289 meta.Session.remove()
289 meta.Session.remove()
290
290
291 return value
291 return value
292
292
293 class LdapLibValidator(formencode.validators.FancyValidator):
293 class LdapLibValidator(formencode.validators.FancyValidator):
294
294
295 def to_python(self, value, state):
295 def to_python(self, value, state):
296
296
297 try:
297 try:
298 import ldap
298 import ldap
299 except ImportError:
299 except ImportError:
300 raise LdapImportError
300 raise LdapImportError
301 return value
301 return value
302
302
303 class BaseDnValidator(formencode.validators.FancyValidator):
304
305 def to_python(self, value, state):
306
307 try:
308 value % {'user':'valid'}
309
310 if value.find('%(user)s') == -1:
311 raise formencode.Invalid(_("You need to specify %(user)s in "
312 "template for example uid=%(user)s "
313 ",dc=company...") ,
314 value, state)
315
316 except KeyError:
317 raise formencode.Invalid(_("Wrong template used, only %(user)s "
318 "is an valid entry") ,
319 value, state)
320
321 return value
322
303 #===============================================================================
323 #===============================================================================
304 # FORMS
324 # FORMS
305 #===============================================================================
325 #===============================================================================
306 class LoginForm(formencode.Schema):
326 class LoginForm(formencode.Schema):
307 allow_extra_fields = True
327 allow_extra_fields = True
308 filter_extra_fields = True
328 filter_extra_fields = True
309 username = UnicodeString(
329 username = UnicodeString(
310 strip=True,
330 strip=True,
311 min=1,
331 min=1,
312 not_empty=True,
332 not_empty=True,
313 messages={
333 messages={
314 'empty':_('Please enter a login'),
334 'empty':_('Please enter a login'),
315 'tooShort':_('Enter a value %(min)i characters long or more')}
335 'tooShort':_('Enter a value %(min)i characters long or more')}
316 )
336 )
317
337
318 password = UnicodeString(
338 password = UnicodeString(
319 strip=True,
339 strip=True,
320 min=6,
340 min=6,
321 not_empty=True,
341 not_empty=True,
322 messages={
342 messages={
323 'empty':_('Please enter a password'),
343 'empty':_('Please enter a password'),
324 'tooShort':_('Enter %(min)i characters or more')}
344 'tooShort':_('Enter %(min)i characters or more')}
325 )
345 )
326
346
327
347
328 #chained validators have access to all data
348 #chained validators have access to all data
329 chained_validators = [ValidAuth]
349 chained_validators = [ValidAuth]
330
350
331 def UserForm(edit=False, old_data={}):
351 def UserForm(edit=False, old_data={}):
332 class _UserForm(formencode.Schema):
352 class _UserForm(formencode.Schema):
333 allow_extra_fields = True
353 allow_extra_fields = True
334 filter_extra_fields = True
354 filter_extra_fields = True
335 username = All(UnicodeString(strip=True, min=1, not_empty=True),
355 username = All(UnicodeString(strip=True, min=1, not_empty=True),
336 ValidUsername(edit, old_data))
356 ValidUsername(edit, old_data))
337 if edit:
357 if edit:
338 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
358 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
339 admin = StringBoolean(if_missing=False)
359 admin = StringBoolean(if_missing=False)
340 else:
360 else:
341 password = All(UnicodeString(strip=True, min=6, not_empty=True))
361 password = All(UnicodeString(strip=True, min=6, not_empty=True))
342 active = StringBoolean(if_missing=False)
362 active = StringBoolean(if_missing=False)
343 name = UnicodeString(strip=True, min=1, not_empty=True)
363 name = UnicodeString(strip=True, min=1, not_empty=True)
344 lastname = UnicodeString(strip=True, min=1, not_empty=True)
364 lastname = UnicodeString(strip=True, min=1, not_empty=True)
345 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
365 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
346
366
347 chained_validators = [ValidPassword]
367 chained_validators = [ValidPassword]
348
368
349 return _UserForm
369 return _UserForm
350
370
351 def RegisterForm(edit=False, old_data={}):
371 def RegisterForm(edit=False, old_data={}):
352 class _RegisterForm(formencode.Schema):
372 class _RegisterForm(formencode.Schema):
353 allow_extra_fields = True
373 allow_extra_fields = True
354 filter_extra_fields = True
374 filter_extra_fields = True
355 username = All(ValidUsername(edit, old_data),
375 username = All(ValidUsername(edit, old_data),
356 UnicodeString(strip=True, min=1, not_empty=True))
376 UnicodeString(strip=True, min=1, not_empty=True))
357 password = All(UnicodeString(strip=True, min=6, not_empty=True))
377 password = All(UnicodeString(strip=True, min=6, not_empty=True))
358 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
378 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
359 active = StringBoolean(if_missing=False)
379 active = StringBoolean(if_missing=False)
360 name = UnicodeString(strip=True, min=1, not_empty=True)
380 name = UnicodeString(strip=True, min=1, not_empty=True)
361 lastname = UnicodeString(strip=True, min=1, not_empty=True)
381 lastname = UnicodeString(strip=True, min=1, not_empty=True)
362 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
382 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
363
383
364 chained_validators = [ValidPasswordsMatch, ValidPassword]
384 chained_validators = [ValidPasswordsMatch, ValidPassword]
365
385
366 return _RegisterForm
386 return _RegisterForm
367
387
368 def PasswordResetForm():
388 def PasswordResetForm():
369 class _PasswordResetForm(formencode.Schema):
389 class _PasswordResetForm(formencode.Schema):
370 allow_extra_fields = True
390 allow_extra_fields = True
371 filter_extra_fields = True
391 filter_extra_fields = True
372 email = All(ValidSystemEmail(), Email(not_empty=True))
392 email = All(ValidSystemEmail(), Email(not_empty=True))
373 return _PasswordResetForm
393 return _PasswordResetForm
374
394
375 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
395 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
376 class _RepoForm(formencode.Schema):
396 class _RepoForm(formencode.Schema):
377 allow_extra_fields = True
397 allow_extra_fields = True
378 filter_extra_fields = False
398 filter_extra_fields = False
379 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
399 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
380 ValidRepoName(edit, old_data))
400 ValidRepoName(edit, old_data))
381 description = UnicodeString(strip=True, min=1, not_empty=True)
401 description = UnicodeString(strip=True, min=1, not_empty=True)
382 private = StringBoolean(if_missing=False)
402 private = StringBoolean(if_missing=False)
383 repo_type = OneOf(supported_backends)
403 repo_type = OneOf(supported_backends)
384 if edit:
404 if edit:
385 user = All(Int(not_empty=True), ValidRepoUser)
405 user = All(Int(not_empty=True), ValidRepoUser)
386
406
387 chained_validators = [ValidPerms]
407 chained_validators = [ValidPerms]
388 return _RepoForm
408 return _RepoForm
389
409
390 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
410 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
391 class _RepoForkForm(formencode.Schema):
411 class _RepoForkForm(formencode.Schema):
392 allow_extra_fields = True
412 allow_extra_fields = True
393 filter_extra_fields = False
413 filter_extra_fields = False
394 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
414 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
395 ValidRepoName(edit, old_data))
415 ValidRepoName(edit, old_data))
396 description = UnicodeString(strip=True, min=1, not_empty=True)
416 description = UnicodeString(strip=True, min=1, not_empty=True)
397 private = StringBoolean(if_missing=False)
417 private = StringBoolean(if_missing=False)
398 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
418 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
399 return _RepoForkForm
419 return _RepoForkForm
400
420
401 def RepoSettingsForm(edit=False, old_data={}):
421 def RepoSettingsForm(edit=False, old_data={}):
402 class _RepoForm(formencode.Schema):
422 class _RepoForm(formencode.Schema):
403 allow_extra_fields = True
423 allow_extra_fields = True
404 filter_extra_fields = False
424 filter_extra_fields = False
405 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
425 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
406 ValidRepoName(edit, old_data))
426 ValidRepoName(edit, old_data))
407 description = UnicodeString(strip=True, min=1, not_empty=True)
427 description = UnicodeString(strip=True, min=1, not_empty=True)
408 private = StringBoolean(if_missing=False)
428 private = StringBoolean(if_missing=False)
409
429
410 chained_validators = [ValidPerms, ValidSettings]
430 chained_validators = [ValidPerms, ValidSettings]
411 return _RepoForm
431 return _RepoForm
412
432
413
433
414 def ApplicationSettingsForm():
434 def ApplicationSettingsForm():
415 class _ApplicationSettingsForm(formencode.Schema):
435 class _ApplicationSettingsForm(formencode.Schema):
416 allow_extra_fields = True
436 allow_extra_fields = True
417 filter_extra_fields = False
437 filter_extra_fields = False
418 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
438 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
419 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
439 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
420
440
421 return _ApplicationSettingsForm
441 return _ApplicationSettingsForm
422
442
423 def ApplicationUiSettingsForm():
443 def ApplicationUiSettingsForm():
424 class _ApplicationUiSettingsForm(formencode.Schema):
444 class _ApplicationUiSettingsForm(formencode.Schema):
425 allow_extra_fields = True
445 allow_extra_fields = True
426 filter_extra_fields = False
446 filter_extra_fields = False
427 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
447 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
428 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
448 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
429 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
449 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
430 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
450 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
431 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
451 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
432 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
452 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
433
453
434 return _ApplicationUiSettingsForm
454 return _ApplicationUiSettingsForm
435
455
436 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
456 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
437 class _DefaultPermissionsForm(formencode.Schema):
457 class _DefaultPermissionsForm(formencode.Schema):
438 allow_extra_fields = True
458 allow_extra_fields = True
439 filter_extra_fields = True
459 filter_extra_fields = True
440 overwrite_default = StringBoolean(if_missing=False)
460 overwrite_default = StringBoolean(if_missing=False)
441 anonymous = OneOf(['True', 'False'], if_missing=False)
461 anonymous = OneOf(['True', 'False'], if_missing=False)
442 default_perm = OneOf(perms_choices)
462 default_perm = OneOf(perms_choices)
443 default_register = OneOf(register_choices)
463 default_register = OneOf(register_choices)
444 default_create = OneOf(create_choices)
464 default_create = OneOf(create_choices)
445
465
446 return _DefaultPermissionsForm
466 return _DefaultPermissionsForm
447
467
448
468
449 def LdapSettingsForm():
469 def LdapSettingsForm():
450 class _LdapSettingsForm(formencode.Schema):
470 class _LdapSettingsForm(formencode.Schema):
451 allow_extra_fields = True
471 allow_extra_fields = True
452 filter_extra_fields = True
472 filter_extra_fields = True
453 pre_validators = [LdapLibValidator]
473 pre_validators = [LdapLibValidator]
454 ldap_active = StringBoolean(if_missing=False)
474 ldap_active = StringBoolean(if_missing=False)
455 ldap_host = UnicodeString(strip=True,)
475 ldap_host = UnicodeString(strip=True,)
456 ldap_port = Number(strip=True,)
476 ldap_port = Number(strip=True,)
457 ldap_ldaps = StringBoolean(if_missing=False)
477 ldap_ldaps = StringBoolean(if_missing=False)
458 ldap_dn_user = UnicodeString(strip=True,)
478 ldap_dn_user = UnicodeString(strip=True,)
459 ldap_dn_pass = UnicodeString(strip=True,)
479 ldap_dn_pass = UnicodeString(strip=True,)
460 ldap_base_dn = UnicodeString(strip=True,)
480 ldap_base_dn = All(BaseDnValidator, UnicodeString(strip=True,))
461
481
462 return _LdapSettingsForm
482 return _LdapSettingsForm
@@ -1,98 +1,103 b''
1 from rhodecode import get_version
2 import sys
1 import sys
3 py_version = sys.version_info
2 py_version = sys.version_info
4
3
4 from rhodecode import get_version
5
5 requirements = [
6 requirements = [
6 "Pylons>=1.0.0",
7 "Pylons>=1.0.0",
7 "SQLAlchemy>=0.6.5",
8 "SQLAlchemy>=0.6.5",
8 "Mako>=0.3.6",
9 "Mako>=0.3.6",
9 "vcs>=0.1.10",
10 "vcs>=0.1.10",
10 "pygments>=1.3.0",
11 "pygments>=1.3.0",
11 "mercurial>=1.7.1",
12 "mercurial>=1.7.1",
12 "whoosh>=1.3.1",
13 "whoosh==1.3.1",
13 "celery>=2.1.3",
14 "celery>=2.1.3",
14 "py-bcrypt",
15 "py-bcrypt",
15 "babel",
16 "babel",
16 ]
17 ]
17
18
18 classifiers = ['Development Status :: 4 - Beta',
19 classifiers = ['Development Status :: 4 - Beta',
19 'Environment :: Web Environment',
20 'Environment :: Web Environment',
20 'Framework :: Pylons',
21 'Framework :: Pylons',
21 'Intended Audience :: Developers',
22 'Intended Audience :: Developers',
22 'License :: OSI Approved :: BSD License',
23 'License :: OSI Approved :: BSD License',
23 'Operating System :: OS Independent',
24 'Operating System :: OS Independent',
24 'Programming Language :: Python', ]
25 'Programming Language :: Python', ]
25
26
26 if sys.version_info < (2, 6):
27 if sys.version_info < (2, 6):
27 requirements.append("simplejson")
28 requirements.append("simplejson")
28 requirements.append("pysqlite")
29 requirements.append("pysqlite")
29
30
30 #additional files from project that goes somewhere in the filesystem
31 #additional files from project that goes somewhere in the filesystem
31 #relative to sys.prefix
32 #relative to sys.prefix
32 data_files = []
33 data_files = []
33
34
34 #additional files that goes into package itself
35 #additional files that goes into package itself
35 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
36 package_data = {'rhodecode': ['i18n/*/LC_MESSAGES/*.mo', ], }
36
37
37 description = ('Mercurial repository browser/management with '
38 description = ('Mercurial repository browser/management with '
38 'build in push/pull server and full text search')
39 'build in push/pull server and full text search')
39 #long description
40 #long description
40 try:
41 try:
41 readme_file = 'README.rst'
42 readme_file = 'README.rst'
42 changelog_file = 'docs/changelog.rst'
43 changelog_file = 'docs/changelog.rst'
43 long_description = open(readme_file).read() + '/n/n' + \
44 long_description = open(readme_file).read() + '/n/n' + \
44 open(changelog_file).read()
45 open(changelog_file).read()
45
46
46 except IOError, err:
47 except IOError, err:
47 sys.stderr.write("[WARNING] Cannot find file specified as "
48 sys.stderr.write("[WARNING] Cannot find file specified as "
48 "long_description (%s)\n or changelog (%s) skipping that file" \
49 "long_description (%s)\n or changelog (%s) skipping that file" \
49 % (readme_file, changelog_file))
50 % (readme_file, changelog_file))
50 long_description = description
51 long_description = description
51
52
52
53
53 try:
54 try:
54 from setuptools import setup, find_packages
55 from setuptools import setup, find_packages
55 except ImportError:
56 except ImportError:
56 from ez_setup import use_setuptools
57 from ez_setup import use_setuptools
57 use_setuptools()
58 use_setuptools()
58 from setuptools import setup, find_packages
59 from setuptools import setup, find_packages
59 #packages
60 #packages
60 packages = find_packages(exclude=['ez_setup'])
61 packages = find_packages(exclude=['ez_setup'])
61
62
62 setup(
63 setup(
63 name='RhodeCode',
64 name='RhodeCode',
64 version=get_version(),
65 version=get_version(),
65 description=description,
66 description=description,
66 long_description=long_description,
67 long_description=long_description,
67 keywords='rhodiumcode mercurial web hgwebdir gitweb git replacement serving hgweb rhodecode',
68 keywords='rhodiumcode mercurial web hgwebdir gitweb git replacement serving hgweb rhodecode',
68 license='BSD',
69 license='BSD',
69 author='Marcin Kuzminski',
70 author='Marcin Kuzminski',
70 author_email='marcin@python-works.com',
71 author_email='marcin@python-works.com',
71 url='http://hg.python-works.com',
72 url='http://hg.python-works.com',
72 install_requires=requirements,
73 install_requires=requirements,
73 classifiers=classifiers,
74 classifiers=classifiers,
74 setup_requires=["PasteScript>=1.6.3"],
75 setup_requires=["PasteScript>=1.6.3"],
75 data_files=data_files,
76 data_files=data_files,
76 packages=packages,
77 packages=packages,
77 include_package_data=True,
78 include_package_data=True,
78 test_suite='nose.collector',
79 test_suite='nose.collector',
79 package_data=package_data,
80 package_data=package_data,
80 message_extractors={'rhodecode': [
81 message_extractors={'rhodecode': [
81 ('**.py', 'python', None),
82 ('**.py', 'python', None),
82 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
83 ('templates/**.mako', 'mako', {'input_encoding': 'utf-8'}),
83 ('public/**', 'ignore', None)]},
84 ('public/**', 'ignore', None)]},
84 zip_safe=False,
85 zip_safe=False,
85 paster_plugins=['PasteScript', 'Pylons'],
86 paster_plugins=['PasteScript', 'Pylons'],
86 entry_points="""
87 entry_points="""
87 [paste.app_factory]
88 [paste.app_factory]
88 main = rhodecode.config.middleware:make_app
89 main = rhodecode.config.middleware:make_app
89
90
90 [paste.app_install]
91 [paste.app_install]
91 main = pylons.util:PylonsInstaller
92 main = pylons.util:PylonsInstaller
92
93
93 [paste.global_paster_command]
94 [paste.global_paster_command]
94 make-index = rhodecode.lib.indexers:MakeIndex
95 make-index = rhodecode.lib.indexers:MakeIndex
95 upgrade-db = rhodecode.lib.utils:UpgradeDb
96 upgrade-db = rhodecode.lib.utils:UpgradeDb
96
97 celeryd=rhodecode.lib.celerypylons.commands:CeleryDaemonCommand
98 celerybeat=rhodecode.lib.celerypylons.commands:CeleryBeatCommand
99 camqadm=rhodecode.lib.celerypylons.commands:CAMQPAdminCommand
100 celeryev=rhodecode.lib.celerypylons.commands:CeleryEventCommand
101
97 """,
102 """,
98 )
103 )
General Comments 0
You need to be logged in to leave comments. Login now