##// END OF EJS Templates
i18n: Use new translation string factory....
johbo -
r51:2ebf8ced default
parent child Browse files
Show More
@@ -1,151 +1,150 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import logging
21 import logging
22
22
23 from pyramid.exceptions import ConfigurationError
23 from pyramid.exceptions import ConfigurationError
24 from pyramid.i18n import TranslationStringFactory
25
24
26 from rhodecode.lib.utils2 import safe_str
25 from rhodecode.lib.utils2 import safe_str
27 from rhodecode.model.settings import SettingsModel
26 from rhodecode.model.settings import SettingsModel
27 from rhodecode.translation import _
28
28
29 _ = TranslationStringFactory('rhodecode-enterprise')
30
29
31 log = logging.getLogger(__name__)
30 log = logging.getLogger(__name__)
32
31
33
32
34 class AuthnResourceBase(object):
33 class AuthnResourceBase(object):
35 __name__ = None
34 __name__ = None
36 __parent__ = None
35 __parent__ = None
37
36
38 def get_root(self):
37 def get_root(self):
39 current = self
38 current = self
40 while current.__parent__ is not None:
39 while current.__parent__ is not None:
41 current = current.__parent__
40 current = current.__parent__
42 return current
41 return current
43
42
44
43
45 class AuthnPluginResourceBase(AuthnResourceBase):
44 class AuthnPluginResourceBase(AuthnResourceBase):
46
45
47 def __init__(self, plugin):
46 def __init__(self, plugin):
48 self.plugin = plugin
47 self.plugin = plugin
49 self.__name__ = plugin.name
48 self.__name__ = plugin.name
50 self.display_name = plugin.get_display_name()
49 self.display_name = plugin.get_display_name()
51
50
52
51
53 class AuthnRootResource(AuthnResourceBase):
52 class AuthnRootResource(AuthnResourceBase):
54 """
53 """
55 This is the root traversal resource object for the authentication settings.
54 This is the root traversal resource object for the authentication settings.
56 """
55 """
57
56
58 def __init__(self):
57 def __init__(self):
59 self._store = {}
58 self._store = {}
60 self._resource_name_map = {}
59 self._resource_name_map = {}
61 self.display_name = _('Global')
60 self.display_name = _('Global')
62
61
63 def __getitem__(self, key):
62 def __getitem__(self, key):
64 """
63 """
65 Customized get item function to return only items (plugins) that are
64 Customized get item function to return only items (plugins) that are
66 activated.
65 activated.
67 """
66 """
68 if self._is_item_active(key):
67 if self._is_item_active(key):
69 return self._store[key]
68 return self._store[key]
70 else:
69 else:
71 raise KeyError('Authentication plugin "{}" is not active.'.format(
70 raise KeyError('Authentication plugin "{}" is not active.'.format(
72 key))
71 key))
73
72
74 def __iter__(self):
73 def __iter__(self):
75 for key in self._store.keys():
74 for key in self._store.keys():
76 if self._is_item_active(key):
75 if self._is_item_active(key):
77 yield self._store[key]
76 yield self._store[key]
78
77
79 def _is_item_active(self, key):
78 def _is_item_active(self, key):
80 activated_plugins = SettingsModel().get_auth_plugins()
79 activated_plugins = SettingsModel().get_auth_plugins()
81 plugin_id = self.get_plugin_id(key)
80 plugin_id = self.get_plugin_id(key)
82 return plugin_id in activated_plugins
81 return plugin_id in activated_plugins
83
82
84 def get_plugin_id(self, resource_name):
83 def get_plugin_id(self, resource_name):
85 """
84 """
86 Return the plugin id for the given traversal resource name.
85 Return the plugin id for the given traversal resource name.
87 """
86 """
88 # TODO: Store this info in the resource element.
87 # TODO: Store this info in the resource element.
89 return self._resource_name_map[resource_name]
88 return self._resource_name_map[resource_name]
90
89
91 def get_sorted_list(self):
90 def get_sorted_list(self):
92 """
91 """
93 Returns a sorted list of sub resources for displaying purposes.
92 Returns a sorted list of sub resources for displaying purposes.
94 """
93 """
95 def sort_key(resource):
94 def sort_key(resource):
96 return str.lower(safe_str(resource.display_name))
95 return str.lower(safe_str(resource.display_name))
97
96
98 active = [item for item in self]
97 active = [item for item in self]
99 return sorted(active, key=sort_key)
98 return sorted(active, key=sort_key)
100
99
101 def get_nav_list(self):
100 def get_nav_list(self):
102 """
101 """
103 Returns a sorted list of resources for displaying the navigation.
102 Returns a sorted list of resources for displaying the navigation.
104 """
103 """
105 list = self.get_sorted_list()
104 list = self.get_sorted_list()
106 list.insert(0, self)
105 list.insert(0, self)
107 return list
106 return list
108
107
109 def add_authn_resource(self, config, plugin_id, resource):
108 def add_authn_resource(self, config, plugin_id, resource):
110 """
109 """
111 Register a traversal resource as a sub element to the authentication
110 Register a traversal resource as a sub element to the authentication
112 settings. This method is registered as a directive on the pyramid
111 settings. This method is registered as a directive on the pyramid
113 configurator object and called by plugins.
112 configurator object and called by plugins.
114 """
113 """
115
114
116 def _ensure_unique_name(name, limit=100):
115 def _ensure_unique_name(name, limit=100):
117 counter = 1
116 counter = 1
118 current = name
117 current = name
119 while current in self._store.keys():
118 while current in self._store.keys():
120 current = '{}{}'.format(name, counter)
119 current = '{}{}'.format(name, counter)
121 counter += 1
120 counter += 1
122 if counter > limit:
121 if counter > limit:
123 raise ConfigurationError(
122 raise ConfigurationError(
124 'Cannot build unique name for traversal resource "%s" '
123 'Cannot build unique name for traversal resource "%s" '
125 'registered by plugin "%s"', name, plugin_id)
124 'registered by plugin "%s"', name, plugin_id)
126 return current
125 return current
127
126
128 # Allow plugin resources with identical names by rename duplicates.
127 # Allow plugin resources with identical names by rename duplicates.
129 unique_name = _ensure_unique_name(resource.__name__)
128 unique_name = _ensure_unique_name(resource.__name__)
130 if unique_name != resource.__name__:
129 if unique_name != resource.__name__:
131 log.warn('Name collision for traversal resource "%s" registered '
130 log.warn('Name collision for traversal resource "%s" registered '
132 'by authentication plugin "%s"', resource.__name__,
131 'by authentication plugin "%s"', resource.__name__,
133 plugin_id)
132 plugin_id)
134 resource.__name__ = unique_name
133 resource.__name__ = unique_name
135
134
136 log.debug('Register traversal resource "%s" for plugin "%s"',
135 log.debug('Register traversal resource "%s" for plugin "%s"',
137 unique_name, plugin_id)
136 unique_name, plugin_id)
138 self._resource_name_map[unique_name] = plugin_id
137 self._resource_name_map[unique_name] = plugin_id
139 resource.__parent__ = self
138 resource.__parent__ = self
140 self._store[unique_name] = resource
139 self._store[unique_name] = resource
141
140
142
141
143 root = AuthnRootResource()
142 root = AuthnRootResource()
144
143
145
144
146 def root_factory(request=None):
145 def root_factory(request=None):
147 """
146 """
148 Returns the root traversal resource instance used for the authentication
147 Returns the root traversal resource instance used for the authentication
149 settings route.
148 settings route.
150 """
149 """
151 return root
150 return root
@@ -1,53 +1,51 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
21 import colander
22
22
23 from pyramid.i18n import TranslationStringFactory
23 from rhodecode.translation import _
24
25 _ = TranslationStringFactory('rhodecode-enterprise')
26
24
27
25
28 class AuthnPluginSettingsSchemaBase(colander.MappingSchema):
26 class AuthnPluginSettingsSchemaBase(colander.MappingSchema):
29 """
27 """
30 This base schema is intended for use in authentication plugins.
28 This base schema is intended for use in authentication plugins.
31 It adds a few default settings (e.g., "enabled"), so that plugin
29 It adds a few default settings (e.g., "enabled"), so that plugin
32 authors don't have to maintain a bunch of boilerplate.
30 authors don't have to maintain a bunch of boilerplate.
33 """
31 """
34 enabled = colander.SchemaNode(
32 enabled = colander.SchemaNode(
35 colander.Bool(),
33 colander.Bool(),
36 default=False,
34 default=False,
37 description=_('Enable or disable this authentication plugin.'),
35 description=_('Enable or disable this authentication plugin.'),
38 missing=False,
36 missing=False,
39 title=_('Enabled'),
37 title=_('Enabled'),
40 widget='bool',
38 widget='bool',
41 )
39 )
42 cache_ttl = colander.SchemaNode(
40 cache_ttl = colander.SchemaNode(
43 colander.Int(),
41 colander.Int(),
44 default=0,
42 default=0,
45 description=_('Amount of seconds to cache the authentication '
43 description=_('Amount of seconds to cache the authentication '
46 'call for this plugin. Useful for long calls like '
44 'call for this plugin. Useful for long calls like '
47 'LDAP to improve the responsiveness of the '
45 'LDAP to improve the responsiveness of the '
48 'authentication system (0 means disabled).'),
46 'authentication system (0 means disabled).'),
49 missing=0,
47 missing=0,
50 title=_('Auth Cache TTL'),
48 title=_('Auth Cache TTL'),
51 validator=colander.Range(min=0, max=None),
49 validator=colander.Range(min=0, max=None),
52 widget='int',
50 widget='int',
53 )
51 )
@@ -1,220 +1,218 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2016 RhodeCode GmbH
3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import colander
21 import colander
22 import formencode.htmlfill
22 import formencode.htmlfill
23 import logging
23 import logging
24
24
25 from pyramid.httpexceptions import HTTPFound
25 from pyramid.httpexceptions import HTTPFound
26 from pyramid.i18n import TranslationStringFactory
27 from pyramid.renderers import render
26 from pyramid.renderers import render
28 from pyramid.response import Response
27 from pyramid.response import Response
29
28
30 from rhodecode.authentication.base import get_auth_cache_manager
29 from rhodecode.authentication.base import get_auth_cache_manager
31 from rhodecode.authentication.interface import IAuthnPluginRegistry
30 from rhodecode.authentication.interface import IAuthnPluginRegistry
32 from rhodecode.lib import auth
31 from rhodecode.lib import auth
33 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
34 from rhodecode.model.forms import AuthSettingsForm
33 from rhodecode.model.forms import AuthSettingsForm
35 from rhodecode.model.meta import Session
34 from rhodecode.model.meta import Session
36 from rhodecode.model.settings import SettingsModel
35 from rhodecode.model.settings import SettingsModel
36 from rhodecode.translation import _
37
37
38 log = logging.getLogger(__name__)
38 log = logging.getLogger(__name__)
39
39
40 _ = TranslationStringFactory('rhodecode-enterprise')
41
42
40
43 class AuthnPluginViewBase(object):
41 class AuthnPluginViewBase(object):
44
42
45 def __init__(self, context, request):
43 def __init__(self, context, request):
46 self.request = request
44 self.request = request
47 self.context = context
45 self.context = context
48 self.plugin = context.plugin
46 self.plugin = context.plugin
49
47
50 # TODO: Think about replacing the htmlfill stuff.
48 # TODO: Think about replacing the htmlfill stuff.
51 def _render_and_fill(self, template, template_context, request,
49 def _render_and_fill(self, template, template_context, request,
52 form_defaults, validation_errors):
50 form_defaults, validation_errors):
53 """
51 """
54 Helper to render a template and fill the HTML form fields with
52 Helper to render a template and fill the HTML form fields with
55 defaults. Also displays the form errors.
53 defaults. Also displays the form errors.
56 """
54 """
57 # Render template to string.
55 # Render template to string.
58 html = render(template, template_context, request=request)
56 html = render(template, template_context, request=request)
59
57
60 # Fill the HTML form fields with default values and add error messages.
58 # Fill the HTML form fields with default values and add error messages.
61 html = formencode.htmlfill.render(
59 html = formencode.htmlfill.render(
62 html,
60 html,
63 defaults=form_defaults,
61 defaults=form_defaults,
64 errors=validation_errors,
62 errors=validation_errors,
65 prefix_error=False,
63 prefix_error=False,
66 encoding="UTF-8",
64 encoding="UTF-8",
67 force_defaults=False)
65 force_defaults=False)
68
66
69 return html
67 return html
70
68
71 def settings_get(self):
69 def settings_get(self):
72 """
70 """
73 View that displays the plugin settings as a form.
71 View that displays the plugin settings as a form.
74 """
72 """
75 form_defaults = {}
73 form_defaults = {}
76 validation_errors = None
74 validation_errors = None
77 schema = self.plugin.get_settings_schema()
75 schema = self.plugin.get_settings_schema()
78
76
79 # Get default values for the form.
77 # Get default values for the form.
80 for node in schema.children:
78 for node in schema.children:
81 value = self.plugin.get_setting_by_name(node.name) or node.default
79 value = self.plugin.get_setting_by_name(node.name) or node.default
82 form_defaults[node.name] = value
80 form_defaults[node.name] = value
83
81
84 template_context = {
82 template_context = {
85 'resource': self.context,
83 'resource': self.context,
86 'plugin': self.context.plugin
84 'plugin': self.context.plugin
87 }
85 }
88
86
89 return Response(self._render_and_fill(
87 return Response(self._render_and_fill(
90 'rhodecode:templates/admin/auth/plugin_settings.html',
88 'rhodecode:templates/admin/auth/plugin_settings.html',
91 template_context,
89 template_context,
92 self.request,
90 self.request,
93 form_defaults,
91 form_defaults,
94 validation_errors))
92 validation_errors))
95
93
96 def settings_post(self):
94 def settings_post(self):
97 """
95 """
98 View that validates and stores the plugin settings.
96 View that validates and stores the plugin settings.
99 """
97 """
100 schema = self.plugin.get_settings_schema()
98 schema = self.plugin.get_settings_schema()
101 try:
99 try:
102 valid_data = schema.deserialize(self.request.params)
100 valid_data = schema.deserialize(self.request.params)
103 except colander.Invalid, e:
101 except colander.Invalid, e:
104 # Display error message and display form again.
102 # Display error message and display form again.
105 form_defaults = self.request.params
103 form_defaults = self.request.params
106 validation_errors = e.asdict()
104 validation_errors = e.asdict()
107 self.request.session.flash(
105 self.request.session.flash(
108 _('Errors exist when saving plugin settings. '
106 _('Errors exist when saving plugin settings. '
109 'Please check the form inputs.'),
107 'Please check the form inputs.'),
110 queue='error')
108 queue='error')
111
109
112 template_context = {
110 template_context = {
113 'resource': self.context,
111 'resource': self.context,
114 'plugin': self.context.plugin
112 'plugin': self.context.plugin
115 }
113 }
116
114
117 return Response(self._render_and_fill(
115 return Response(self._render_and_fill(
118 'rhodecode:templates/admin/auth/plugin_settings.html',
116 'rhodecode:templates/admin/auth/plugin_settings.html',
119 template_context,
117 template_context,
120 self.request,
118 self.request,
121 form_defaults,
119 form_defaults,
122 validation_errors))
120 validation_errors))
123
121
124 # Store validated data.
122 # Store validated data.
125 for name, value in valid_data.items():
123 for name, value in valid_data.items():
126 self.plugin.create_or_update_setting(name, value)
124 self.plugin.create_or_update_setting(name, value)
127 Session.commit()
125 Session.commit()
128
126
129 # Display success message and redirect.
127 # Display success message and redirect.
130 self.request.session.flash(
128 self.request.session.flash(
131 _('Auth settings updated successfully.'),
129 _('Auth settings updated successfully.'),
132 queue='success')
130 queue='success')
133 redirect_to = self.request.resource_path(
131 redirect_to = self.request.resource_path(
134 self.context, route_name='auth_home')
132 self.context, route_name='auth_home')
135 return HTTPFound(redirect_to)
133 return HTTPFound(redirect_to)
136
134
137
135
138 # TODO: Ongoing migration in these views.
136 # TODO: Ongoing migration in these views.
139 # - Maybe we should also use a colander schema for these views.
137 # - Maybe we should also use a colander schema for these views.
140 class AuthSettingsView(object):
138 class AuthSettingsView(object):
141 def __init__(self, context, request):
139 def __init__(self, context, request):
142 self.context = context
140 self.context = context
143 self.request = request
141 self.request = request
144
142
145 # TODO: Move this into a utility function. It is needed in all view
143 # TODO: Move this into a utility function. It is needed in all view
146 # classes during migration. Maybe a mixin?
144 # classes during migration. Maybe a mixin?
147
145
148 # Some of the decorators rely on this attribute to be present on the
146 # Some of the decorators rely on this attribute to be present on the
149 # class of the decorated method.
147 # class of the decorated method.
150 self._rhodecode_user = request.user
148 self._rhodecode_user = request.user
151
149
152 @LoginRequired()
150 @LoginRequired()
153 @HasPermissionAllDecorator('hg.admin')
151 @HasPermissionAllDecorator('hg.admin')
154 def index(self, defaults={}, errors=None, prefix_error=False):
152 def index(self, defaults={}, errors=None, prefix_error=False):
155 authn_registry = self.request.registry.getUtility(IAuthnPluginRegistry)
153 authn_registry = self.request.registry.getUtility(IAuthnPluginRegistry)
156 default_plugins = ['egg:rhodecode-enterprise-ce#rhodecode']
154 default_plugins = ['egg:rhodecode-enterprise-ce#rhodecode']
157 enabled_plugins = SettingsModel().get_auth_plugins() or default_plugins
155 enabled_plugins = SettingsModel().get_auth_plugins() or default_plugins
158
156
159 # Create template context and render it.
157 # Create template context and render it.
160 template_context = {
158 template_context = {
161 'resource': self.context,
159 'resource': self.context,
162 'available_plugins': authn_registry.get_plugins(),
160 'available_plugins': authn_registry.get_plugins(),
163 'enabled_plugins': enabled_plugins,
161 'enabled_plugins': enabled_plugins,
164 }
162 }
165 html = render('rhodecode:templates/admin/auth/auth_settings.html',
163 html = render('rhodecode:templates/admin/auth/auth_settings.html',
166 template_context,
164 template_context,
167 request=self.request)
165 request=self.request)
168
166
169 # Create form default values and fill the form.
167 # Create form default values and fill the form.
170 form_defaults = {
168 form_defaults = {
171 'auth_plugins': ','.join(enabled_plugins)
169 'auth_plugins': ','.join(enabled_plugins)
172 }
170 }
173 form_defaults.update(defaults)
171 form_defaults.update(defaults)
174 html = formencode.htmlfill.render(
172 html = formencode.htmlfill.render(
175 html,
173 html,
176 defaults=form_defaults,
174 defaults=form_defaults,
177 errors=errors,
175 errors=errors,
178 prefix_error=prefix_error,
176 prefix_error=prefix_error,
179 encoding="UTF-8",
177 encoding="UTF-8",
180 force_defaults=False)
178 force_defaults=False)
181
179
182 return Response(html)
180 return Response(html)
183
181
184 @LoginRequired()
182 @LoginRequired()
185 @HasPermissionAllDecorator('hg.admin')
183 @HasPermissionAllDecorator('hg.admin')
186 @auth.CSRFRequired()
184 @auth.CSRFRequired()
187 def auth_settings(self):
185 def auth_settings(self):
188 try:
186 try:
189 form = AuthSettingsForm()()
187 form = AuthSettingsForm()()
190 form_result = form.to_python(self.request.params)
188 form_result = form.to_python(self.request.params)
191 plugins = ','.join(form_result['auth_plugins'])
189 plugins = ','.join(form_result['auth_plugins'])
192 setting = SettingsModel().create_or_update_setting(
190 setting = SettingsModel().create_or_update_setting(
193 'auth_plugins', plugins)
191 'auth_plugins', plugins)
194 Session().add(setting)
192 Session().add(setting)
195 Session().commit()
193 Session().commit()
196
194
197 cache_manager = get_auth_cache_manager()
195 cache_manager = get_auth_cache_manager()
198 cache_manager.clear()
196 cache_manager.clear()
199 self.request.session.flash(
197 self.request.session.flash(
200 _('Auth settings updated successfully.'),
198 _('Auth settings updated successfully.'),
201 queue='success')
199 queue='success')
202 except formencode.Invalid as errors:
200 except formencode.Invalid as errors:
203 e = errors.error_dict or {}
201 e = errors.error_dict or {}
204 self.request.session.flash(
202 self.request.session.flash(
205 _('Errors exist when saving plugin setting. '
203 _('Errors exist when saving plugin setting. '
206 'Please check the form inputs.'),
204 'Please check the form inputs.'),
207 queue='error')
205 queue='error')
208 return self.index(
206 return self.index(
209 defaults=errors.value,
207 defaults=errors.value,
210 errors=e,
208 errors=e,
211 prefix_error=False)
209 prefix_error=False)
212 except Exception:
210 except Exception:
213 log.exception('Exception in auth_settings')
211 log.exception('Exception in auth_settings')
214 self.request.session.flash(
212 self.request.session.flash(
215 _('Error occurred during update of auth settings.'),
213 _('Error occurred during update of auth settings.'),
216 queue='error')
214 queue='error')
217
215
218 redirect_to = self.request.resource_path(
216 redirect_to = self.request.resource_path(
219 self.context, route_name='auth_home')
217 self.context, route_name='auth_home')
220 return HTTPFound(redirect_to)
218 return HTTPFound(redirect_to)
@@ -1,346 +1,342 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2016-2016 RhodeCode GmbH
3 # Copyright (C) 2016-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21 import datetime
21 import datetime
22 import formencode
22 import formencode
23 import logging
23 import logging
24 import urlparse
24 import urlparse
25 import uuid
26
25
27 from pylons import url
26 from pylons import url
28 from pyramid.httpexceptions import HTTPFound
27 from pyramid.httpexceptions import HTTPFound
29 from pyramid.i18n import TranslationStringFactory
30 from pyramid.view import view_config
28 from pyramid.view import view_config
31 from recaptcha.client.captcha import submit
29 from recaptcha.client.captcha import submit
32
30
33 from rhodecode.authentication.base import loadplugin
34 from rhodecode.events import UserRegistered
31 from rhodecode.events import UserRegistered
35 from rhodecode.lib.auth import (
32 from rhodecode.lib.auth import (
36 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
33 AuthUser, HasPermissionAnyDecorator, CSRFRequired)
37 from rhodecode.lib.base import get_ip_addr
34 from rhodecode.lib.base import get_ip_addr
38 from rhodecode.lib.exceptions import UserCreationError
35 from rhodecode.lib.exceptions import UserCreationError
39 from rhodecode.lib.utils2 import safe_str
36 from rhodecode.lib.utils2 import safe_str
40 from rhodecode.model.db import User
37 from rhodecode.model.db import User
41 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
38 from rhodecode.model.forms import LoginForm, RegisterForm, PasswordResetForm
42 from rhodecode.model.login_session import LoginSession
39 from rhodecode.model.login_session import LoginSession
43 from rhodecode.model.meta import Session
40 from rhodecode.model.meta import Session
44 from rhodecode.model.settings import SettingsModel
41 from rhodecode.model.settings import SettingsModel
45 from rhodecode.model.user import UserModel
42 from rhodecode.model.user import UserModel
46
43 from rhodecode.translation import _
47
44
48 _ = TranslationStringFactory('rhodecode-enterprise')
49
45
50 log = logging.getLogger(__name__)
46 log = logging.getLogger(__name__)
51
47
52
48
53 def _store_user_in_session(session, username, remember=False):
49 def _store_user_in_session(session, username, remember=False):
54 user = User.get_by_username(username, case_insensitive=True)
50 user = User.get_by_username(username, case_insensitive=True)
55 auth_user = AuthUser(user.user_id)
51 auth_user = AuthUser(user.user_id)
56 auth_user.set_authenticated()
52 auth_user.set_authenticated()
57 cs = auth_user.get_cookie_store()
53 cs = auth_user.get_cookie_store()
58 session['rhodecode_user'] = cs
54 session['rhodecode_user'] = cs
59 user.update_lastlogin()
55 user.update_lastlogin()
60 Session().commit()
56 Session().commit()
61
57
62 # If they want to be remembered, update the cookie
58 # If they want to be remembered, update the cookie
63 if remember:
59 if remember:
64 _year = (datetime.datetime.now() +
60 _year = (datetime.datetime.now() +
65 datetime.timedelta(seconds=60 * 60 * 24 * 365))
61 datetime.timedelta(seconds=60 * 60 * 24 * 365))
66 session._set_cookie_expires(_year)
62 session._set_cookie_expires(_year)
67
63
68 session.save()
64 session.save()
69
65
70 log.info('user %s is now authenticated and stored in '
66 log.info('user %s is now authenticated and stored in '
71 'session, session attrs %s', username, cs)
67 'session, session attrs %s', username, cs)
72
68
73 # dumps session attrs back to cookie
69 # dumps session attrs back to cookie
74 session._update_cookie_out()
70 session._update_cookie_out()
75 # we set new cookie
71 # we set new cookie
76 headers = None
72 headers = None
77 if session.request['set_cookie']:
73 if session.request['set_cookie']:
78 # send set-cookie headers back to response to update cookie
74 # send set-cookie headers back to response to update cookie
79 headers = [('Set-Cookie', session.request['cookie_out'])]
75 headers = [('Set-Cookie', session.request['cookie_out'])]
80 return headers
76 return headers
81
77
82
78
83 class LoginView(object):
79 class LoginView(object):
84
80
85 def __init__(self, context, request):
81 def __init__(self, context, request):
86 self.request = request
82 self.request = request
87 self.context = context
83 self.context = context
88 self.session = request.session
84 self.session = request.session
89 self._rhodecode_user = request.user
85 self._rhodecode_user = request.user
90
86
91 def _validate_came_from(self, came_from):
87 def _validate_came_from(self, came_from):
92 if not came_from:
88 if not came_from:
93 return came_from
89 return came_from
94
90
95 parsed = urlparse.urlparse(came_from)
91 parsed = urlparse.urlparse(came_from)
96 allowed_schemes = ['http', 'https']
92 allowed_schemes = ['http', 'https']
97 if parsed.scheme and parsed.scheme not in allowed_schemes:
93 if parsed.scheme and parsed.scheme not in allowed_schemes:
98 log.error('Suspicious URL scheme detected %s for url %s' %
94 log.error('Suspicious URL scheme detected %s for url %s' %
99 (parsed.scheme, parsed))
95 (parsed.scheme, parsed))
100 came_from = url('home')
96 came_from = url('home')
101 elif parsed.netloc and self.request.host != parsed.netloc:
97 elif parsed.netloc and self.request.host != parsed.netloc:
102 log.error('Suspicious NETLOC detected %s for url %s server url '
98 log.error('Suspicious NETLOC detected %s for url %s server url '
103 'is: %s' % (parsed.netloc, parsed, self.request.host))
99 'is: %s' % (parsed.netloc, parsed, self.request.host))
104 came_from = url('home')
100 came_from = url('home')
105 if any(bad_str in parsed.path for bad_str in ('\r', '\n')):
101 if any(bad_str in parsed.path for bad_str in ('\r', '\n')):
106 log.error('Header injection detected `%s` for url %s server url ' %
102 log.error('Header injection detected `%s` for url %s server url ' %
107 (parsed.path, parsed))
103 (parsed.path, parsed))
108 came_from = url('home')
104 came_from = url('home')
109 return came_from
105 return came_from
110
106
111 def _get_came_from(self):
107 def _get_came_from(self):
112 _default_came_from = url('home')
108 _default_came_from = url('home')
113 came_from = self._validate_came_from(
109 came_from = self._validate_came_from(
114 safe_str(self.request.GET.get('came_from', '')))
110 safe_str(self.request.GET.get('came_from', '')))
115 return came_from or _default_came_from
111 return came_from or _default_came_from
116
112
117 def _get_template_context(self):
113 def _get_template_context(self):
118 return {
114 return {
119 'came_from': self._get_came_from(),
115 'came_from': self._get_came_from(),
120 'defaults': {},
116 'defaults': {},
121 'errors': {},
117 'errors': {},
122 }
118 }
123
119
124 @view_config(
120 @view_config(
125 route_name='login', request_method='GET',
121 route_name='login', request_method='GET',
126 renderer='rhodecode:templates/login.html')
122 renderer='rhodecode:templates/login.html')
127 def login(self):
123 def login(self):
128 user = self.request.user
124 user = self.request.user
129
125
130 # redirect if already logged in
126 # redirect if already logged in
131 if user.is_authenticated and not user.is_default and user.ip_allowed:
127 if user.is_authenticated and not user.is_default and user.ip_allowed:
132 raise HTTPFound(self._get_came_from())
128 raise HTTPFound(self._get_came_from())
133
129
134 return self._get_template_context()
130 return self._get_template_context()
135
131
136 @view_config(
132 @view_config(
137 route_name='login', request_method='POST',
133 route_name='login', request_method='POST',
138 renderer='rhodecode:templates/login.html')
134 renderer='rhodecode:templates/login.html')
139 def login_post(self):
135 def login_post(self):
140 came_from = self._get_came_from()
136 came_from = self._get_came_from()
141 session = self.request.session
137 session = self.request.session
142 login_form = LoginForm()()
138 login_form = LoginForm()()
143
139
144 try:
140 try:
145 session.invalidate()
141 session.invalidate()
146 form_result = login_form.to_python(self.request.params)
142 form_result = login_form.to_python(self.request.params)
147 # form checks for username/password, now we're authenticated
143 # form checks for username/password, now we're authenticated
148 headers = _store_user_in_session(
144 headers = _store_user_in_session(
149 self.session,
145 self.session,
150 username=form_result['username'],
146 username=form_result['username'],
151 remember=form_result['remember'])
147 remember=form_result['remember'])
152 raise HTTPFound(came_from, headers=headers)
148 raise HTTPFound(came_from, headers=headers)
153 except formencode.Invalid as errors:
149 except formencode.Invalid as errors:
154 defaults = errors.value
150 defaults = errors.value
155 # remove password from filling in form again
151 # remove password from filling in form again
156 del defaults['password']
152 del defaults['password']
157 render_ctx = self._get_template_context()
153 render_ctx = self._get_template_context()
158 render_ctx.update({
154 render_ctx.update({
159 'errors': errors.error_dict,
155 'errors': errors.error_dict,
160 'defaults': defaults,
156 'defaults': defaults,
161 })
157 })
162 return render_ctx
158 return render_ctx
163
159
164 except UserCreationError as e:
160 except UserCreationError as e:
165 # container auth or other auth functions that create users on
161 # container auth or other auth functions that create users on
166 # the fly can throw this exception signaling that there's issue
162 # the fly can throw this exception signaling that there's issue
167 # with user creation, explanation should be provided in
163 # with user creation, explanation should be provided in
168 # Exception itself
164 # Exception itself
169 session.flash(e, queue='error')
165 session.flash(e, queue='error')
170
166
171 # check if we use container plugin, and try to login using it.
167 # check if we use container plugin, and try to login using it.
172 from rhodecode.authentication.base import authenticate, HTTP_TYPE
168 from rhodecode.authentication.base import authenticate, HTTP_TYPE
173 try:
169 try:
174 log.debug('Running PRE-AUTH for container based authentication')
170 log.debug('Running PRE-AUTH for container based authentication')
175 auth_info = authenticate(
171 auth_info = authenticate(
176 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
172 '', '', self.request.environ, HTTP_TYPE, skip_missing=True)
177 except UserCreationError as e:
173 except UserCreationError as e:
178 log.error(e)
174 log.error(e)
179 session.flash(e, queue='error')
175 session.flash(e, queue='error')
180 # render login, with flash message about limit
176 # render login, with flash message about limit
181 return self._get_template_context()
177 return self._get_template_context()
182
178
183 if auth_info:
179 if auth_info:
184 headers = _store_user_in_session(auth_info.get('username'))
180 headers = _store_user_in_session(auth_info.get('username'))
185 raise HTTPFound(came_from, headers=headers)
181 raise HTTPFound(came_from, headers=headers)
186
182
187 return self._get_template_context()
183 return self._get_template_context()
188
184
189 @CSRFRequired()
185 @CSRFRequired()
190 @view_config(route_name='logout', request_method='POST')
186 @view_config(route_name='logout', request_method='POST')
191 def logout(self):
187 def logout(self):
192 LoginSession().destroy_user_session()
188 LoginSession().destroy_user_session()
193 return HTTPFound(url('home'))
189 return HTTPFound(url('home'))
194
190
195 @HasPermissionAnyDecorator(
191 @HasPermissionAnyDecorator(
196 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
192 'hg.admin', 'hg.register.auto_activate', 'hg.register.manual_activate')
197 @view_config(
193 @view_config(
198 route_name='register', request_method='GET',
194 route_name='register', request_method='GET',
199 renderer='rhodecode:templates/register.html',)
195 renderer='rhodecode:templates/register.html',)
200 def register(self, defaults={}, errors={}):
196 def register(self, defaults={}, errors={}):
201 settings = SettingsModel().get_all_settings()
197 settings = SettingsModel().get_all_settings()
202 captcha_public_key = settings.get('rhodecode_captcha_public_key')
198 captcha_public_key = settings.get('rhodecode_captcha_public_key')
203 captcha_private_key = settings.get('rhodecode_captcha_private_key')
199 captcha_private_key = settings.get('rhodecode_captcha_private_key')
204 captcha_active = bool(captcha_private_key)
200 captcha_active = bool(captcha_private_key)
205 register_message = settings.get('rhodecode_register_message') or ''
201 register_message = settings.get('rhodecode_register_message') or ''
206 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
202 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
207 .AuthUser.permissions['global']
203 .AuthUser.permissions['global']
208
204
209 render_ctx = self._get_template_context()
205 render_ctx = self._get_template_context()
210 render_ctx.update({
206 render_ctx.update({
211 'defaults': defaults,
207 'defaults': defaults,
212 'errors': errors,
208 'errors': errors,
213 'auto_active': auto_active,
209 'auto_active': auto_active,
214 'captcha_active': captcha_active,
210 'captcha_active': captcha_active,
215 'captcha_public_key': captcha_public_key,
211 'captcha_public_key': captcha_public_key,
216 'register_message': register_message,
212 'register_message': register_message,
217 })
213 })
218 return render_ctx
214 return render_ctx
219
215
220 @view_config(
216 @view_config(
221 route_name='register', request_method='POST',
217 route_name='register', request_method='POST',
222 renderer='rhodecode:templates/register.html')
218 renderer='rhodecode:templates/register.html')
223 def register_post(self):
219 def register_post(self):
224 captcha_private_key = SettingsModel().get_setting_by_name(
220 captcha_private_key = SettingsModel().get_setting_by_name(
225 'rhodecode_captcha_private_key')
221 'rhodecode_captcha_private_key')
226 captcha_active = bool(captcha_private_key)
222 captcha_active = bool(captcha_private_key)
227 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
223 auto_active = 'hg.register.auto_activate' in User.get_default_user()\
228 .AuthUser.permissions['global']
224 .AuthUser.permissions['global']
229
225
230 register_form = RegisterForm()()
226 register_form = RegisterForm()()
231 try:
227 try:
232 form_result = register_form.to_python(self.request.params)
228 form_result = register_form.to_python(self.request.params)
233 form_result['active'] = auto_active
229 form_result['active'] = auto_active
234
230
235 if captcha_active:
231 if captcha_active:
236 response = submit(
232 response = submit(
237 self.request.params.get('recaptcha_challenge_field'),
233 self.request.params.get('recaptcha_challenge_field'),
238 self.request.params.get('recaptcha_response_field'),
234 self.request.params.get('recaptcha_response_field'),
239 private_key=captcha_private_key,
235 private_key=captcha_private_key,
240 remoteip=get_ip_addr(self.request.environ))
236 remoteip=get_ip_addr(self.request.environ))
241 if captcha_active and not response.is_valid:
237 if captcha_active and not response.is_valid:
242 _value = form_result
238 _value = form_result
243 _msg = _('bad captcha')
239 _msg = _('bad captcha')
244 error_dict = {'recaptcha_field': _msg}
240 error_dict = {'recaptcha_field': _msg}
245 raise formencode.Invalid(_msg, _value, None,
241 raise formencode.Invalid(_msg, _value, None,
246 error_dict=error_dict)
242 error_dict=error_dict)
247
243
248 new_user = UserModel().create_registration(form_result)
244 new_user = UserModel().create_registration(form_result)
249 event = UserRegistered(user=new_user, session=self.session)
245 event = UserRegistered(user=new_user, session=self.session)
250 self.request.registry.notify(event)
246 self.request.registry.notify(event)
251 self.session.flash(
247 self.session.flash(
252 _('You have successfully registered with RhodeCode'),
248 _('You have successfully registered with RhodeCode'),
253 queue='success')
249 queue='success')
254 Session().commit()
250 Session().commit()
255
251
256 redirect_ro = self.request.route_path('login')
252 redirect_ro = self.request.route_path('login')
257 raise HTTPFound(redirect_ro)
253 raise HTTPFound(redirect_ro)
258
254
259 except formencode.Invalid as errors:
255 except formencode.Invalid as errors:
260 del errors.value['password']
256 del errors.value['password']
261 del errors.value['password_confirmation']
257 del errors.value['password_confirmation']
262 return self.register(
258 return self.register(
263 defaults=errors.value, errors=errors.error_dict)
259 defaults=errors.value, errors=errors.error_dict)
264
260
265 except UserCreationError as e:
261 except UserCreationError as e:
266 # container auth or other auth functions that create users on
262 # container auth or other auth functions that create users on
267 # the fly can throw this exception signaling that there's issue
263 # the fly can throw this exception signaling that there's issue
268 # with user creation, explanation should be provided in
264 # with user creation, explanation should be provided in
269 # Exception itself
265 # Exception itself
270 self.session.flash(e, queue='error')
266 self.session.flash(e, queue='error')
271 return self.register()
267 return self.register()
272
268
273 @view_config(
269 @view_config(
274 route_name='reset_password', request_method=('GET', 'POST'),
270 route_name='reset_password', request_method=('GET', 'POST'),
275 renderer='rhodecode:templates/password_reset.html')
271 renderer='rhodecode:templates/password_reset.html')
276 def password_reset(self):
272 def password_reset(self):
277 settings = SettingsModel().get_all_settings()
273 settings = SettingsModel().get_all_settings()
278 captcha_private_key = settings.get('rhodecode_captcha_private_key')
274 captcha_private_key = settings.get('rhodecode_captcha_private_key')
279 captcha_active = bool(captcha_private_key)
275 captcha_active = bool(captcha_private_key)
280 captcha_public_key = settings.get('rhodecode_captcha_public_key')
276 captcha_public_key = settings.get('rhodecode_captcha_public_key')
281
277
282 render_ctx = {
278 render_ctx = {
283 'captcha_active': captcha_active,
279 'captcha_active': captcha_active,
284 'captcha_public_key': captcha_public_key,
280 'captcha_public_key': captcha_public_key,
285 'defaults': {},
281 'defaults': {},
286 'errors': {},
282 'errors': {},
287 }
283 }
288
284
289 if self.request.POST:
285 if self.request.POST:
290 password_reset_form = PasswordResetForm()()
286 password_reset_form = PasswordResetForm()()
291 try:
287 try:
292 form_result = password_reset_form.to_python(
288 form_result = password_reset_form.to_python(
293 self.request.params)
289 self.request.params)
294 if captcha_active:
290 if captcha_active:
295 response = submit(
291 response = submit(
296 self.request.params.get('recaptcha_challenge_field'),
292 self.request.params.get('recaptcha_challenge_field'),
297 self.request.params.get('recaptcha_response_field'),
293 self.request.params.get('recaptcha_response_field'),
298 private_key=captcha_private_key,
294 private_key=captcha_private_key,
299 remoteip=get_ip_addr(self.request.environ))
295 remoteip=get_ip_addr(self.request.environ))
300 if captcha_active and not response.is_valid:
296 if captcha_active and not response.is_valid:
301 _value = form_result
297 _value = form_result
302 _msg = _('bad captcha')
298 _msg = _('bad captcha')
303 error_dict = {'recaptcha_field': _msg}
299 error_dict = {'recaptcha_field': _msg}
304 raise formencode.Invalid(_msg, _value, None,
300 raise formencode.Invalid(_msg, _value, None,
305 error_dict=error_dict)
301 error_dict=error_dict)
306
302
307 # Generate reset URL and send mail.
303 # Generate reset URL and send mail.
308 user_email = form_result['email']
304 user_email = form_result['email']
309 user = User.get_by_email(user_email)
305 user = User.get_by_email(user_email)
310 password_reset_url = self.request.route_url(
306 password_reset_url = self.request.route_url(
311 'reset_password_confirmation',
307 'reset_password_confirmation',
312 _query={'key': user.api_key})
308 _query={'key': user.api_key})
313 UserModel().reset_password_link(
309 UserModel().reset_password_link(
314 form_result, password_reset_url)
310 form_result, password_reset_url)
315
311
316 # Display success message and redirect.
312 # Display success message and redirect.
317 self.session.flash(
313 self.session.flash(
318 _('Your password reset link was sent'),
314 _('Your password reset link was sent'),
319 queue='success')
315 queue='success')
320 return HTTPFound(self.request.route_path('login'))
316 return HTTPFound(self.request.route_path('login'))
321
317
322 except formencode.Invalid as errors:
318 except formencode.Invalid as errors:
323 render_ctx.update({
319 render_ctx.update({
324 'defaults': errors.value,
320 'defaults': errors.value,
325 'errors': errors.error_dict,
321 'errors': errors.error_dict,
326 })
322 })
327
323
328 return render_ctx
324 return render_ctx
329
325
330 @view_config(route_name='reset_password_confirmation',
326 @view_config(route_name='reset_password_confirmation',
331 request_method='GET')
327 request_method='GET')
332 def password_reset_confirmation(self):
328 def password_reset_confirmation(self):
333 if self.request.GET and self.request.GET.get('key'):
329 if self.request.GET and self.request.GET.get('key'):
334 try:
330 try:
335 user = User.get_by_auth_token(self.request.GET.get('key'))
331 user = User.get_by_auth_token(self.request.GET.get('key'))
336 data = {'email': user.email}
332 data = {'email': user.email}
337 UserModel().reset_password(data)
333 UserModel().reset_password(data)
338 self.session.flash(
334 self.session.flash(
339 _('Your password reset was successful, '
335 _('Your password reset was successful, '
340 'a new password has been sent to your email'),
336 'a new password has been sent to your email'),
341 queue='success')
337 queue='success')
342 except Exception as e:
338 except Exception as e:
343 log.error(e)
339 log.error(e)
344 return HTTPFound(self.request.route_path('reset_password'))
340 return HTTPFound(self.request.route_path('reset_password'))
345
341
346 return HTTPFound(self.request.route_path('login'))
342 return HTTPFound(self.request.route_path('login'))
@@ -1,54 +1,55 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2010-2016 RhodeCode GmbH
3 # Copyright (C) 2010-2016 RhodeCode GmbH
4 #
4 #
5 # This program is free software: you can redistribute it and/or modify
5 # This program is free software: you can redistribute it and/or modify
6 # it under the terms of the GNU Affero General Public License, version 3
6 # it under the terms of the GNU Affero General Public License, version 3
7 # (only), as published by the Free Software Foundation.
7 # (only), as published by the Free Software Foundation.
8 #
8 #
9 # This program is distributed in the hope that it will be useful,
9 # This program is distributed in the hope that it will be useful,
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 # GNU General Public License for more details.
12 # GNU General Public License for more details.
13 #
13 #
14 # You should have received a copy of the GNU Affero General Public License
14 # You should have received a copy of the GNU Affero General Public License
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 #
16 #
17 # This program is dual-licensed. If you wish to learn more about the
17 # This program is dual-licensed. If you wish to learn more about the
18 # RhodeCode Enterprise Edition, including its added features, Support services,
18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20
20
21
21
22 import pylons
22 import pylons
23 from pyramid.i18n import get_localizer, TranslationStringFactory
23
24 from pyramid.i18n import get_localizer
24 from pyramid.threadlocal import get_current_request
25 from pyramid.threadlocal import get_current_request
25
26
26 tsf = TranslationStringFactory('rc_root')
27 from rhodecode.translation import _ as tsf
27
28
28
29
29 def add_renderer_globals(event):
30 def add_renderer_globals(event):
30 # Put pylons stuff into the context. This will be removed as soon as
31 # Put pylons stuff into the context. This will be removed as soon as
31 # migration to pyramid is finished.
32 # migration to pyramid is finished.
32 conf = pylons.config._current_obj()
33 conf = pylons.config._current_obj()
33 event['h'] = conf.get('pylons.h')
34 event['h'] = conf.get('pylons.h')
34 event['c'] = pylons.tmpl_context
35 event['c'] = pylons.tmpl_context
35 event['url'] = pylons.url
36 event['url'] = pylons.url
36
37
37 # TODO: When executed in pyramid view context the request is not available
38 # TODO: When executed in pyramid view context the request is not available
38 # in the event. Find a better solution to get the request.
39 # in the event. Find a better solution to get the request.
39 request = event['request'] or get_current_request()
40 request = event['request'] or get_current_request()
40
41
41 # Add Pyramid translation as '_' to context
42 # Add Pyramid translation as '_' to context
42 event['_'] = request.translate
43 event['_'] = request.translate
43 event['localizer'] = request.localizer
44 event['localizer'] = request.localizer
44
45
45
46
46 def add_localizer(event):
47 def add_localizer(event):
47 request = event.request
48 request = event.request
48 localizer = get_localizer(request)
49 localizer = get_localizer(request)
49
50
50 def auto_translate(*args, **kwargs):
51 def auto_translate(*args, **kwargs):
51 return localizer.translate(tsf(*args, **kwargs))
52 return localizer.translate(tsf(*args, **kwargs))
52
53
53 request.localizer = localizer
54 request.localizer = localizer
54 request.translate = auto_translate
55 request.translate = auto_translate
General Comments 0
You need to be logged in to leave comments. Login now