##// END OF EJS Templates
Some code cleanups and fixes
marcink -
r1628:de71a4bd beta
parent child Browse files
Show More
@@ -1,241 +1,243 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20
21 21 #smtp_server = mail.server.com
22 22 #smtp_username =
23 23 #smtp_password =
24 24 #smtp_port =
25 25 #smtp_use_tls = false
26 26 #smtp_use_ssl = true
27 27 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 28 #smtp_auth =
29 29
30 30 [server:main]
31 31 ##nr of threads to spawn
32 32 threadpool_workers = 5
33 33
34 34 ##max request before thread respawn
35 35 threadpool_max_requests = 6
36 36
37 37 ##option to use threads of process
38 38 use_threadpool = true
39 39
40 40 use = egg:Paste#http
41 41 host = 0.0.0.0
42 42 port = 5000
43 43
44 44 [app:main]
45 45 use = egg:rhodecode
46 46 full_stack = true
47 47 static_files = true
48 48 lang=en
49 49 cache_dir = %(here)s/data
50 50 index_dir = %(here)s/data/index
51 51 app_instance_uuid = develop
52 52 cut_off_limit = 256000
53 53 force_https = false
54 54 commit_parse_limit = 25
55 55 use_gravatar = true
56 container_auth_enabled = false
57 proxypass_auth_enabled = false
56 58
57 59 ####################################
58 60 ### CELERY CONFIG ####
59 61 ####################################
60 62 use_celery = false
61 63 broker.host = localhost
62 64 broker.vhost = rabbitmqhost
63 65 broker.port = 5672
64 66 broker.user = rabbitmq
65 67 broker.password = qweqwe
66 68
67 69 celery.imports = rhodecode.lib.celerylib.tasks
68 70
69 71 celery.result.backend = amqp
70 72 celery.result.dburi = amqp://
71 73 celery.result.serialier = json
72 74
73 75 #celery.send.task.error.emails = true
74 76 #celery.amqp.task.result.expires = 18000
75 77
76 78 celeryd.concurrency = 2
77 79 #celeryd.log.file = celeryd.log
78 80 celeryd.log.level = debug
79 81 celeryd.max.tasks.per.child = 1
80 82
81 83 #tasks will never be sent to the queue, but executed locally instead.
82 84 celery.always.eager = false
83 85
84 86 ####################################
85 87 ### BEAKER CACHE ####
86 88 ####################################
87 89 beaker.cache.data_dir=%(here)s/data/cache/data
88 90 beaker.cache.lock_dir=%(here)s/data/cache/lock
89 91
90 92 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
91 93
92 94 beaker.cache.super_short_term.type=memory
93 95 beaker.cache.super_short_term.expire=10
94 96 beaker.cache.super_short_term.key_length = 256
95 97
96 98 beaker.cache.short_term.type=memory
97 99 beaker.cache.short_term.expire=60
98 100 beaker.cache.short_term.key_length = 256
99 101
100 102 beaker.cache.long_term.type=memory
101 103 beaker.cache.long_term.expire=36000
102 104 beaker.cache.long_term.key_length = 256
103 105
104 106 beaker.cache.sql_cache_short.type=memory
105 107 beaker.cache.sql_cache_short.expire=10
106 108 beaker.cache.sql_cache_short.key_length = 256
107 109
108 110 beaker.cache.sql_cache_med.type=memory
109 111 beaker.cache.sql_cache_med.expire=360
110 112 beaker.cache.sql_cache_med.key_length = 256
111 113
112 114 beaker.cache.sql_cache_long.type=file
113 115 beaker.cache.sql_cache_long.expire=3600
114 116 beaker.cache.sql_cache_long.key_length = 256
115 117
116 118 ####################################
117 119 ### BEAKER SESSION ####
118 120 ####################################
119 121 ## Type of storage used for the session, current types are
120 122 ## dbm, file, memcached, database, and memory.
121 123 ## The storage uses the Container API
122 124 ##that is also used by the cache system.
123 125 beaker.session.type = file
124 126
125 127 beaker.session.key = rhodecode
126 128 beaker.session.secret = g654dcno0-9873jhgfreyu
127 129 beaker.session.timeout = 36000
128 130
129 131 ##auto save the session to not to use .save()
130 132 beaker.session.auto = False
131 133
132 134 ##true exire at browser close
133 135 #beaker.session.cookie_expires = 3600
134 136
135 137
136 138 ################################################################################
137 139 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
138 140 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
139 141 ## execute malicious code after an exception is raised. ##
140 142 ################################################################################
141 143 #set debug = false
142 144
143 145 ##################################
144 146 ### LOGVIEW CONFIG ###
145 147 ##################################
146 148 logview.sqlalchemy = #faa
147 149 logview.pylons.templating = #bfb
148 150 logview.pylons.util = #eee
149 151
150 152 #########################################################
151 153 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
152 154 #########################################################
153 155 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
154 156 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
155 157 sqlalchemy.db1.echo = false
156 158 sqlalchemy.db1.pool_recycle = 3600
157 159 sqlalchemy.convert_unicode = true
158 160
159 161 ################################
160 162 ### LOGGING CONFIGURATION ####
161 163 ################################
162 164 [loggers]
163 165 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
164 166
165 167 [handlers]
166 168 keys = console, console_sql
167 169
168 170 [formatters]
169 171 keys = generic, color_formatter, color_formatter_sql
170 172
171 173 #############
172 174 ## LOGGERS ##
173 175 #############
174 176 [logger_root]
175 177 level = NOTSET
176 178 handlers = console
177 179
178 180 [logger_routes]
179 181 level = DEBUG
180 182 handlers =
181 183 qualname = routes.middleware
182 184 # "level = DEBUG" logs the route matched and routing variables.
183 185 propagate = 1
184 186
185 187 [logger_beaker]
186 188 level = DEBUG
187 189 handlers =
188 190 qualname = beaker.container
189 191 propagate = 1
190 192
191 193 [logger_templates]
192 194 level = INFO
193 195 handlers =
194 196 qualname = pylons.templating
195 197 propagate = 1
196 198
197 199 [logger_rhodecode]
198 200 level = DEBUG
199 201 handlers =
200 202 qualname = rhodecode
201 203 propagate = 1
202 204
203 205 [logger_sqlalchemy]
204 206 level = INFO
205 207 handlers = console_sql
206 208 qualname = sqlalchemy.engine
207 209 propagate = 0
208 210
209 211 ##############
210 212 ## HANDLERS ##
211 213 ##############
212 214
213 215 [handler_console]
214 216 class = StreamHandler
215 217 args = (sys.stderr,)
216 218 level = DEBUG
217 219 formatter = color_formatter
218 220
219 221 [handler_console_sql]
220 222 class = StreamHandler
221 223 args = (sys.stderr,)
222 224 level = DEBUG
223 225 formatter = color_formatter_sql
224 226
225 227 ################
226 228 ## FORMATTERS ##
227 229 ################
228 230
229 231 [formatter_generic]
230 232 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
231 233 datefmt = %Y-%m-%d %H:%M:%S
232 234
233 235 [formatter_color_formatter]
234 236 class=rhodecode.lib.colored_formatter.ColorFormatter
235 237 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
236 238 datefmt = %Y-%m-%d %H:%M:%S
237 239
238 240 [formatter_color_formatter_sql]
239 241 class=rhodecode.lib.colored_formatter.ColorFormatterSql
240 242 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
241 243 datefmt = %Y-%m-%d %H:%M:%S
@@ -1,241 +1,243 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20
21 21 #smtp_server = mail.server.com
22 22 #smtp_username =
23 23 #smtp_password =
24 24 #smtp_port =
25 25 #smtp_use_tls = false
26 26 #smtp_use_ssl = true
27 27 # Specify available auth parameters here (e.g. LOGIN PLAIN CRAM-MD5, etc.)
28 28 #smtp_auth =
29 29
30 30 [server:main]
31 31 ##nr of threads to spawn
32 32 threadpool_workers = 5
33 33
34 34 ##max request before thread respawn
35 35 threadpool_max_requests = 10
36 36
37 37 ##option to use threads of process
38 38 use_threadpool = true
39 39
40 40 use = egg:Paste#http
41 41 host = 127.0.0.1
42 42 port = 8001
43 43
44 44 [app:main]
45 45 use = egg:rhodecode
46 46 full_stack = true
47 47 static_files = true
48 48 lang=en
49 49 cache_dir = %(here)s/data
50 50 index_dir = %(here)s/data/index
51 51 app_instance_uuid = prod1234
52 52 cut_off_limit = 256000
53 53 force_https = false
54 54 commit_parse_limit = 50
55 55 use_gravatar = true
56 container_auth_enabled = false
57 proxypass_auth_enabled = false
56 58
57 59 ####################################
58 60 ### CELERY CONFIG ####
59 61 ####################################
60 62 use_celery = false
61 63 broker.host = localhost
62 64 broker.vhost = rabbitmqhost
63 65 broker.port = 5672
64 66 broker.user = rabbitmq
65 67 broker.password = qweqwe
66 68
67 69 celery.imports = rhodecode.lib.celerylib.tasks
68 70
69 71 celery.result.backend = amqp
70 72 celery.result.dburi = amqp://
71 73 celery.result.serialier = json
72 74
73 75 #celery.send.task.error.emails = true
74 76 #celery.amqp.task.result.expires = 18000
75 77
76 78 celeryd.concurrency = 2
77 79 #celeryd.log.file = celeryd.log
78 80 celeryd.log.level = debug
79 81 celeryd.max.tasks.per.child = 1
80 82
81 83 #tasks will never be sent to the queue, but executed locally instead.
82 84 celery.always.eager = false
83 85
84 86 ####################################
85 87 ### BEAKER CACHE ####
86 88 ####################################
87 89 beaker.cache.data_dir=%(here)s/data/cache/data
88 90 beaker.cache.lock_dir=%(here)s/data/cache/lock
89 91
90 92 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
91 93
92 94 beaker.cache.super_short_term.type=memory
93 95 beaker.cache.super_short_term.expire=10
94 96 beaker.cache.super_short_term.key_length = 256
95 97
96 98 beaker.cache.short_term.type=memory
97 99 beaker.cache.short_term.expire=60
98 100 beaker.cache.short_term.key_length = 256
99 101
100 102 beaker.cache.long_term.type=memory
101 103 beaker.cache.long_term.expire=36000
102 104 beaker.cache.long_term.key_length = 256
103 105
104 106 beaker.cache.sql_cache_short.type=memory
105 107 beaker.cache.sql_cache_short.expire=10
106 108 beaker.cache.sql_cache_short.key_length = 256
107 109
108 110 beaker.cache.sql_cache_med.type=memory
109 111 beaker.cache.sql_cache_med.expire=360
110 112 beaker.cache.sql_cache_med.key_length = 256
111 113
112 114 beaker.cache.sql_cache_long.type=file
113 115 beaker.cache.sql_cache_long.expire=3600
114 116 beaker.cache.sql_cache_long.key_length = 256
115 117
116 118 ####################################
117 119 ### BEAKER SESSION ####
118 120 ####################################
119 121 ## Type of storage used for the session, current types are
120 122 ## dbm, file, memcached, database, and memory.
121 123 ## The storage uses the Container API
122 124 ##that is also used by the cache system.
123 125 beaker.session.type = file
124 126
125 127 beaker.session.key = rhodecode
126 128 beaker.session.secret = g654dcno0-9873jhgfreyu
127 129 beaker.session.timeout = 36000
128 130
129 131 ##auto save the session to not to use .save()
130 132 beaker.session.auto = False
131 133
132 134 ##true exire at browser close
133 135 #beaker.session.cookie_expires = 3600
134 136
135 137
136 138 ################################################################################
137 139 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
138 140 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
139 141 ## execute malicious code after an exception is raised. ##
140 142 ################################################################################
141 143 set debug = false
142 144
143 145 ##################################
144 146 ### LOGVIEW CONFIG ###
145 147 ##################################
146 148 logview.sqlalchemy = #faa
147 149 logview.pylons.templating = #bfb
148 150 logview.pylons.util = #eee
149 151
150 152 #########################################################
151 153 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
152 154 #########################################################
153 155 #sqlalchemy.db1.url = sqlite:///%(here)s/rhodecode.db
154 156 sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode
155 157 sqlalchemy.db1.echo = false
156 158 sqlalchemy.db1.pool_recycle = 3600
157 159 sqlalchemy.convert_unicode = true
158 160
159 161 ################################
160 162 ### LOGGING CONFIGURATION ####
161 163 ################################
162 164 [loggers]
163 165 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
164 166
165 167 [handlers]
166 168 keys = console, console_sql
167 169
168 170 [formatters]
169 171 keys = generic, color_formatter, color_formatter_sql
170 172
171 173 #############
172 174 ## LOGGERS ##
173 175 #############
174 176 [logger_root]
175 177 level = NOTSET
176 178 handlers = console
177 179
178 180 [logger_routes]
179 181 level = DEBUG
180 182 handlers =
181 183 qualname = routes.middleware
182 184 # "level = DEBUG" logs the route matched and routing variables.
183 185 propagate = 1
184 186
185 187 [logger_beaker]
186 188 level = DEBUG
187 189 handlers =
188 190 qualname = beaker.container
189 191 propagate = 1
190 192
191 193 [logger_templates]
192 194 level = INFO
193 195 handlers =
194 196 qualname = pylons.templating
195 197 propagate = 1
196 198
197 199 [logger_rhodecode]
198 200 level = DEBUG
199 201 handlers =
200 202 qualname = rhodecode
201 203 propagate = 1
202 204
203 205 [logger_sqlalchemy]
204 206 level = INFO
205 207 handlers = console_sql
206 208 qualname = sqlalchemy.engine
207 209 propagate = 0
208 210
209 211 ##############
210 212 ## HANDLERS ##
211 213 ##############
212 214
213 215 [handler_console]
214 216 class = StreamHandler
215 217 args = (sys.stderr,)
216 218 level = INFO
217 219 formatter = generic
218 220
219 221 [handler_console_sql]
220 222 class = StreamHandler
221 223 args = (sys.stderr,)
222 224 level = WARN
223 225 formatter = generic
224 226
225 227 ################
226 228 ## FORMATTERS ##
227 229 ################
228 230
229 231 [formatter_generic]
230 232 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
231 233 datefmt = %Y-%m-%d %H:%M:%S
232 234
233 235 [formatter_color_formatter]
234 236 class=rhodecode.lib.colored_formatter.ColorFormatter
235 237 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
236 238 datefmt = %Y-%m-%d %H:%M:%S
237 239
238 240 [formatter_color_formatter_sql]
239 241 class=rhodecode.lib.colored_formatter.ColorFormatterSql
240 242 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
241 243 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
@@ -1,166 +1,166 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.controllers.login
4 4 ~~~~~~~~~~~~~~~~~~~~~~~~~~~
5 5
6 6 Login controller for rhodeocode
7 7
8 8 :created_on: Apr 22, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import formencode
28 28
29 29 from formencode import htmlfill
30 30
31 31 from pylons.i18n.translation import _
32 32 from pylons.controllers.util import abort, redirect
33 33 from pylons import request, response, session, tmpl_context as c, url
34 34
35 35 import rhodecode.lib.helpers as h
36 36 from rhodecode.lib.auth import AuthUser, HasPermissionAnyDecorator
37 37 from rhodecode.lib.base import BaseController, render
38 38 from rhodecode.model.db import User
39 39 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
40 40 from rhodecode.model.user import UserModel
41 41
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45
46 46 class LoginController(BaseController):
47 47
48 48 def __before__(self):
49 49 super(LoginController, self).__before__()
50 50
51 51 def index(self):
52 #redirect if already logged in
52 # redirect if already logged in
53 53 c.came_from = request.GET.get('came_from', None)
54 54
55 55 if self.rhodecode_user.is_authenticated \
56 56 and self.rhodecode_user.username != 'default':
57 57
58 58 return redirect(url('home'))
59 59
60 60 if request.POST:
61 61 #import Login Form validator class
62 62 login_form = LoginForm()
63 63 try:
64 64 c.form_result = login_form.to_python(dict(request.POST))
65 #form checks for username/password, now we're authenticated
65 # form checks for username/password, now we're authenticated
66 66 username = c.form_result['username']
67 67 user = User.get_by_username(username, case_insensitive=True)
68 68 auth_user = AuthUser(user.user_id)
69 69 auth_user.set_authenticated()
70 70 session['rhodecode_user'] = auth_user
71 71 session.save()
72 72
73 73 log.info('user %s is now authenticated and stored in session',
74 74 username)
75 75 user.update_lastlogin()
76 76
77 77 if c.came_from:
78 78 return redirect(c.came_from)
79 79 else:
80 80 return redirect(url('home'))
81 81
82 82 except formencode.Invalid, errors:
83 83 return htmlfill.render(
84 84 render('/login.html'),
85 85 defaults=errors.value,
86 86 errors=errors.error_dict or {},
87 87 prefix_error=False,
88 88 encoding="UTF-8")
89 89
90 90 return render('/login.html')
91 91
92 92 @HasPermissionAnyDecorator('hg.admin', 'hg.register.auto_activate',
93 93 'hg.register.manual_activate')
94 94 def register(self):
95 95 user_model = UserModel()
96 96 c.auto_active = False
97 97 for perm in User.get_by_username('default').user_perms:
98 98 if perm.permission.permission_name == 'hg.register.auto_activate':
99 99 c.auto_active = True
100 100 break
101 101
102 102 if request.POST:
103 103
104 104 register_form = RegisterForm()()
105 105 try:
106 106 form_result = register_form.to_python(dict(request.POST))
107 107 form_result['active'] = c.auto_active
108 108 user_model.create_registration(form_result)
109 109 h.flash(_('You have successfully registered into rhodecode'),
110 110 category='success')
111 111 return redirect(url('login_home'))
112 112
113 113 except formencode.Invalid, errors:
114 114 return htmlfill.render(
115 115 render('/register.html'),
116 116 defaults=errors.value,
117 117 errors=errors.error_dict or {},
118 118 prefix_error=False,
119 119 encoding="UTF-8")
120 120
121 121 return render('/register.html')
122 122
123 123 def password_reset(self):
124 124 user_model = UserModel()
125 125 if request.POST:
126 126
127 127 password_reset_form = PasswordResetForm()()
128 128 try:
129 129 form_result = password_reset_form.to_python(dict(request.POST))
130 130 user_model.reset_password_link(form_result)
131 131 h.flash(_('Your password reset link was sent'),
132 132 category='success')
133 133 return redirect(url('login_home'))
134 134
135 135 except formencode.Invalid, errors:
136 136 return htmlfill.render(
137 137 render('/password_reset.html'),
138 138 defaults=errors.value,
139 139 errors=errors.error_dict or {},
140 140 prefix_error=False,
141 141 encoding="UTF-8")
142 142
143 143 return render('/password_reset.html')
144 144
145 145 def password_reset_confirmation(self):
146 146
147 147 if request.GET and request.GET.get('key'):
148 148 try:
149 149 user_model = UserModel()
150 150 user = User.get_by_api_key(request.GET.get('key'))
151 151 data = dict(email=user.email)
152 152 user_model.reset_password(data)
153 153 h.flash(_('Your password reset was successful, '
154 154 'new password has been sent to your email'),
155 155 category='success')
156 156 except Exception, e:
157 157 log.error(e)
158 158 return redirect(url('reset_password'))
159 159
160 160 return redirect(url('login_home'))
161 161
162 162 def logout(self):
163 163 del session['rhodecode_user']
164 164 session.save()
165 165 log.info('Logging out and setting user as Empty')
166 166 redirect(url('home'))
@@ -1,663 +1,677 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.lib.auth
4 4 ~~~~~~~~~~~~~~~~~~
5 5
6 6 authentication and permission libraries
7 7
8 8 :created_on: Apr 4, 2010
9 9 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
10 10 :license: GPLv3, see COPYING for more details.
11 11 """
12 12 # This program is free software: you can redistribute it and/or modify
13 13 # it under the terms of the GNU General Public License as published by
14 14 # the Free Software Foundation, either version 3 of the License, or
15 15 # (at your option) any later version.
16 16 #
17 17 # This program is distributed in the hope that it will be useful,
18 18 # but WITHOUT ANY WARRANTY; without even the implied warranty of
19 19 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 20 # GNU General Public License for more details.
21 21 #
22 22 # You should have received a copy of the GNU General Public License
23 23 # along with this program. If not, see <http://www.gnu.org/licenses/>.
24 24
25 25 import random
26 26 import logging
27 27 import traceback
28 28 import hashlib
29 29
30 30 from tempfile import _RandomNameSequence
31 31 from decorator import decorator
32 32
33 33 from pylons import config, session, url, request
34 34 from pylons.controllers.util import abort, redirect
35 35 from pylons.i18n.translation import _
36 36
37 37 from rhodecode import __platform__, PLATFORM_WIN, PLATFORM_OTHERS
38 38
39 39 if __platform__ in PLATFORM_WIN:
40 40 from hashlib import sha256
41 41 if __platform__ in PLATFORM_OTHERS:
42 42 import bcrypt
43 43
44 44 from rhodecode.lib import str2bool, safe_unicode
45 45 from rhodecode.lib.exceptions import LdapPasswordError, LdapUsernameError
46 46 from rhodecode.lib.utils import get_repo_slug
47 47 from rhodecode.lib.auth_ldap import AuthLdap
48 48
49 49 from rhodecode.model import meta
50 50 from rhodecode.model.user import UserModel
51 51 from rhodecode.model.db import Permission, RhodeCodeSettings, User
52 52
53 53 log = logging.getLogger(__name__)
54 54
55 55
56 56 class PasswordGenerator(object):
57 57 """This is a simple class for generating password from
58 58 different sets of characters
59 59 usage:
60 60 passwd_gen = PasswordGenerator()
61 61 #print 8-letter password containing only big and small letters
62 62 of alphabet
63 63 print passwd_gen.gen_password(8, passwd_gen.ALPHABETS_BIG_SMALL)
64 64 """
65 65 ALPHABETS_NUM = r'''1234567890'''
66 66 ALPHABETS_SMALL = r'''qwertyuiopasdfghjklzxcvbnm'''
67 67 ALPHABETS_BIG = r'''QWERTYUIOPASDFGHJKLZXCVBNM'''
68 68 ALPHABETS_SPECIAL = r'''`-=[]\;',./~!@#$%^&*()_+{}|:"<>?'''
69 69 ALPHABETS_FULL = ALPHABETS_BIG + ALPHABETS_SMALL \
70 70 + ALPHABETS_NUM + ALPHABETS_SPECIAL
71 71 ALPHABETS_ALPHANUM = ALPHABETS_BIG + ALPHABETS_SMALL + ALPHABETS_NUM
72 72 ALPHABETS_BIG_SMALL = ALPHABETS_BIG + ALPHABETS_SMALL
73 73 ALPHABETS_ALPHANUM_BIG = ALPHABETS_BIG + ALPHABETS_NUM
74 74 ALPHABETS_ALPHANUM_SMALL = ALPHABETS_SMALL + ALPHABETS_NUM
75 75
76 76 def __init__(self, passwd=''):
77 77 self.passwd = passwd
78 78
79 79 def gen_password(self, len, type):
80 80 self.passwd = ''.join([random.choice(type) for _ in xrange(len)])
81 81 return self.passwd
82 82
83 83
84 84 class RhodeCodeCrypto(object):
85 85
86 86 @classmethod
87 87 def hash_string(cls, str_):
88 88 """
89 89 Cryptographic function used for password hashing based on pybcrypt
90 90 or pycrypto in windows
91 91
92 92 :param password: password to hash
93 93 """
94 94 if __platform__ in PLATFORM_WIN:
95 95 return sha256(str_).hexdigest()
96 96 elif __platform__ in PLATFORM_OTHERS:
97 97 return bcrypt.hashpw(str_, bcrypt.gensalt(10))
98 98 else:
99 99 raise Exception('Unknown or unsupported platform %s' \
100 100 % __platform__)
101 101
102 102 @classmethod
103 103 def hash_check(cls, password, hashed):
104 104 """
105 105 Checks matching password with it's hashed value, runs different
106 106 implementation based on platform it runs on
107 107
108 108 :param password: password
109 109 :param hashed: password in hashed form
110 110 """
111 111
112 112 if __platform__ in PLATFORM_WIN:
113 113 return sha256(password).hexdigest() == hashed
114 114 elif __platform__ in PLATFORM_OTHERS:
115 115 return bcrypt.hashpw(password, hashed) == hashed
116 116 else:
117 117 raise Exception('Unknown or unsupported platform %s' \
118 118 % __platform__)
119 119
120 120
121 121 def get_crypt_password(password):
122 122 return RhodeCodeCrypto.hash_string(password)
123 123
124 124
125 125 def check_password(password, hashed):
126 126 return RhodeCodeCrypto.hash_check(password, hashed)
127 127
128
129 def generate_api_key(username, salt=None):
128 def generate_api_key(str_, salt=None):
129 """
130 Generates API KEY from given string
131
132 :param str_:
133 :param salt:
134 """
135
130 136 if salt is None:
131 137 salt = _RandomNameSequence().next()
132 138
133 return hashlib.sha1(username + salt).hexdigest()
139 return hashlib.sha1(str_ + salt).hexdigest()
134 140
135 141
136 142 def authfunc(environ, username, password):
137 """Dummy authentication function used in Mercurial/Git/ and access control,
143 """
144 Dummy authentication function used in Mercurial/Git/ and access control,
138 145
139 146 :param environ: needed only for using in Basic auth
140 147 """
141 148 return authenticate(username, password)
142 149
143 150
144 151 def authenticate(username, password):
145 """Authentication function used for access control,
152 """
153 Authentication function used for access control,
146 154 firstly checks for db authentication then if ldap is enabled for ldap
147 155 authentication, also creates ldap user if not in database
148 156
149 157 :param username: username
150 158 :param password: password
151 159 """
152 160
153 161 user_model = UserModel()
154 162 user = User.get_by_username(username)
155 163
156 164 log.debug('Authenticating user using RhodeCode account')
157 165 if user is not None and not user.ldap_dn:
158 166 if user.active:
159 167 if user.username == 'default' and user.active:
160 168 log.info('user %s authenticated correctly as anonymous user',
161 169 username)
162 170 return True
163 171
164 172 elif user.username == username and check_password(password,
165 173 user.password):
166 174 log.info('user %s authenticated correctly', username)
167 175 return True
168 176 else:
169 177 log.warning('user %s is disabled', username)
170 178
171 179 else:
172 180 log.debug('Regular authentication failed')
173 181 user_obj = User.get_by_username(username, case_insensitive=True)
174 182
175 183 if user_obj is not None and not user_obj.ldap_dn:
176 184 log.debug('this user already exists as non ldap')
177 185 return False
178 186
179 187 ldap_settings = RhodeCodeSettings.get_ldap_settings()
180 188 #======================================================================
181 189 # FALLBACK TO LDAP AUTH IF ENABLE
182 190 #======================================================================
183 191 if str2bool(ldap_settings.get('ldap_active')):
184 192 log.debug("Authenticating user using ldap")
185 193 kwargs = {
186 194 'server': ldap_settings.get('ldap_host', ''),
187 195 'base_dn': ldap_settings.get('ldap_base_dn', ''),
188 196 'port': ldap_settings.get('ldap_port'),
189 197 'bind_dn': ldap_settings.get('ldap_dn_user'),
190 198 'bind_pass': ldap_settings.get('ldap_dn_pass'),
191 199 'tls_kind': ldap_settings.get('ldap_tls_kind'),
192 200 'tls_reqcert': ldap_settings.get('ldap_tls_reqcert'),
193 201 'ldap_filter': ldap_settings.get('ldap_filter'),
194 202 'search_scope': ldap_settings.get('ldap_search_scope'),
195 203 'attr_login': ldap_settings.get('ldap_attr_login'),
196 204 'ldap_version': 3,
197 205 }
198 206 log.debug('Checking for ldap authentication')
199 207 try:
200 208 aldap = AuthLdap(**kwargs)
201 209 (user_dn, ldap_attrs) = aldap.authenticate_ldap(username,
202 210 password)
203 211 log.debug('Got ldap DN response %s', user_dn)
204 212
205 213 get_ldap_attr = lambda k: ldap_attrs.get(ldap_settings\
206 214 .get(k), [''])[0]
207 215
208 216 user_attrs = {
209 217 'name': safe_unicode(get_ldap_attr('ldap_attr_firstname')),
210 218 'lastname': safe_unicode(get_ldap_attr('ldap_attr_lastname')),
211 219 'email': get_ldap_attr('ldap_attr_email'),
212 220 }
213 221
214 222 if user_model.create_ldap(username, password, user_dn,
215 223 user_attrs):
216 224 log.info('created new ldap user %s', username)
217 225
218 226 return True
219 227 except (LdapUsernameError, LdapPasswordError,):
220 228 pass
221 229 except (Exception,):
222 230 log.error(traceback.format_exc())
223 231 pass
224 232 return False
225 233
226 234 def login_container_auth(username):
227 235 user = User.get_by_username(username)
228 236 if user is None:
229 237 user_model = UserModel()
230 238 user_attrs = {
231 'name': username,
232 'lastname': None,
233 'email': None,
234 }
235 if not user_model.create_for_container_auth(username, user_attrs):
239 'name': username,
240 'lastname': None,
241 'email': None,
242 }
243 user = user_model.create_for_container_auth(username, user_attrs)
244 if not user:
236 245 return None
237 user = User.get_by_username(username)
238 246 log.info('User %s was created by container authentication', username)
239 247
240 248 if not user.active:
241 249 return None
242 250
243 251 user.update_lastlogin()
244 log.debug('User %s is now logged in by container authentication', user.username)
252 log.debug('User %s is now logged in by container authentication',
253 user.username)
245 254 return user
246 255
247 def get_container_username(environ, cfg=config):
256 def get_container_username(environ, cfg):
248 257 from paste.httpheaders import REMOTE_USER
249 258 from paste.deploy.converters import asbool
250 259
260 proxy_pass_enabled = asbool(cfg.get('proxypass_auth_enabled', False))
251 261 username = REMOTE_USER(environ)
252
253 if not username and asbool(cfg.get('proxypass_auth_enabled', False)):
262
263 if not username and proxy_pass_enabled:
254 264 username = environ.get('HTTP_X_FORWARDED_USER')
255 265
256 if username:
257 #Removing realm and domain from username
266 if username and proxy_pass_enabled:
267 # Removing realm and domain from username
258 268 username = username.partition('@')[0]
259 269 username = username.rpartition('\\')[2]
260 270 log.debug('Received username %s from container', username)
261 271
262 272 return username
263 273
264 274 class AuthUser(object):
265 275 """
266 276 A simple object that handles all attributes of user in RhodeCode
267 277
268 278 It does lookup based on API key,given user, or user present in session
269 279 Then it fills all required information for such user. It also checks if
270 280 anonymous access is enabled and if so, it returns default user as logged
271 281 in
272 282 """
273 283
274 284 def __init__(self, user_id=None, api_key=None, username=None):
275 285
276 286 self.user_id = user_id
277 287 self.api_key = None
278 288 self.username = username
279
289
280 290 self.name = ''
281 291 self.lastname = ''
282 292 self.email = ''
283 293 self.is_authenticated = False
284 294 self.admin = False
285 295 self.permissions = {}
286 296 self._api_key = api_key
287 297 self.propagate_data()
288 298
289 299 def propagate_data(self):
290 300 user_model = UserModel()
291 301 self.anonymous_user = User.get_by_username('default')
292 302 is_user_loaded = False
303
304 # try go get user by api key
293 305 if self._api_key and self._api_key != self.anonymous_user.api_key:
294 #try go get user by api key
295 306 log.debug('Auth User lookup by API KEY %s', self._api_key)
296 307 is_user_loaded = user_model.fill_data(self, api_key=self._api_key)
297 elif self.user_id is not None \
298 and self.user_id != self.anonymous_user.user_id:
308 # lookup by userid
309 elif (self.user_id is not None and
310 self.user_id != self.anonymous_user.user_id):
299 311 log.debug('Auth User lookup by USER ID %s', self.user_id)
300 312 is_user_loaded = user_model.fill_data(self, user_id=self.user_id)
313 # lookup by username
301 314 elif self.username:
302 315 log.debug('Auth User lookup by USER NAME %s', self.username)
303 316 dbuser = login_container_auth(self.username)
304 317 if dbuser is not None:
305 318 for k, v in dbuser.get_dict().items():
306 319 setattr(self, k, v)
307 320 self.set_authenticated()
308 321 is_user_loaded = True
309 322
310 323 if not is_user_loaded:
324 # if we cannot authenticate user try anonymous
311 325 if self.anonymous_user.active is True:
312 user_model.fill_data(self,
313 user_id=self.anonymous_user.user_id)
314 #then we set this user is logged in
326 user_model.fill_data(self,user_id=self.anonymous_user.user_id)
327 # then we set this user is logged in
315 328 self.is_authenticated = True
316 329 else:
317 330 self.user_id = None
318 331 self.username = None
319 332 self.is_authenticated = False
320 333
321 334 if not self.username:
322 335 self.username = 'None'
323 336
324 337 log.debug('Auth User is now %s', self)
325 338 user_model.fill_perms(self)
326 339
327 340 @property
328 341 def is_admin(self):
329 342 return self.admin
330 343
331 344 @property
332 345 def full_contact(self):
333 346 return '%s %s <%s>' % (self.name, self.lastname, self.email)
334 347
335 348 def __repr__(self):
336 349 return "<AuthUser('id:%s:%s|%s')>" % (self.user_id, self.username,
337 350 self.is_authenticated)
338 351
339 352 def set_authenticated(self, authenticated=True):
340
341 353 if self.user_id != self.anonymous_user.user_id:
342 354 self.is_authenticated = authenticated
343 355
344 356
345 357 def set_available_permissions(config):
346 """This function will propagate pylons globals with all available defined
358 """
359 This function will propagate pylons globals with all available defined
347 360 permission given in db. We don't want to check each time from db for new
348 361 permissions since adding a new permission also requires application restart
349 362 ie. to decorate new views with the newly created permission
350 363
351 364 :param config: current pylons config instance
352 365
353 366 """
354 367 log.info('getting information about all available permissions')
355 368 try:
356 369 sa = meta.Session()
357 370 all_perms = sa.query(Permission).all()
358 371 except:
359 372 pass
360 373 finally:
361 374 meta.Session.remove()
362 375
363 376 config['available_permissions'] = [x.permission_name for x in all_perms]
364 377
365 378
366 379 #==============================================================================
367 380 # CHECK DECORATORS
368 381 #==============================================================================
369 382 class LoginRequired(object):
370 383 """
371 384 Must be logged in to execute this function else
372 385 redirect to login page
373 386
374 387 :param api_access: if enabled this checks only for valid auth token
375 388 and grants access based on valid token
376 389 """
377 390
378 391 def __init__(self, api_access=False):
379 392 self.api_access = api_access
380 393
381 394 def __call__(self, func):
382 395 return decorator(self.__wrapper, func)
383 396
384 397 def __wrapper(self, func, *fargs, **fkwargs):
385 398 cls = fargs[0]
386 399 user = cls.rhodecode_user
387 400
388 401 api_access_ok = False
389 402 if self.api_access:
390 403 log.debug('Checking API KEY access for %s', cls)
391 404 if user.api_key == request.GET.get('api_key'):
392 405 api_access_ok = True
393 406 else:
394 407 log.debug("API KEY token not valid")
395 408
396 409 log.debug('Checking if %s is authenticated @ %s', user.username, cls)
397 410 if user.is_authenticated or api_access_ok:
398 411 log.debug('user %s is authenticated', user.username)
399 412 return func(*fargs, **fkwargs)
400 413 else:
401 414 log.warn('user %s NOT authenticated', user.username)
402 415 p = url.current()
403 416
404 417 log.debug('redirecting to login page with %s', p)
405 418 return redirect(url('login_home', came_from=p))
406 419
407 420
408 421 class NotAnonymous(object):
409 422 """Must be logged in to execute this function else
410 423 redirect to login page"""
411 424
412 425 def __call__(self, func):
413 426 return decorator(self.__wrapper, func)
414 427
415 428 def __wrapper(self, func, *fargs, **fkwargs):
416 429 cls = fargs[0]
417 430 self.user = cls.rhodecode_user
418 431
419 432 log.debug('Checking if user is not anonymous @%s', cls)
420 433
421 434 anonymous = self.user.username == 'default'
422 435
423 436 if anonymous:
424 437 p = url.current()
425 438
426 439 import rhodecode.lib.helpers as h
427 440 h.flash(_('You need to be a registered user to '
428 441 'perform this action'),
429 442 category='warning')
430 443 return redirect(url('login_home', came_from=p))
431 444 else:
432 445 return func(*fargs, **fkwargs)
433 446
434 447
435 448 class PermsDecorator(object):
436 449 """Base class for controller decorators"""
437 450
438 451 def __init__(self, *required_perms):
439 452 available_perms = config['available_permissions']
440 453 for perm in required_perms:
441 454 if perm not in available_perms:
442 455 raise Exception("'%s' permission is not defined" % perm)
443 456 self.required_perms = set(required_perms)
444 457 self.user_perms = None
445 458
446 459 def __call__(self, func):
447 460 return decorator(self.__wrapper, func)
448 461
449 462 def __wrapper(self, func, *fargs, **fkwargs):
450 463 cls = fargs[0]
451 464 self.user = cls.rhodecode_user
452 465 self.user_perms = self.user.permissions
453 466 log.debug('checking %s permissions %s for %s %s',
454 467 self.__class__.__name__, self.required_perms, cls,
455 468 self.user)
456 469
457 470 if self.check_permissions():
458 471 log.debug('Permission granted for %s %s', cls, self.user)
459 472 return func(*fargs, **fkwargs)
460 473
461 474 else:
462 475 log.warning('Permission denied for %s %s', cls, self.user)
463 476
464 477
465 478 anonymous = self.user.username == 'default'
466 479
467 480 if anonymous:
468 481 p = url.current()
469 482
470 483 import rhodecode.lib.helpers as h
471 484 h.flash(_('You need to be a signed in to '
472 485 'view this page'),
473 486 category='warning')
474 487 return redirect(url('login_home', came_from=p))
475 488
476 489 else:
477 #redirect with forbidden ret code
490 # redirect with forbidden ret code
478 491 return abort(403)
479 492
480 493 def check_permissions(self):
481 494 """Dummy function for overriding"""
482 495 raise Exception('You have to write this function in child class')
483 496
484 497
485 498 class HasPermissionAllDecorator(PermsDecorator):
486 499 """Checks for access permission for all given predicates. All of them
487 500 have to be meet in order to fulfill the request
488 501 """
489 502
490 503 def check_permissions(self):
491 504 if self.required_perms.issubset(self.user_perms.get('global')):
492 505 return True
493 506 return False
494 507
495 508
496 509 class HasPermissionAnyDecorator(PermsDecorator):
497 510 """Checks for access permission for any of given predicates. In order to
498 511 fulfill the request any of predicates must be meet
499 512 """
500 513
501 514 def check_permissions(self):
502 515 if self.required_perms.intersection(self.user_perms.get('global')):
503 516 return True
504 517 return False
505 518
506 519
507 520 class HasRepoPermissionAllDecorator(PermsDecorator):
508 521 """Checks for access permission for all given predicates for specific
509 522 repository. All of them have to be meet in order to fulfill the request
510 523 """
511 524
512 525 def check_permissions(self):
513 526 repo_name = get_repo_slug(request)
514 527 try:
515 528 user_perms = set([self.user_perms['repositories'][repo_name]])
516 529 except KeyError:
517 530 return False
518 531 if self.required_perms.issubset(user_perms):
519 532 return True
520 533 return False
521 534
522 535
523 536 class HasRepoPermissionAnyDecorator(PermsDecorator):
524 537 """Checks for access permission for any of given predicates for specific
525 538 repository. In order to fulfill the request any of predicates must be meet
526 539 """
527 540
528 541 def check_permissions(self):
529 542 repo_name = get_repo_slug(request)
530 543
531 544 try:
532 545 user_perms = set([self.user_perms['repositories'][repo_name]])
533 546 except KeyError:
534 547 return False
535 548 if self.required_perms.intersection(user_perms):
536 549 return True
537 550 return False
538 551
539 552
540 553 #==============================================================================
541 554 # CHECK FUNCTIONS
542 555 #==============================================================================
543 556 class PermsFunction(object):
544 557 """Base function for other check functions"""
545 558
546 559 def __init__(self, *perms):
547 560 available_perms = config['available_permissions']
548 561
549 562 for perm in perms:
550 563 if perm not in available_perms:
551 564 raise Exception("'%s' permission in not defined" % perm)
552 565 self.required_perms = set(perms)
553 566 self.user_perms = None
554 567 self.granted_for = ''
555 568 self.repo_name = None
556 569
557 570 def __call__(self, check_Location=''):
558 571 user = session.get('rhodecode_user', False)
559 572 if not user:
560 573 return False
561 574 self.user_perms = user.permissions
562 575 self.granted_for = user
563 576 log.debug('checking %s %s %s', self.__class__.__name__,
564 577 self.required_perms, user)
565 578
566 579 if self.check_permissions():
567 580 log.debug('Permission granted %s @ %s', self.granted_for,
568 581 check_Location or 'unspecified location')
569 582 return True
570 583
571 584 else:
572 585 log.warning('Permission denied for %s @ %s', self.granted_for,
573 586 check_Location or 'unspecified location')
574 587 return False
575 588
576 589 def check_permissions(self):
577 590 """Dummy function for overriding"""
578 591 raise Exception('You have to write this function in child class')
579 592
580 593
581 594 class HasPermissionAll(PermsFunction):
582 595 def check_permissions(self):
583 596 if self.required_perms.issubset(self.user_perms.get('global')):
584 597 return True
585 598 return False
586 599
587 600
588 601 class HasPermissionAny(PermsFunction):
589 602 def check_permissions(self):
590 603 if self.required_perms.intersection(self.user_perms.get('global')):
591 604 return True
592 605 return False
593 606
594 607
595 608 class HasRepoPermissionAll(PermsFunction):
596 609
597 610 def __call__(self, repo_name=None, check_Location=''):
598 611 self.repo_name = repo_name
599 612 return super(HasRepoPermissionAll, self).__call__(check_Location)
600 613
601 614 def check_permissions(self):
602 615 if not self.repo_name:
603 616 self.repo_name = get_repo_slug(request)
604 617
605 618 try:
606 619 self.user_perms = set([self.user_perms['reposit'
607 620 'ories'][self.repo_name]])
608 621 except KeyError:
609 622 return False
610 623 self.granted_for = self.repo_name
611 624 if self.required_perms.issubset(self.user_perms):
612 625 return True
613 626 return False
614 627
615 628
616 629 class HasRepoPermissionAny(PermsFunction):
617 630
618 631 def __call__(self, repo_name=None, check_Location=''):
619 632 self.repo_name = repo_name
620 633 return super(HasRepoPermissionAny, self).__call__(check_Location)
621 634
622 635 def check_permissions(self):
623 636 if not self.repo_name:
624 637 self.repo_name = get_repo_slug(request)
625 638
626 639 try:
627 640 self.user_perms = set([self.user_perms['reposi'
628 641 'tories'][self.repo_name]])
629 642 except KeyError:
630 643 return False
631 644 self.granted_for = self.repo_name
632 645 if self.required_perms.intersection(self.user_perms):
633 646 return True
634 647 return False
635 648
636 649
637 650 #==============================================================================
638 651 # SPECIAL VERSION TO HANDLE MIDDLEWARE AUTH
639 652 #==============================================================================
640 653 class HasPermissionAnyMiddleware(object):
641 654 def __init__(self, *perms):
642 655 self.required_perms = set(perms)
643 656
644 657 def __call__(self, user, repo_name):
645 658 usr = AuthUser(user.user_id)
646 659 try:
647 660 self.user_perms = set([usr.permissions['repositories'][repo_name]])
648 661 except:
649 662 self.user_perms = set()
650 663 self.granted_for = ''
651 664 self.username = user.username
652 665 self.repo_name = repo_name
653 666 return self.check_permissions()
654 667
655 668 def check_permissions(self):
656 669 log.debug('checking mercurial protocol '
657 670 'permissions %s for user:%s repository:%s', self.user_perms,
658 671 self.username, self.repo_name)
659 672 if self.required_perms.intersection(self.user_perms):
660 673 log.debug('permission granted')
661 674 return True
662 675 log.debug('permission denied')
663 676 return False
677
@@ -1,92 +1,91 b''
1 1 """The base Controller API
2 2
3 3 Provides the BaseController class for subclassing.
4 4 """
5 5 import logging
6 6 import time
7 7 from pylons import config, tmpl_context as c, request, session, url
8 8 from pylons.controllers import WSGIController
9 9 from pylons.controllers.util import redirect
10 10 from pylons.templating import render_mako as render
11 11 from paste.deploy.converters import asbool
12 12
13 13 from rhodecode import __version__
14 14 from rhodecode.lib.auth import AuthUser, get_container_username
15 15 from rhodecode.lib.utils import get_repo_slug
16 16 from rhodecode.model import meta
17 17 from rhodecode.model.scm import ScmModel
18 18 from rhodecode import BACKENDS
19 19 from rhodecode.model.db import Repository
20 20
21 21 log = logging.getLogger(__name__)
22 22
23 23 class BaseController(WSGIController):
24 24
25 25 def __before__(self):
26 26 c.rhodecode_version = __version__
27 27 c.rhodecode_name = config.get('rhodecode_title')
28 28 c.ga_code = config.get('rhodecode_ga_code')
29 29 c.repo_name = get_repo_slug(request)
30 30 c.backends = BACKENDS.keys()
31 31 self.cut_off_limit = int(config.get('cut_off_limit'))
32 32
33 33 self.sa = meta.Session()
34 34 self.scm_model = ScmModel(self.sa)
35 35
36 #c.unread_journal = scm_model.get_unread_journal()
37
38 36 def __call__(self, environ, start_response):
39 37 """Invoke the Controller"""
40 38 # WSGIController.__call__ dispatches to the Controller method
41 39 # the request is routed to. This routing information is
42 40 # available in environ['pylons.routes_dict']
43 41 start = time.time()
44 42 try:
45 # putting this here makes sure that we update permissions each time
43 # make sure that we update permissions each time we call controller
46 44 api_key = request.GET.get('api_key')
47 45 user_id = getattr(session.get('rhodecode_user'), 'user_id', None)
48 46 if asbool(config.get('container_auth_enabled', False)):
49 47 username = get_container_username(environ)
50 48 else:
51 49 username = None
52
53 self.rhodecode_user = c.rhodecode_user = AuthUser(user_id, api_key, username)
50 auth_user = AuthUser(user_id, api_key, username)
51 self.rhodecode_user = c.rhodecode_user = auth_user
54 52 if not self.rhodecode_user.is_authenticated and \
55 53 self.rhodecode_user.user_id is not None:
56 54 self.rhodecode_user.set_authenticated(
57 55 getattr(session.get('rhodecode_user'),
58 56 'is_authenticated', False))
59 57 session['rhodecode_user'] = self.rhodecode_user
60 58 session.save()
61 59 return WSGIController.__call__(self, environ, start_response)
62 60 finally:
63 61 log.debug('Request time: %.3fs' % (time.time()-start))
64 62 meta.Session.remove()
65 63
66 64
67 65 class BaseRepoController(BaseController):
68 66 """
69 Base class for controllers responsible for loading all needed data
70 for those controllers, loaded items are
67 Base class for controllers responsible for loading all needed data for
68 repository loaded items are
71 69
72 c.rhodecode_repo: instance of scm repository (taken from cache)
73
70 c.rhodecode_repo: instance of scm repository
71 c.rhodecode_db_repo: instance of db
72 c.repository_followers: number of followers
73 c.repository_forks: number of forks
74 74 """
75 75
76 76 def __before__(self):
77 77 super(BaseRepoController, self).__before__()
78 78 if c.repo_name:
79 79
80 80 c.rhodecode_db_repo = Repository.get_by_repo_name(c.repo_name)
81 81 c.rhodecode_repo = c.rhodecode_db_repo.scm_instance
82 82
83 83 if c.rhodecode_repo is None:
84 84 log.error('%s this repository is present in database but it '
85 85 'cannot be created as an scm instance', c.repo_name)
86 86
87 87 redirect(url('home'))
88 88
89 c.repository_followers = \
90 self.scm_model.get_followers(c.repo_name)
89 c.repository_followers = self.scm_model.get_followers(c.repo_name)
91 90 c.repository_forks = self.scm_model.get_forks(c.repo_name)
92 91
@@ -1,694 +1,694 b''
1 1 """ this is forms validation classes
2 2 http://formencode.org/module-formencode.validators.html
3 3 for list off all availible validators
4 4
5 5 we can create our own validators
6 6
7 7 The table below outlines the options which can be used in a schema in addition to the validators themselves
8 8 pre_validators [] These validators will be applied before the schema
9 9 chained_validators [] These validators will be applied after the schema
10 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 11 filter_extra_fields False If True, then keys that aren't associated with a validator are removed
12 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 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 16 <name> = formencode.validators.<name of validator>
17 17 <name> must equal form name
18 18 list=[1,2,3,4,5]
19 19 for SELECT use formencode.All(OneOf(list), Int())
20 20
21 21 """
22 22 import os
23 23 import re
24 24 import logging
25 25 import traceback
26 26
27 27 import formencode
28 28 from formencode import All
29 29 from formencode.validators import UnicodeString, OneOf, Int, Number, Regex, \
30 30 Email, Bool, StringBoolean, Set
31 31
32 32 from pylons.i18n.translation import _
33 33 from webhelpers.pylonslib.secure_form import authentication_token
34 34
35 35 from rhodecode.config.routing import ADMIN_PREFIX
36 36 from rhodecode.lib.utils import repo_name_slug
37 37 from rhodecode.lib.auth import authenticate, get_crypt_password
38 38 from rhodecode.lib.exceptions import LdapImportError
39 39 from rhodecode.model.user import UserModel
40 40 from rhodecode.model.repo import RepoModel
41 41 from rhodecode.model.db import User, UsersGroup, Group
42 42 from rhodecode import BACKENDS
43 43
44 44 log = logging.getLogger(__name__)
45 45
46 46 #this is needed to translate the messages using _() in validators
47 47 class State_obj(object):
48 48 _ = staticmethod(_)
49 49
50 50 #==============================================================================
51 51 # VALIDATORS
52 52 #==============================================================================
53 53 class ValidAuthToken(formencode.validators.FancyValidator):
54 54 messages = {'invalid_token':_('Token mismatch')}
55 55
56 56 def validate_python(self, value, state):
57 57
58 58 if value != authentication_token():
59 59 raise formencode.Invalid(self.message('invalid_token', state,
60 60 search_number=value), value, state)
61 61
62 62 def ValidUsername(edit, old_data):
63 63 class _ValidUsername(formencode.validators.FancyValidator):
64 64
65 65 def validate_python(self, value, state):
66 66 if value in ['default', 'new_user']:
67 67 raise formencode.Invalid(_('Invalid username'), value, state)
68 68 #check if user is unique
69 69 old_un = None
70 70 if edit:
71 71 old_un = UserModel().get(old_data.get('user_id')).username
72 72
73 73 if old_un != value or not edit:
74 74 if User.get_by_username(value, case_insensitive=True):
75 75 raise formencode.Invalid(_('This username already '
76 76 'exists') , value, state)
77 77
78 78 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
79 79 raise formencode.Invalid(_('Username may only contain '
80 80 'alphanumeric characters '
81 81 'underscores, periods or dashes '
82 82 'and must begin with alphanumeric '
83 83 'character'), value, state)
84 84
85 85 return _ValidUsername
86 86
87 87
88 88 def ValidUsersGroup(edit, old_data):
89 89
90 90 class _ValidUsersGroup(formencode.validators.FancyValidator):
91 91
92 92 def validate_python(self, value, state):
93 93 if value in ['default']:
94 94 raise formencode.Invalid(_('Invalid group name'), value, state)
95 95 #check if group is unique
96 96 old_ugname = None
97 97 if edit:
98 98 old_ugname = UsersGroup.get(
99 99 old_data.get('users_group_id')).users_group_name
100 100
101 101 if old_ugname != value or not edit:
102 102 if UsersGroup.get_by_group_name(value, cache=False,
103 103 case_insensitive=True):
104 104 raise formencode.Invalid(_('This users group '
105 105 'already exists') , value,
106 106 state)
107 107
108 108
109 109 if re.match(r'^[a-zA-Z0-9]{1}[a-zA-Z0-9\-\_\.]+$', value) is None:
110 110 raise formencode.Invalid(_('Group name may only contain '
111 111 'alphanumeric characters '
112 112 'underscores, periods or dashes '
113 113 'and must begin with alphanumeric '
114 114 'character'), value, state)
115 115
116 116 return _ValidUsersGroup
117 117
118 118
119 119 def ValidReposGroup(edit, old_data):
120 120 class _ValidReposGroup(formencode.validators.FancyValidator):
121 121
122 122 def validate_python(self, value, state):
123 123 #TODO WRITE VALIDATIONS
124 124 group_name = value.get('group_name')
125 125 group_parent_id = int(value.get('group_parent_id') or -1)
126 126
127 127 # slugify repo group just in case :)
128 128 slug = repo_name_slug(group_name)
129 129
130 130 # check for parent of self
131 131 if edit and old_data['group_id'] == group_parent_id:
132 132 e_dict = {'group_parent_id':_('Cannot assign this group '
133 133 'as parent')}
134 134 raise formencode.Invalid('', value, state,
135 135 error_dict=e_dict)
136 136
137 137 old_gname = None
138 138 if edit:
139 139 old_gname = Group.get(
140 140 old_data.get('group_id')).group_name
141 141
142 142 if old_gname != group_name or not edit:
143 143 # check filesystem
144 144 gr = Group.query().filter(Group.group_name == slug)\
145 145 .filter(Group.group_parent_id == group_parent_id).scalar()
146 146
147 147 if gr:
148 148 e_dict = {'group_name':_('This group already exists')}
149 149 raise formencode.Invalid('', value, state,
150 150 error_dict=e_dict)
151 151
152 152 return _ValidReposGroup
153 153
154 154 class ValidPassword(formencode.validators.FancyValidator):
155 155
156 156 def to_python(self, value, state):
157 157
158 158 if value:
159 159
160 160 if value.get('password'):
161 161 try:
162 162 value['password'] = get_crypt_password(value['password'])
163 163 except UnicodeEncodeError:
164 164 e_dict = {'password':_('Invalid characters in password')}
165 165 raise formencode.Invalid('', value, state, error_dict=e_dict)
166 166
167 167 if value.get('password_confirmation'):
168 168 try:
169 169 value['password_confirmation'] = \
170 170 get_crypt_password(value['password_confirmation'])
171 171 except UnicodeEncodeError:
172 172 e_dict = {'password_confirmation':_('Invalid characters in password')}
173 173 raise formencode.Invalid('', value, state, error_dict=e_dict)
174 174
175 175 if value.get('new_password'):
176 176 try:
177 177 value['new_password'] = \
178 178 get_crypt_password(value['new_password'])
179 179 except UnicodeEncodeError:
180 180 e_dict = {'new_password':_('Invalid characters in password')}
181 181 raise formencode.Invalid('', value, state, error_dict=e_dict)
182 182
183 183 return value
184 184
185 185 class ValidPasswordsMatch(formencode.validators.FancyValidator):
186 186
187 187 def validate_python(self, value, state):
188 188
189 189 pass_val = value.get('password') or value.get('new_password')
190 190 if pass_val != value['password_confirmation']:
191 191 e_dict = {'password_confirmation':
192 192 _('Passwords do not match')}
193 193 raise formencode.Invalid('', value, state, error_dict=e_dict)
194 194
195 195 class ValidAuth(formencode.validators.FancyValidator):
196 196 messages = {
197 197 'invalid_password':_('invalid password'),
198 198 'invalid_login':_('invalid user name'),
199 199 'disabled_account':_('Your account is disabled')
200 200 }
201 201
202 202 # error mapping
203 203 e_dict = {'username':messages['invalid_login'],
204 204 'password':messages['invalid_password']}
205 205 e_dict_disable = {'username':messages['disabled_account']}
206 206
207 207 def validate_python(self, value, state):
208 208 password = value['password']
209 209 username = value['username']
210 210 user = User.get_by_username(username)
211
211
212 212 if authenticate(username, password):
213 213 return value
214 214 else:
215 215 if user and user.active is False:
216 216 log.warning('user %s is disabled', username)
217 217 raise formencode.Invalid(self.message('disabled_account',
218 218 state=State_obj),
219 219 value, state,
220 220 error_dict=self.e_dict_disable)
221 221 else:
222 222 log.warning('user %s not authenticated', username)
223 223 raise formencode.Invalid(self.message('invalid_password',
224 224 state=State_obj), value, state,
225 225 error_dict=self.e_dict)
226 226
227 227 class ValidRepoUser(formencode.validators.FancyValidator):
228 228
229 229 def to_python(self, value, state):
230 230 try:
231 231 User.query().filter(User.active == True)\
232 232 .filter(User.username == value).one()
233 233 except Exception:
234 234 raise formencode.Invalid(_('This username is not valid'),
235 235 value, state)
236 236 return value
237 237
238 238 def ValidRepoName(edit, old_data):
239 239 class _ValidRepoName(formencode.validators.FancyValidator):
240 240 def to_python(self, value, state):
241 241
242 242 repo_name = value.get('repo_name')
243 243
244 244 slug = repo_name_slug(repo_name)
245 245 if slug in [ADMIN_PREFIX, '']:
246 246 e_dict = {'repo_name': _('This repository name is disallowed')}
247 247 raise formencode.Invalid('', value, state, error_dict=e_dict)
248 248
249 249
250 250 if value.get('repo_group'):
251 251 gr = Group.get(value.get('repo_group'))
252 252 group_path = gr.full_path
253 253 # value needs to be aware of group name in order to check
254 254 # db key This is an actual just the name to store in the
255 255 # database
256 256 repo_name_full = group_path + Group.url_sep() + repo_name
257 257
258 258 else:
259 259 group_path = ''
260 260 repo_name_full = repo_name
261 261
262 262
263 263 value['repo_name_full'] = repo_name_full
264 264 rename = old_data.get('repo_name') != repo_name_full
265 265 create = not edit
266 266 if rename or create:
267 267
268 268 if group_path != '':
269 269 if RepoModel().get_by_repo_name(repo_name_full,):
270 270 e_dict = {'repo_name':_('This repository already '
271 271 'exists in a group "%s"') %
272 272 gr.group_name}
273 273 raise formencode.Invalid('', value, state,
274 274 error_dict=e_dict)
275 275 elif Group.get_by_group_name(repo_name_full):
276 276 e_dict = {'repo_name':_('There is a group with this'
277 277 ' name already "%s"') %
278 278 repo_name_full}
279 279 raise formencode.Invalid('', value, state,
280 280 error_dict=e_dict)
281 281
282 282 elif RepoModel().get_by_repo_name(repo_name_full):
283 283 e_dict = {'repo_name':_('This repository '
284 284 'already exists')}
285 285 raise formencode.Invalid('', value, state,
286 286 error_dict=e_dict)
287 287
288 288 return value
289 289
290 290 return _ValidRepoName
291 291
292 292 def ValidForkName():
293 293 class _ValidForkName(formencode.validators.FancyValidator):
294 294 def to_python(self, value, state):
295 295
296 296 repo_name = value.get('fork_name')
297 297
298 298 slug = repo_name_slug(repo_name)
299 299 if slug in [ADMIN_PREFIX, '']:
300 300 e_dict = {'repo_name': _('This repository name is disallowed')}
301 301 raise formencode.Invalid('', value, state, error_dict=e_dict)
302 302
303 303 if RepoModel().get_by_repo_name(repo_name):
304 304 e_dict = {'fork_name':_('This repository '
305 305 'already exists')}
306 306 raise formencode.Invalid('', value, state,
307 307 error_dict=e_dict)
308 308 return value
309 309 return _ValidForkName
310 310
311 311
312 312 def SlugifyName():
313 313 class _SlugifyName(formencode.validators.FancyValidator):
314 314
315 315 def to_python(self, value, state):
316 316 return repo_name_slug(value)
317 317
318 318 return _SlugifyName
319 319
320 320 def ValidCloneUri():
321 321 from mercurial.httprepo import httprepository, httpsrepository
322 322 from rhodecode.lib.utils import make_ui
323 323
324 324 class _ValidCloneUri(formencode.validators.FancyValidator):
325 325
326 326 def to_python(self, value, state):
327 327 if not value:
328 328 pass
329 329 elif value.startswith('https'):
330 330 try:
331 331 httpsrepository(make_ui('db'), value).capabilities
332 332 except Exception, e:
333 333 log.error(traceback.format_exc())
334 334 raise formencode.Invalid(_('invalid clone url'), value,
335 335 state)
336 336 elif value.startswith('http'):
337 337 try:
338 338 httprepository(make_ui('db'), value).capabilities
339 339 except Exception, e:
340 340 log.error(traceback.format_exc())
341 341 raise formencode.Invalid(_('invalid clone url'), value,
342 342 state)
343 343 else:
344 344 raise formencode.Invalid(_('Invalid clone url, provide a '
345 345 'valid clone http\s url'), value,
346 346 state)
347 347 return value
348 348
349 349 return _ValidCloneUri
350 350
351 351 def ValidForkType(old_data):
352 352 class _ValidForkType(formencode.validators.FancyValidator):
353 353
354 354 def to_python(self, value, state):
355 355 if old_data['repo_type'] != value:
356 356 raise formencode.Invalid(_('Fork have to be the same '
357 357 'type as original'), value, state)
358 358
359 359 return value
360 360 return _ValidForkType
361 361
362 362 class ValidPerms(formencode.validators.FancyValidator):
363 363 messages = {'perm_new_member_name':_('This username or users group name'
364 364 ' is not valid')}
365 365
366 366 def to_python(self, value, state):
367 367 perms_update = []
368 368 perms_new = []
369 369 #build a list of permission to update and new permission to create
370 370 for k, v in value.items():
371 371 #means new added member to permissions
372 372 if k.startswith('perm_new_member'):
373 373 new_perm = value.get('perm_new_member', False)
374 374 new_member = value.get('perm_new_member_name', False)
375 375 new_type = value.get('perm_new_member_type')
376 376
377 377 if new_member and new_perm:
378 378 if (new_member, new_perm, new_type) not in perms_new:
379 379 perms_new.append((new_member, new_perm, new_type))
380 380 elif k.startswith('u_perm_') or k.startswith('g_perm_'):
381 381 member = k[7:]
382 382 t = {'u':'user',
383 383 'g':'users_group'}[k[0]]
384 384 if member == 'default':
385 385 if value['private']:
386 386 #set none for default when updating to private repo
387 387 v = 'repository.none'
388 388 perms_update.append((member, v, t))
389 389
390 390 value['perms_updates'] = perms_update
391 391 value['perms_new'] = perms_new
392 392
393 393 #update permissions
394 394 for k, v, t in perms_new:
395 395 try:
396 396 if t is 'user':
397 397 self.user_db = User.query()\
398 398 .filter(User.active == True)\
399 399 .filter(User.username == k).one()
400 400 if t is 'users_group':
401 401 self.user_db = UsersGroup.query()\
402 402 .filter(UsersGroup.users_group_active == True)\
403 403 .filter(UsersGroup.users_group_name == k).one()
404 404
405 405 except Exception:
406 406 msg = self.message('perm_new_member_name',
407 407 state=State_obj)
408 408 raise formencode.Invalid(msg, value, state,
409 409 error_dict={'perm_new_member_name':msg})
410 410 return value
411 411
412 412 class ValidSettings(formencode.validators.FancyValidator):
413 413
414 414 def to_python(self, value, state):
415 415 #settings form can't edit user
416 416 if value.has_key('user'):
417 417 del['value']['user']
418 418
419 419 return value
420 420
421 421 class ValidPath(formencode.validators.FancyValidator):
422 422 def to_python(self, value, state):
423 423
424 424 if not os.path.isdir(value):
425 425 msg = _('This is not a valid path')
426 426 raise formencode.Invalid(msg, value, state,
427 427 error_dict={'paths_root_path':msg})
428 428 return value
429 429
430 430 def UniqSystemEmail(old_data):
431 431 class _UniqSystemEmail(formencode.validators.FancyValidator):
432 432 def to_python(self, value, state):
433 433 value = value.lower()
434 434 if old_data.get('email') != value:
435 435 user = User.query().filter(User.email == value).scalar()
436 436 if user:
437 437 raise formencode.Invalid(
438 438 _("This e-mail address is already taken"),
439 439 value, state)
440 440 return value
441 441
442 442 return _UniqSystemEmail
443 443
444 444 class ValidSystemEmail(formencode.validators.FancyValidator):
445 445 def to_python(self, value, state):
446 446 value = value.lower()
447 447 user = User.query().filter(User.email == value).scalar()
448 448 if user is None:
449 449 raise formencode.Invalid(_("This e-mail address doesn't exist.") ,
450 450 value, state)
451 451
452 452 return value
453 453
454 454 class LdapLibValidator(formencode.validators.FancyValidator):
455 455
456 456 def to_python(self, value, state):
457 457
458 458 try:
459 459 import ldap
460 460 except ImportError:
461 461 raise LdapImportError
462 462 return value
463 463
464 464 class AttrLoginValidator(formencode.validators.FancyValidator):
465 465
466 466 def to_python(self, value, state):
467 467
468 468 if not value or not isinstance(value, (str, unicode)):
469 469 raise formencode.Invalid(_("The LDAP Login attribute of the CN "
470 470 "must be specified - this is the name "
471 471 "of the attribute that is equivalent "
472 472 "to 'username'"),
473 473 value, state)
474 474
475 475 return value
476 476
477 477 #===============================================================================
478 478 # FORMS
479 479 #===============================================================================
480 480 class LoginForm(formencode.Schema):
481 481 allow_extra_fields = True
482 482 filter_extra_fields = True
483 483 username = UnicodeString(
484 484 strip=True,
485 485 min=1,
486 486 not_empty=True,
487 487 messages={
488 488 'empty':_('Please enter a login'),
489 489 'tooShort':_('Enter a value %(min)i characters long or more')}
490 490 )
491 491
492 492 password = UnicodeString(
493 493 strip=True,
494 494 min=3,
495 495 not_empty=True,
496 496 messages={
497 497 'empty':_('Please enter a password'),
498 498 'tooShort':_('Enter %(min)i characters or more')}
499 499 )
500 500
501 501 chained_validators = [ValidAuth]
502 502
503 503 def UserForm(edit=False, old_data={}):
504 504 class _UserForm(formencode.Schema):
505 505 allow_extra_fields = True
506 506 filter_extra_fields = True
507 507 username = All(UnicodeString(strip=True, min=1, not_empty=True),
508 508 ValidUsername(edit, old_data))
509 509 if edit:
510 510 new_password = All(UnicodeString(strip=True, min=6, not_empty=False))
511 511 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
512 512 admin = StringBoolean(if_missing=False)
513 513 else:
514 514 password = All(UnicodeString(strip=True, min=6, not_empty=True))
515 515 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=False))
516 516
517 517 active = StringBoolean(if_missing=False)
518 518 name = UnicodeString(strip=True, min=1, not_empty=True)
519 519 lastname = UnicodeString(strip=True, min=1, not_empty=True)
520 520 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
521 521
522 522 chained_validators = [ValidPasswordsMatch, ValidPassword]
523 523
524 524 return _UserForm
525 525
526 526
527 527 def UsersGroupForm(edit=False, old_data={}, available_members=[]):
528 528 class _UsersGroupForm(formencode.Schema):
529 529 allow_extra_fields = True
530 530 filter_extra_fields = True
531 531
532 532 users_group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
533 533 ValidUsersGroup(edit, old_data))
534 534
535 535 users_group_active = StringBoolean(if_missing=False)
536 536
537 537 if edit:
538 538 users_group_members = OneOf(available_members, hideList=False,
539 539 testValueList=True,
540 540 if_missing=None, not_empty=False)
541 541
542 542 return _UsersGroupForm
543 543
544 544 def ReposGroupForm(edit=False, old_data={}, available_groups=[]):
545 545 class _ReposGroupForm(formencode.Schema):
546 546 allow_extra_fields = True
547 547 filter_extra_fields = True
548 548
549 549 group_name = All(UnicodeString(strip=True, min=1, not_empty=True),
550 550 SlugifyName())
551 551 group_description = UnicodeString(strip=True, min=1,
552 552 not_empty=True)
553 553 group_parent_id = OneOf(available_groups, hideList=False,
554 554 testValueList=True,
555 555 if_missing=None, not_empty=False)
556 556
557 557 chained_validators = [ValidReposGroup(edit, old_data)]
558 558
559 559 return _ReposGroupForm
560 560
561 561 def RegisterForm(edit=False, old_data={}):
562 562 class _RegisterForm(formencode.Schema):
563 563 allow_extra_fields = True
564 564 filter_extra_fields = True
565 565 username = All(ValidUsername(edit, old_data),
566 566 UnicodeString(strip=True, min=1, not_empty=True))
567 567 password = All(UnicodeString(strip=True, min=6, not_empty=True))
568 568 password_confirmation = All(UnicodeString(strip=True, min=6, not_empty=True))
569 569 active = StringBoolean(if_missing=False)
570 570 name = UnicodeString(strip=True, min=1, not_empty=True)
571 571 lastname = UnicodeString(strip=True, min=1, not_empty=True)
572 572 email = All(Email(not_empty=True), UniqSystemEmail(old_data))
573 573
574 574 chained_validators = [ValidPasswordsMatch, ValidPassword]
575 575
576 576 return _RegisterForm
577 577
578 578 def PasswordResetForm():
579 579 class _PasswordResetForm(formencode.Schema):
580 580 allow_extra_fields = True
581 581 filter_extra_fields = True
582 582 email = All(ValidSystemEmail(), Email(not_empty=True))
583 583 return _PasswordResetForm
584 584
585 585 def RepoForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
586 586 repo_groups=[]):
587 587 class _RepoForm(formencode.Schema):
588 588 allow_extra_fields = True
589 589 filter_extra_fields = False
590 590 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
591 591 SlugifyName())
592 592 clone_uri = All(UnicodeString(strip=True, min=1, not_empty=False),
593 593 ValidCloneUri()())
594 594 repo_group = OneOf(repo_groups, hideList=True)
595 595 repo_type = OneOf(supported_backends)
596 596 description = UnicodeString(strip=True, min=1, not_empty=True)
597 597 private = StringBoolean(if_missing=False)
598 598 enable_statistics = StringBoolean(if_missing=False)
599 599 enable_downloads = StringBoolean(if_missing=False)
600 600
601 601 if edit:
602 602 #this is repo owner
603 603 user = All(UnicodeString(not_empty=True), ValidRepoUser)
604 604
605 605 chained_validators = [ValidRepoName(edit, old_data), ValidPerms]
606 606 return _RepoForm
607 607
608 608 def RepoForkForm(edit=False, old_data={}, supported_backends=BACKENDS.keys()):
609 609 class _RepoForkForm(formencode.Schema):
610 610 allow_extra_fields = True
611 611 filter_extra_fields = False
612 612 fork_name = All(UnicodeString(strip=True, min=1, not_empty=True),
613 613 SlugifyName())
614 614 description = UnicodeString(strip=True, min=1, not_empty=True)
615 615 private = StringBoolean(if_missing=False)
616 616 repo_type = All(ValidForkType(old_data), OneOf(supported_backends))
617 617
618 618 chained_validators = [ValidForkName()]
619 619
620 620 return _RepoForkForm
621 621
622 622 def RepoSettingsForm(edit=False, old_data={}, supported_backends=BACKENDS.keys(),
623 623 repo_groups=[]):
624 624 class _RepoForm(formencode.Schema):
625 625 allow_extra_fields = True
626 626 filter_extra_fields = False
627 627 repo_name = All(UnicodeString(strip=True, min=1, not_empty=True),
628 628 SlugifyName())
629 629 description = UnicodeString(strip=True, min=1, not_empty=True)
630 630 repo_group = OneOf(repo_groups, hideList=True)
631 631 private = StringBoolean(if_missing=False)
632 632
633 633 chained_validators = [ValidRepoName(edit, old_data), ValidPerms,
634 634 ValidSettings]
635 635 return _RepoForm
636 636
637 637
638 638 def ApplicationSettingsForm():
639 639 class _ApplicationSettingsForm(formencode.Schema):
640 640 allow_extra_fields = True
641 641 filter_extra_fields = False
642 642 rhodecode_title = UnicodeString(strip=True, min=1, not_empty=True)
643 643 rhodecode_realm = UnicodeString(strip=True, min=1, not_empty=True)
644 644 rhodecode_ga_code = UnicodeString(strip=True, min=1, not_empty=False)
645 645
646 646 return _ApplicationSettingsForm
647 647
648 648 def ApplicationUiSettingsForm():
649 649 class _ApplicationUiSettingsForm(formencode.Schema):
650 650 allow_extra_fields = True
651 651 filter_extra_fields = False
652 652 web_push_ssl = OneOf(['true', 'false'], if_missing='false')
653 653 paths_root_path = All(ValidPath(), UnicodeString(strip=True, min=1, not_empty=True))
654 654 hooks_changegroup_update = OneOf(['True', 'False'], if_missing=False)
655 655 hooks_changegroup_repo_size = OneOf(['True', 'False'], if_missing=False)
656 656 hooks_pretxnchangegroup_push_logger = OneOf(['True', 'False'], if_missing=False)
657 657 hooks_preoutgoing_pull_logger = OneOf(['True', 'False'], if_missing=False)
658 658
659 659 return _ApplicationUiSettingsForm
660 660
661 661 def DefaultPermissionsForm(perms_choices, register_choices, create_choices):
662 662 class _DefaultPermissionsForm(formencode.Schema):
663 663 allow_extra_fields = True
664 664 filter_extra_fields = True
665 665 overwrite_default = StringBoolean(if_missing=False)
666 666 anonymous = OneOf(['True', 'False'], if_missing=False)
667 667 default_perm = OneOf(perms_choices)
668 668 default_register = OneOf(register_choices)
669 669 default_create = OneOf(create_choices)
670 670
671 671 return _DefaultPermissionsForm
672 672
673 673
674 674 def LdapSettingsForm(tls_reqcert_choices, search_scope_choices, tls_kind_choices):
675 675 class _LdapSettingsForm(formencode.Schema):
676 676 allow_extra_fields = True
677 677 filter_extra_fields = True
678 678 pre_validators = [LdapLibValidator]
679 679 ldap_active = StringBoolean(if_missing=False)
680 680 ldap_host = UnicodeString(strip=True,)
681 681 ldap_port = Number(strip=True,)
682 682 ldap_tls_kind = OneOf(tls_kind_choices)
683 683 ldap_tls_reqcert = OneOf(tls_reqcert_choices)
684 684 ldap_dn_user = UnicodeString(strip=True,)
685 685 ldap_dn_pass = UnicodeString(strip=True,)
686 686 ldap_base_dn = UnicodeString(strip=True,)
687 687 ldap_filter = UnicodeString(strip=True,)
688 688 ldap_search_scope = OneOf(search_scope_choices)
689 689 ldap_attr_login = All(AttrLoginValidator, UnicodeString(strip=True,))
690 690 ldap_attr_firstname = UnicodeString(strip=True,)
691 691 ldap_attr_lastname = UnicodeString(strip=True,)
692 692 ldap_attr_email = UnicodeString(strip=True,)
693 693
694 694 return _LdapSettingsForm
@@ -1,422 +1,422 b''
1 1 # -*- coding: utf-8 -*-
2 2 """
3 3 rhodecode.model.user
4 4 ~~~~~~~~~~~~~~~~~~~~
5 5
6 6 users model for RhodeCode
7 7
8 8 :created_on: Apr 9, 2010
9 9 :author: marcink
10 10 :copyright: (C) 2009-2011 Marcin Kuzminski <marcin@python-works.com>
11 11 :license: GPLv3, see COPYING for more details.
12 12 """
13 13 # This program is free software: you can redistribute it and/or modify
14 14 # it under the terms of the GNU General Public License as published by
15 15 # the Free Software Foundation, either version 3 of the License, or
16 16 # (at your option) any later version.
17 17 #
18 18 # This program is distributed in the hope that it will be useful,
19 19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 21 # GNU General Public License for more details.
22 22 #
23 23 # You should have received a copy of the GNU General Public License
24 24 # along with this program. If not, see <http://www.gnu.org/licenses/>.
25 25
26 26 import logging
27 27 import traceback
28 28
29 29 from pylons.i18n.translation import _
30 30
31 31 from rhodecode.lib import safe_unicode
32 32 from rhodecode.model import BaseModel
33 33 from rhodecode.model.caching_query import FromCache
34 34 from rhodecode.model.db import User, RepoToPerm, Repository, Permission, \
35 35 UserToPerm, UsersGroupRepoToPerm, UsersGroupToPerm, UsersGroupMember
36 36 from rhodecode.lib.exceptions import DefaultUserException, \
37 37 UserOwnsReposException
38 38
39 39 from sqlalchemy.exc import DatabaseError
40 40 from rhodecode.lib import generate_api_key
41 41 from sqlalchemy.orm import joinedload
42 42
43 43 log = logging.getLogger(__name__)
44 44
45 45 PERM_WEIGHTS = {'repository.none': 0,
46 46 'repository.read': 1,
47 47 'repository.write': 3,
48 48 'repository.admin': 3}
49 49
50 50
51 51 class UserModel(BaseModel):
52 52 def get(self, user_id, cache=False):
53 53 user = self.sa.query(User)
54 54 if cache:
55 55 user = user.options(FromCache("sql_cache_short",
56 56 "get_user_%s" % user_id))
57 57 return user.get(user_id)
58 58
59 59 def get_by_username(self, username, cache=False, case_insensitive=False):
60 60
61 61 if case_insensitive:
62 62 user = self.sa.query(User).filter(User.username.ilike(username))
63 63 else:
64 64 user = self.sa.query(User)\
65 65 .filter(User.username == username)
66 66 if cache:
67 67 user = user.options(FromCache("sql_cache_short",
68 68 "get_user_%s" % username))
69 69 return user.scalar()
70 70
71 71 def get_by_api_key(self, api_key, cache=False):
72 72
73 73 user = self.sa.query(User)\
74 74 .filter(User.api_key == api_key)
75 75 if cache:
76 76 user = user.options(FromCache("sql_cache_short",
77 77 "get_user_%s" % api_key))
78 78 return user.scalar()
79 79
80 80 def create(self, form_data):
81 81 try:
82 82 new_user = User()
83 83 for k, v in form_data.items():
84 84 setattr(new_user, k, v)
85 85
86 86 new_user.api_key = generate_api_key(form_data['username'])
87 87 self.sa.add(new_user)
88 88 self.sa.commit()
89 89 return new_user
90 90 except:
91 91 log.error(traceback.format_exc())
92 92 self.sa.rollback()
93 93 raise
94 94
95 95 def create_for_container_auth(self, username, attrs):
96 96 """
97 97 Creates the given user if it's not already in the database
98 98
99 99 :param username:
100 100 :param attrs:
101 101 """
102 102 if self.get_by_username(username, case_insensitive=True) is None:
103 103 try:
104 104 new_user = User()
105 105 new_user.username = username
106 106 new_user.password = None
107 107 new_user.api_key = generate_api_key(username)
108 108 new_user.email = attrs['email']
109 new_user.active = True
109 new_user.active = attrs.get('active', True)
110 110 new_user.name = attrs['name']
111 111 new_user.lastname = attrs['lastname']
112 112
113 113 self.sa.add(new_user)
114 114 self.sa.commit()
115 return True
115 return new_user
116 116 except (DatabaseError,):
117 117 log.error(traceback.format_exc())
118 118 self.sa.rollback()
119 119 raise
120 log.debug('User %s already exists. Skipping creation of account for container auth.',
121 username)
122 return False
120 log.debug('User %s already exists. Skipping creation of account'
121 ' for container auth.', username)
122 return None
123 123
124 124 def create_ldap(self, username, password, user_dn, attrs):
125 125 """
126 126 Checks if user is in database, if not creates this user marked
127 127 as ldap user
128 128
129 129 :param username:
130 130 :param password:
131 131 :param user_dn:
132 132 :param attrs:
133 133 """
134 134 from rhodecode.lib.auth import get_crypt_password
135 135 log.debug('Checking for such ldap account in RhodeCode database')
136 136 if self.get_by_username(username, case_insensitive=True) is None:
137 137 try:
138 138 new_user = User()
139 139 # add ldap account always lowercase
140 140 new_user.username = username.lower()
141 141 new_user.password = get_crypt_password(password)
142 142 new_user.api_key = generate_api_key(username)
143 143 new_user.email = attrs['email']
144 new_user.active = attrs.get('active',True)
144 new_user.active = attrs.get('active', True)
145 145 new_user.ldap_dn = safe_unicode(user_dn)
146 146 new_user.name = attrs['name']
147 147 new_user.lastname = attrs['lastname']
148 148
149 149 self.sa.add(new_user)
150 150 self.sa.commit()
151 return True
151 return new_user
152 152 except (DatabaseError,):
153 153 log.error(traceback.format_exc())
154 154 self.sa.rollback()
155 155 raise
156 156 log.debug('this %s user exists skipping creation of ldap account',
157 157 username)
158 return False
158 return None
159 159
160 160 def create_registration(self, form_data):
161 161 from rhodecode.lib.celerylib import tasks, run_task
162 162 try:
163 163 new_user = User()
164 164 for k, v in form_data.items():
165 165 if k != 'admin':
166 166 setattr(new_user, k, v)
167 167
168 168 self.sa.add(new_user)
169 169 self.sa.commit()
170 170 body = ('New user registration\n'
171 171 'username: %s\n'
172 172 'email: %s\n')
173 173 body = body % (form_data['username'], form_data['email'])
174 174
175 175 run_task(tasks.send_email, None,
176 176 _('[RhodeCode] New User registration'),
177 177 body)
178 178 except:
179 179 log.error(traceback.format_exc())
180 180 self.sa.rollback()
181 181 raise
182 182
183 183 def update(self, user_id, form_data):
184 184 try:
185 185 user = self.get(user_id, cache=False)
186 186 if user.username == 'default':
187 187 raise DefaultUserException(
188 188 _("You can't Edit this user since it's"
189 189 " crucial for entire application"))
190 190
191 191 for k, v in form_data.items():
192 192 if k == 'new_password' and v != '':
193 193 user.password = v
194 194 user.api_key = generate_api_key(user.username)
195 195 else:
196 196 setattr(user, k, v)
197 197
198 198 self.sa.add(user)
199 199 self.sa.commit()
200 200 except:
201 201 log.error(traceback.format_exc())
202 202 self.sa.rollback()
203 203 raise
204 204
205 205 def update_my_account(self, user_id, form_data):
206 206 try:
207 207 user = self.get(user_id, cache=False)
208 208 if user.username == 'default':
209 209 raise DefaultUserException(
210 210 _("You can't Edit this user since it's"
211 211 " crucial for entire application"))
212 212 for k, v in form_data.items():
213 213 if k == 'new_password' and v != '':
214 214 user.password = v
215 215 user.api_key = generate_api_key(user.username)
216 216 else:
217 217 if k not in ['admin', 'active']:
218 218 setattr(user, k, v)
219 219
220 220 self.sa.add(user)
221 221 self.sa.commit()
222 222 except:
223 223 log.error(traceback.format_exc())
224 224 self.sa.rollback()
225 225 raise
226 226
227 227 def delete(self, user_id):
228 228 try:
229 229 user = self.get(user_id, cache=False)
230 230 if user.username == 'default':
231 231 raise DefaultUserException(
232 232 _("You can't remove this user since it's"
233 233 " crucial for entire application"))
234 234 if user.repositories:
235 235 raise UserOwnsReposException(_('This user still owns %s '
236 236 'repositories and cannot be '
237 237 'removed. Switch owners or '
238 238 'remove those repositories') \
239 239 % user.repositories)
240 240 self.sa.delete(user)
241 241 self.sa.commit()
242 242 except:
243 243 log.error(traceback.format_exc())
244 244 self.sa.rollback()
245 245 raise
246 246
247 247 def reset_password_link(self, data):
248 248 from rhodecode.lib.celerylib import tasks, run_task
249 249 run_task(tasks.send_password_link, data['email'])
250 250
251 251 def reset_password(self, data):
252 252 from rhodecode.lib.celerylib import tasks, run_task
253 253 run_task(tasks.reset_user_password, data['email'])
254 254
255 255 def fill_data(self, auth_user, user_id=None, api_key=None):
256 256 """
257 257 Fetches auth_user by user_id,or api_key if present.
258 258 Fills auth_user attributes with those taken from database.
259 259 Additionally set's is_authenitated if lookup fails
260 260 present in database
261 261
262 262 :param auth_user: instance of user to set attributes
263 263 :param user_id: user id to fetch by
264 264 :param api_key: api key to fetch by
265 265 """
266 266 if user_id is None and api_key is None:
267 267 raise Exception('You need to pass user_id or api_key')
268 268
269 269 try:
270 270 if api_key:
271 271 dbuser = self.get_by_api_key(api_key)
272 272 else:
273 273 dbuser = self.get(user_id)
274 274
275 275 if dbuser is not None and dbuser.active:
276 276 log.debug('filling %s data', dbuser)
277 277 for k, v in dbuser.get_dict().items():
278 278 setattr(auth_user, k, v)
279 279 else:
280 280 return False
281 281
282 282 except:
283 283 log.error(traceback.format_exc())
284 284 auth_user.is_authenticated = False
285 285 return False
286 286
287 287 return True
288 288
289 289 def fill_perms(self, user):
290 290 """
291 291 Fills user permission attribute with permissions taken from database
292 292 works for permissions given for repositories, and for permissions that
293 293 are granted to groups
294 294
295 295 :param user: user instance to fill his perms
296 296 """
297 297
298 298 user.permissions['repositories'] = {}
299 299 user.permissions['global'] = set()
300 300
301 301 #======================================================================
302 302 # fetch default permissions
303 303 #======================================================================
304 304 default_user = self.get_by_username('default', cache=True)
305 305
306 306 default_perms = self.sa.query(RepoToPerm, Repository, Permission)\
307 307 .join((Repository, RepoToPerm.repository_id ==
308 308 Repository.repo_id))\
309 309 .join((Permission, RepoToPerm.permission_id ==
310 310 Permission.permission_id))\
311 311 .filter(RepoToPerm.user == default_user).all()
312 312
313 313 if user.is_admin:
314 314 #==================================================================
315 315 # #admin have all default rights set to admin
316 316 #==================================================================
317 317 user.permissions['global'].add('hg.admin')
318 318
319 319 for perm in default_perms:
320 320 p = 'repository.admin'
321 321 user.permissions['repositories'][perm.RepoToPerm.
322 322 repository.repo_name] = p
323 323
324 324 else:
325 325 #==================================================================
326 326 # set default permissions
327 327 #==================================================================
328 328 uid = user.user_id
329 329
330 330 #default global
331 331 default_global_perms = self.sa.query(UserToPerm)\
332 332 .filter(UserToPerm.user == default_user)
333 333
334 334 for perm in default_global_perms:
335 335 user.permissions['global'].add(perm.permission.permission_name)
336 336
337 337 #default for repositories
338 338 for perm in default_perms:
339 339 if perm.Repository.private and not (perm.Repository.user_id ==
340 340 uid):
341 341 #diself.sable defaults for private repos,
342 342 p = 'repository.none'
343 343 elif perm.Repository.user_id == uid:
344 344 #set admin if owner
345 345 p = 'repository.admin'
346 346 else:
347 347 p = perm.Permission.permission_name
348 348
349 349 user.permissions['repositories'][perm.RepoToPerm.
350 350 repository.repo_name] = p
351 351
352 352 #==================================================================
353 353 # overwrite default with user permissions if any
354 354 #==================================================================
355 355
356 356 #user global
357 357 user_perms = self.sa.query(UserToPerm)\
358 358 .options(joinedload(UserToPerm.permission))\
359 359 .filter(UserToPerm.user_id == uid).all()
360 360
361 361 for perm in user_perms:
362 362 user.permissions['global'].add(perm.permission.
363 363 permission_name)
364 364
365 365 #user repositories
366 366 user_repo_perms = self.sa.query(RepoToPerm, Permission,
367 367 Repository)\
368 368 .join((Repository, RepoToPerm.repository_id ==
369 369 Repository.repo_id))\
370 370 .join((Permission, RepoToPerm.permission_id ==
371 371 Permission.permission_id))\
372 372 .filter(RepoToPerm.user_id == uid).all()
373 373
374 374 for perm in user_repo_perms:
375 375 # set admin if owner
376 376 if perm.Repository.user_id == uid:
377 377 p = 'repository.admin'
378 378 else:
379 379 p = perm.Permission.permission_name
380 380 user.permissions['repositories'][perm.RepoToPerm.
381 381 repository.repo_name] = p
382 382
383 383 #==================================================================
384 384 # check if user is part of groups for this repository and fill in
385 385 # (or replace with higher) permissions
386 386 #==================================================================
387 387
388 388 #users group global
389 389 user_perms_from_users_groups = self.sa.query(UsersGroupToPerm)\
390 390 .options(joinedload(UsersGroupToPerm.permission))\
391 391 .join((UsersGroupMember, UsersGroupToPerm.users_group_id ==
392 392 UsersGroupMember.users_group_id))\
393 393 .filter(UsersGroupMember.user_id == uid).all()
394 394
395 395 for perm in user_perms_from_users_groups:
396 396 user.permissions['global'].add(perm.permission.permission_name)
397 397
398 398 #users group repositories
399 399 user_repo_perms_from_users_groups = self.sa.query(
400 400 UsersGroupRepoToPerm,
401 401 Permission, Repository,)\
402 402 .join((Repository, UsersGroupRepoToPerm.repository_id ==
403 403 Repository.repo_id))\
404 404 .join((Permission, UsersGroupRepoToPerm.permission_id ==
405 405 Permission.permission_id))\
406 406 .join((UsersGroupMember, UsersGroupRepoToPerm.users_group_id ==
407 407 UsersGroupMember.users_group_id))\
408 408 .filter(UsersGroupMember.user_id == uid).all()
409 409
410 410 for perm in user_repo_perms_from_users_groups:
411 411 p = perm.Permission.permission_name
412 412 cur_perm = user.permissions['repositories'][perm.
413 413 UsersGroupRepoToPerm.
414 414 repository.repo_name]
415 415 #overwrite permission only if it's greater than permission
416 416 # given from other sources
417 417 if PERM_WEIGHTS[p] > PERM_WEIGHTS[cur_perm]:
418 418 user.permissions['repositories'][perm.UsersGroupRepoToPerm.
419 419 repository.repo_name] = p
420 420
421 421 return user
422 422
@@ -1,83 +1,82 b''
1 1 """Pylons application test package
2 2
3 3 This package assumes the Pylons environment is already loaded, such as
4 4 when this script is imported from the `nosetests --with-pylons=test.ini`
5 5 command.
6 6
7 7 This module initializes the application via ``websetup`` (`paster
8 8 setup-app`) and provides the base testing objects.
9 9 """
10 10 import os
11 11 from os.path import join as jn
12 12
13 13 from unittest import TestCase
14 14
15 15 from paste.deploy import loadapp
16 16 from paste.script.appinstall import SetupCommand
17 17 from pylons import config, url
18 18 from routes.util import URLGenerator
19 19 from webtest import TestApp
20 20
21 21 from rhodecode.model import meta
22 22 import logging
23 23
24
25 24 log = logging.getLogger(__name__)
26 25
27 26 import pylons.test
28 27
29 28 __all__ = ['environ', 'url', 'TestController', 'TESTS_TMP_PATH', 'HG_REPO',
30 29 'GIT_REPO', 'NEW_HG_REPO', 'NEW_GIT_REPO', 'HG_FORK', 'GIT_FORK',
31 30 'TEST_USER_ADMIN_LOGIN', 'TEST_USER_ADMIN_PASS' ]
32 31
33 32 # Invoke websetup with the current config file
34 #SetupCommand('setup-app').run([config_file])
33 # SetupCommand('setup-app').run([config_file])
35 34
36 35 ##RUNNING DESIRED TESTS
37 36 # nosetests -x rhodecode.tests.functional.test_admin_settings:TestSettingsController.test_my_account
38 37 # nosetests --pdb --pdb-failures
39 38 environ = {}
40 39
41 40 #SOME GLOBALS FOR TESTS
42 41 from tempfile import _RandomNameSequence
43 42 TESTS_TMP_PATH = jn('/', 'tmp', 'rc_test_%s' % _RandomNameSequence().next())
44 43 TEST_USER_ADMIN_LOGIN = 'test_admin'
45 44 TEST_USER_ADMIN_PASS = 'test12'
46 45 HG_REPO = 'vcs_test_hg'
47 46 GIT_REPO = 'vcs_test_git'
48 47
49 48 NEW_HG_REPO = 'vcs_test_hg_new'
50 49 NEW_GIT_REPO = 'vcs_test_git_new'
51 50
52 51 HG_FORK = 'vcs_test_hg_fork'
53 52 GIT_FORK = 'vcs_test_git_fork'
54 53
55 54 class TestController(TestCase):
56 55
57 56 def __init__(self, *args, **kwargs):
58 57 wsgiapp = pylons.test.pylonsapp
59 58 config = wsgiapp.config
60 59
61 60 self.app = TestApp(wsgiapp)
62 61 url._push_object(URLGenerator(config['routes.map'], environ))
63 62 self.sa = meta.Session
64 63 self.index_location = config['app_conf']['index_dir']
65 64 TestCase.__init__(self, *args, **kwargs)
66 65
67 66 def log_user(self, username=TEST_USER_ADMIN_LOGIN,
68 67 password=TEST_USER_ADMIN_PASS):
69 68 response = self.app.post(url(controller='login', action='index'),
70 69 {'username':username,
71 70 'password':password})
72
71
73 72 if 'invalid user name' in response.body:
74 73 self.fail('could not login using %s %s' % (username, password))
75 74
76 75 self.assertEqual(response.status, '302 Found')
77 76 self.assertEqual(response.session['rhodecode_user'].username, username)
78 77 return response.follow()
79 78
80 79 def checkSessionFlash(self, response, msg):
81 80 self.assertTrue('flash' in response.session)
82 81 self.assertTrue(msg in response.session['flash'][0][1])
83 82
1 NO CONTENT: file renamed from rhodecode/tests/test_concurency.py to rhodecode/tests/_test_concurency.py
@@ -1,227 +1,229 b''
1 1 ################################################################################
2 2 ################################################################################
3 3 # RhodeCode - Pylons environment configuration #
4 4 # #
5 5 # The %(here)s variable will be replaced with the parent directory of this file#
6 6 ################################################################################
7 7
8 8 [DEFAULT]
9 9 debug = true
10 10 pdebug = false
11 11 ################################################################################
12 12 ## Uncomment and replace with the address which should receive ##
13 13 ## any error reports after application crash ##
14 14 ## Additionally those settings will be used by RhodeCode mailing system ##
15 15 ################################################################################
16 16 #email_to = admin@localhost
17 17 #error_email_from = paste_error@localhost
18 18 #app_email_from = rhodecode-noreply@localhost
19 19 #error_message =
20 20
21 21 #smtp_server = mail.server.com
22 22 #smtp_username =
23 23 #smtp_password =
24 24 #smtp_port =
25 25 #smtp_use_tls = false
26 26 #smtp_use_ssl = true
27 27
28 28 [server:main]
29 29 ##nr of threads to spawn
30 30 threadpool_workers = 5
31 31
32 32 ##max request before thread respawn
33 33 threadpool_max_requests = 2
34 34
35 35 ##option to use threads of process
36 36 use_threadpool = true
37 37
38 38 use = egg:Paste#http
39 39 host = 127.0.0.1
40 40 port = 5000
41 41
42 42 [app:main]
43 43 use = egg:rhodecode
44 44 full_stack = true
45 45 static_files = true
46 46 lang=en
47 47 cache_dir = /tmp/data
48 48 index_dir = /tmp/index
49 49 app_instance_uuid = develop-test
50 50 cut_off_limit = 256000
51 51 force_https = false
52 52 commit_parse_limit = 25
53 53 use_gravatar = true
54 container_auth_enabled = false
55 proxypass_auth_enabled = false
54 56
55 57 ####################################
56 58 ### CELERY CONFIG ####
57 59 ####################################
58 60 use_celery = false
59 61 broker.host = localhost
60 62 broker.vhost = rabbitmqhost
61 63 broker.port = 5672
62 64 broker.user = rabbitmq
63 65 broker.password = qweqwe
64 66
65 67 celery.imports = rhodecode.lib.celerylib.tasks
66 68
67 69 celery.result.backend = amqp
68 70 celery.result.dburi = amqp://
69 71 celery.result.serialier = json
70 72
71 73 #celery.send.task.error.emails = true
72 74 #celery.amqp.task.result.expires = 18000
73 75
74 76 celeryd.concurrency = 2
75 77 #celeryd.log.file = celeryd.log
76 78 celeryd.log.level = debug
77 79 celeryd.max.tasks.per.child = 1
78 80
79 81 #tasks will never be sent to the queue, but executed locally instead.
80 82 celery.always.eager = false
81 83
82 84 ####################################
83 85 ### BEAKER CACHE ####
84 86 ####################################
85 87 beaker.cache.data_dir=/tmp/data/cache/data
86 88 beaker.cache.lock_dir=/tmp/data/cache/lock
87 89 beaker.cache.regions=super_short_term,short_term,long_term,sql_cache_short,sql_cache_med,sql_cache_long
88 90
89 91 beaker.cache.super_short_term.type=memory
90 92 beaker.cache.super_short_term.expire=10
91 93 beaker.cache.super_short_term.key_length = 256
92 94
93 95 beaker.cache.short_term.type=memory
94 96 beaker.cache.short_term.expire=60
95 97 beaker.cache.short_term.key_length = 256
96 98
97 99 beaker.cache.long_term.type=memory
98 100 beaker.cache.long_term.expire=36000
99 101 beaker.cache.long_term.key_length = 256
100 102
101 103 beaker.cache.sql_cache_short.type=memory
102 104 beaker.cache.sql_cache_short.expire=10
103 105 beaker.cache.sql_cache_short.key_length = 256
104 106
105 107 beaker.cache.sql_cache_med.type=memory
106 108 beaker.cache.sql_cache_med.expire=360
107 109 beaker.cache.sql_cache_med.key_length = 256
108 110
109 111 beaker.cache.sql_cache_long.type=file
110 112 beaker.cache.sql_cache_long.expire=3600
111 113 beaker.cache.sql_cache_long.key_length = 256
112 114
113 115 ####################################
114 116 ### BEAKER SESSION ####
115 117 ####################################
116 118 ## Type of storage used for the session, current types are
117 119 ## dbm, file, memcached, database, and memory.
118 120 ## The storage uses the Container API
119 121 ##that is also used by the cache system.
120 122 beaker.session.type = file
121 123
122 124 beaker.session.key = rhodecode
123 125 beaker.session.secret = g654dcno0-9873jhgfreyu
124 126 beaker.session.timeout = 36000
125 127
126 128 ##auto save the session to not to use .save()
127 129 beaker.session.auto = False
128 130
129 131 ##true exire at browser close
130 132 #beaker.session.cookie_expires = 3600
131 133
132 134
133 135 ################################################################################
134 136 ## WARNING: *THE LINE BELOW MUST BE UNCOMMENTED ON A PRODUCTION ENVIRONMENT* ##
135 137 ## Debug mode will enable the interactive debugging tool, allowing ANYONE to ##
136 138 ## execute malicious code after an exception is raised. ##
137 139 ################################################################################
138 140 #set debug = false
139 141
140 142 ##################################
141 143 ### LOGVIEW CONFIG ###
142 144 ##################################
143 145 logview.sqlalchemy = #faa
144 146 logview.pylons.templating = #bfb
145 147 logview.pylons.util = #eee
146 148
147 149 #########################################################
148 150 ### DB CONFIGS - EACH DB WILL HAVE IT'S OWN CONFIG ###
149 151 #########################################################
150 152 sqlalchemy.db1.url = sqlite:///%(here)s/test.db
151 153 #sqlalchemy.db1.url = postgresql://postgres:qwe@localhost/rhodecode_tests
152 154 #sqlalchemy.db1.echo = false
153 155 #sqlalchemy.db1.pool_recycle = 3600
154 156 sqlalchemy.convert_unicode = true
155 157
156 158 ################################
157 159 ### LOGGING CONFIGURATION ####
158 160 ################################
159 161 [loggers]
160 162 keys = root, routes, rhodecode, sqlalchemy, beaker, templates
161 163
162 164 [handlers]
163 165 keys = console
164 166
165 167 [formatters]
166 168 keys = generic, color_formatter
167 169
168 170 #############
169 171 ## LOGGERS ##
170 172 #############
171 173 [logger_root]
172 174 level = ERROR
173 175 handlers = console
174 176
175 177 [logger_routes]
176 178 level = ERROR
177 179 handlers =
178 180 qualname = routes.middleware
179 181 # "level = DEBUG" logs the route matched and routing variables.
180 182 propagate = 1
181 183
182 184 [logger_beaker]
183 185 level = DEBUG
184 186 handlers =
185 187 qualname = beaker.container
186 188 propagate = 1
187 189
188 190 [logger_templates]
189 191 level = INFO
190 192 handlers =
191 193 qualname = pylons.templating
192 194 propagate = 1
193 195
194 196 [logger_rhodecode]
195 197 level = ERROR
196 198 handlers =
197 199 qualname = rhodecode
198 200 propagate = 1
199 201
200 202 [logger_sqlalchemy]
201 203 level = ERROR
202 204 handlers = console
203 205 qualname = sqlalchemy.engine
204 206 propagate = 0
205 207
206 208 ##############
207 209 ## HANDLERS ##
208 210 ##############
209 211
210 212 [handler_console]
211 213 class = StreamHandler
212 214 args = (sys.stderr,)
213 215 level = NOTSET
214 216 formatter = generic
215 217
216 218 ################
217 219 ## FORMATTERS ##
218 220 ################
219 221
220 222 [formatter_generic]
221 223 format = %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
222 224 datefmt = %Y-%m-%d %H:%M:%S
223 225
224 226 [formatter_color_formatter]
225 227 class=rhodecode.lib.colored_formatter.ColorFormatter
226 228 format= %(asctime)s.%(msecs)03d %(levelname)-5.5s [%(name)s] %(message)s
227 229 datefmt = %Y-%m-%d %H:%M:%S No newline at end of file
General Comments 0
You need to be logged in to leave comments. Login now