##// END OF EJS Templates
authn: Fix handling of form errors and default values.
johbo -
r90:1b568d51 default
parent child Browse files
Show More
@@ -1,178 +1,182 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2016 RhodeCode GmbH
4 4 #
5 5 # This program is free software: you can redistribute it and/or modify
6 6 # it under the terms of the GNU Affero General Public License, version 3
7 7 # (only), as published by the Free Software Foundation.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU Affero General Public License
15 15 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 16 #
17 17 # This program is dual-licensed. If you wish to learn more about the
18 18 # RhodeCode Enterprise Edition, including its added features, Support services,
19 19 # and proprietary license terms, please see https://rhodecode.com/licenses/
20 20
21 21 import colander
22 22 import formencode.htmlfill
23 23 import logging
24 24
25 25 from pyramid.httpexceptions import HTTPFound
26 26 from pyramid.renderers import render
27 27 from pyramid.response import Response
28 28
29 29 from rhodecode.authentication.base import get_auth_cache_manager
30 30 from rhodecode.authentication.interface import IAuthnPluginRegistry
31 31 from rhodecode.lib import auth
32 32 from rhodecode.lib.auth import LoginRequired, HasPermissionAllDecorator
33 33 from rhodecode.model.forms import AuthSettingsForm
34 34 from rhodecode.model.meta import Session
35 35 from rhodecode.model.settings import SettingsModel
36 36 from rhodecode.translation import _
37 37
38 38 log = logging.getLogger(__name__)
39 39
40 40
41 41 class AuthnPluginViewBase(object):
42 42
43 43 def __init__(self, context, request):
44 44 self.request = request
45 45 self.context = context
46 46 self.plugin = context.plugin
47 47
48 def settings_get(self, errors={}):
48 def settings_get(self, defaults=None, errors=None):
49 49 """
50 50 View that displays the plugin settings as a form.
51 51 """
52 defaults = defaults or {}
53 errors = errors or {}
52 54 schema = self.plugin.get_settings_schema()
53 55
54 56 # Get default values for the form.
55 for node in schema.children:
56 value = self.plugin.get_setting_by_name(node.name)
57 if value:
58 node.default = value
57 for node in schema:
58 db_value = self.plugin.get_setting_by_name(node.name)
59 defaults.setdefault(node.name, db_value)
59 60
60 61 template_context = {
62 'defaults': defaults,
61 63 'errors': errors,
62 64 'plugin': self.context.plugin,
63 65 'resource': self.context,
64 66 }
65 67
66 68 return template_context
67 69
68 70 def settings_post(self):
69 71 """
70 72 View that validates and stores the plugin settings.
71 73 """
72 74 schema = self.plugin.get_settings_schema()
73 75 try:
74 76 valid_data = schema.deserialize(self.request.params)
75 77 except colander.Invalid, e:
76 78 # Display error message and display form again.
77 79 self.request.session.flash(
78 80 _('Errors exist when saving plugin settings. '
79 81 'Please check the form inputs.'),
80 82 queue='error')
81 return self.settings_get(errors=e.asdict())
83 defaults = schema.flatten(self.request.params)
84 return self.settings_get(errors=e.asdict(), defaults=defaults)
82 85
83 86 # Store validated data.
84 87 for name, value in valid_data.items():
85 88 self.plugin.create_or_update_setting(name, value)
86 89 Session.commit()
87 90
88 91 # Display success message and redirect.
89 92 self.request.session.flash(
90 93 _('Auth settings updated successfully.'),
91 94 queue='success')
92 95 redirect_to = self.request.resource_path(
93 96 self.context, route_name='auth_home')
94 97 return HTTPFound(redirect_to)
95 98
96 99
97 100 # TODO: Ongoing migration in these views.
98 101 # - Maybe we should also use a colander schema for these views.
99 102 class AuthSettingsView(object):
100 103 def __init__(self, context, request):
101 104 self.context = context
102 105 self.request = request
103 106
104 107 # TODO: Move this into a utility function. It is needed in all view
105 108 # classes during migration. Maybe a mixin?
106 109
107 110 # Some of the decorators rely on this attribute to be present on the
108 111 # class of the decorated method.
109 112 self._rhodecode_user = request.user
110 113
111 114 @LoginRequired()
112 115 @HasPermissionAllDecorator('hg.admin')
113 def index(self, defaults={}, errors=None, prefix_error=False):
116 def index(self, defaults=None, errors=None, prefix_error=False):
117 defaults = defaults or {}
114 118 authn_registry = self.request.registry.getUtility(IAuthnPluginRegistry)
115 119 enabled_plugins = SettingsModel().get_auth_plugins()
116 120
117 121 # Create template context and render it.
118 122 template_context = {
119 123 'resource': self.context,
120 124 'available_plugins': authn_registry.get_plugins(),
121 125 'enabled_plugins': enabled_plugins,
122 126 }
123 127 html = render('rhodecode:templates/admin/auth/auth_settings.html',
124 128 template_context,
125 129 request=self.request)
126 130
127 131 # Create form default values and fill the form.
128 132 form_defaults = {
129 133 'auth_plugins': ','.join(enabled_plugins)
130 134 }
131 135 form_defaults.update(defaults)
132 136 html = formencode.htmlfill.render(
133 137 html,
134 138 defaults=form_defaults,
135 139 errors=errors,
136 140 prefix_error=prefix_error,
137 141 encoding="UTF-8",
138 142 force_defaults=False)
139 143
140 144 return Response(html)
141 145
142 146 @LoginRequired()
143 147 @HasPermissionAllDecorator('hg.admin')
144 148 @auth.CSRFRequired()
145 149 def auth_settings(self):
146 150 try:
147 151 form = AuthSettingsForm()()
148 152 form_result = form.to_python(self.request.params)
149 153 plugins = ','.join(form_result['auth_plugins'])
150 154 setting = SettingsModel().create_or_update_setting(
151 155 'auth_plugins', plugins)
152 156 Session().add(setting)
153 157 Session().commit()
154 158
155 159 cache_manager = get_auth_cache_manager()
156 160 cache_manager.clear()
157 161 self.request.session.flash(
158 162 _('Auth settings updated successfully.'),
159 163 queue='success')
160 164 except formencode.Invalid as errors:
161 165 e = errors.error_dict or {}
162 166 self.request.session.flash(
163 167 _('Errors exist when saving plugin setting. '
164 168 'Please check the form inputs.'),
165 169 queue='error')
166 170 return self.index(
167 171 defaults=errors.value,
168 172 errors=e,
169 173 prefix_error=False)
170 174 except Exception:
171 175 log.exception('Exception in auth_settings')
172 176 self.request.session.flash(
173 177 _('Error occurred during update of auth settings.'),
174 178 queue='error')
175 179
176 180 redirect_to = self.request.resource_path(
177 181 self.context, route_name='auth_home')
178 182 return HTTPFound(redirect_to)
@@ -1,116 +1,116 b''
1 1 ## -*- coding: utf-8 -*-
2 2 <%inherit file="/base/base.html"/>
3 3
4 4 <%def name="title()">
5 5 ${_('Authentication Settings')}
6 6 %if c.rhodecode_name:
7 7 &middot; ${h.branding(c.rhodecode_name)}}
8 8 %endif
9 9 </%def>
10 10
11 11 <%def name="breadcrumbs_links()">
12 12 ${h.link_to(_('Admin'),h.url('admin_home'))}
13 13 &raquo;
14 14 ${h.link_to(_('Authentication Plugins'),request.resource_path(resource.__parent__, route_name='auth_home'))}
15 15 &raquo;
16 16 ${resource.display_name}
17 17 </%def>
18 18
19 19 <%def name="menu_bar_nav()">
20 20 ${self.menu_items(active='admin')}
21 21 </%def>
22 22
23 23 <%def name="main()">
24 24 <div class="box">
25 25 <div class="title">
26 26 ${self.breadcrumbs()}
27 27 </div>
28 28 <div class='sidebar-col-wrapper'>
29 29
30 30 ## TODO: This is repeated in the auth root template and should be merged
31 31 ## into a single solution.
32 32 <div class="sidebar">
33 33 <ul class="nav nav-pills nav-stacked">
34 34 % for item in resource.get_root().get_nav_list():
35 35 <li ${'class=active' if item == resource else ''}>
36 36 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
37 37 </li>
38 38 % endfor
39 39 </ul>
40 40 </div>
41 41
42 42 <div class="main-content-full-width">
43 43 <div class="panel panel-default">
44 44 <div class="panel-heading">
45 45 <h3 class="panel-title">${_('Plugin')}: ${resource.display_name}</h3>
46 46 </div>
47 47 <div class="panel-body">
48 48 <div class="plugin_form">
49 49 <div class="fields">
50 50 ${h.secure_form(request.resource_path(resource, route_name='auth_home'))}
51 51 <div class="form">
52 52
53 53 %for node in plugin.get_settings_schema():
54 54 <% label_css_class = ("label-checkbox" if (node.widget == "bool") else "") %>
55 55 <div class="field">
56 56 <div class="label ${label_css_class}"><label for="${node.name}">${node.title}</label></div>
57 57 <div class="input">
58 58 %if node.widget in ["string", "int", "unicode"]:
59 ${h.text(node.name, node.default, class_="medium")}
59 ${h.text(node.name, defaults.get(node.name), class_="medium")}
60 60 %elif node.widget == "password":
61 ${h.password(node.name, node.default, class_="medium")}
61 ${h.password(node.name, defaults.get(node.name), class_="medium")}
62 62 %elif node.widget == "bool":
63 <div class="checkbox">${h.checkbox(node.name, node.default)}</div>
63 <div class="checkbox">${h.checkbox(node.name, True, checked=defaults.get(node.name))}</div>
64 64 %elif node.widget == "select":
65 ${h.select(node.name, node.default, node.validator.choices)}
65 ${h.select(node.name, defaults.get(node.name), node.validator.choices)}
66 66 %elif node.widget == "readonly":
67 67 ${node.default}
68 68 %else:
69 69 This field is of type ${node.typ}, which cannot be displayed. Must be one of [string|int|bool|select].
70 70 %endif
71 71 %if node.name in errors:
72 72 <span class="error-message">${errors.get(node.name)}</span>
73 73 <br />
74 74 %endif
75 75 <p class="help-block">${node.description}</p>
76 76 </div>
77 77 </div>
78 78 %endfor
79 79
80 80 ## Allow derived templates to add something below the form
81 81 ## input fields
82 82 %if hasattr(next, 'below_form_fields'):
83 83 ${next.below_form_fields()}
84 84 %endif
85 85
86 86 <div class="buttons">
87 87 ${h.submit('save',_('Save'),class_="btn")}
88 88 </div>
89 89
90 90 </div>
91 91 ${h.end_form()}
92 92 </div>
93 93 </div>
94 94 </div>
95 95 </div>
96 96 </div>
97 97
98 98 </div>
99 99 </div>
100 100 </%def>
101 101
102 102 ## TODO: Ugly hack to get ldap select elements to work.
103 103 ## Find a solution to integrate this nicely.
104 104 <script>
105 105 $(document).ready(function() {
106 106 var select2Options = {
107 107 containerCssClass: 'drop-menu',
108 108 dropdownCssClass: 'drop-menu-dropdown',
109 109 dropdownAutoWidth: true,
110 110 minimumResultsForSearch: -1
111 111 };
112 112 $("#tls_kind").select2(select2Options);
113 113 $("#tls_reqcert").select2(select2Options);
114 114 $("#search_scope").select2(select2Options);
115 115 });
116 116 </script>
General Comments 0
You need to be logged in to leave comments. Login now