##// END OF EJS Templates
auth-plugins: use a nicer visual display of auth plugins that would highlight that order is...
marcink -
r2659:8b68aff1 default
parent child Browse files
Show More
@@ -1,191 +1,191 b''
1 # -*- coding: utf-8 -*-
1 # -*- coding: utf-8 -*-
2
2
3 # Copyright (C) 2012-2018 RhodeCode GmbH
3 # Copyright (C) 2012-2018 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.renderers import render
26 from pyramid.renderers import render
27 from pyramid.response import Response
27 from pyramid.response import Response
28
28
29 from rhodecode.apps._base import BaseAppView
29 from rhodecode.apps._base import BaseAppView
30 from rhodecode.authentication.base import (
30 from rhodecode.authentication.base import (
31 get_auth_cache_manager, get_perms_cache_manager, get_authn_registry)
31 get_auth_cache_manager, get_perms_cache_manager, get_authn_registry)
32 from rhodecode.lib import helpers as h
32 from rhodecode.lib import helpers as h
33 from rhodecode.lib.auth import (
33 from rhodecode.lib.auth import (
34 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
35 from rhodecode.lib.caches import clear_cache_manager
35 from rhodecode.lib.caches import clear_cache_manager
36 from rhodecode.model.forms import AuthSettingsForm
36 from rhodecode.model.forms import AuthSettingsForm
37 from rhodecode.model.meta import Session
37 from rhodecode.model.meta import Session
38 from rhodecode.model.settings import SettingsModel
38 from rhodecode.model.settings import SettingsModel
39
39
40 log = logging.getLogger(__name__)
40 log = logging.getLogger(__name__)
41
41
42
42
43 class AuthnPluginViewBase(BaseAppView):
43 class AuthnPluginViewBase(BaseAppView):
44
44
45 def load_default_context(self):
45 def load_default_context(self):
46 c = self._get_local_tmpl_context()
46 c = self._get_local_tmpl_context()
47 self.plugin = self.context.plugin
47 self.plugin = self.context.plugin
48 return c
48 return c
49
49
50 @LoginRequired()
50 @LoginRequired()
51 @HasPermissionAllDecorator('hg.admin')
51 @HasPermissionAllDecorator('hg.admin')
52 def settings_get(self, defaults=None, errors=None):
52 def settings_get(self, defaults=None, errors=None):
53 """
53 """
54 View that displays the plugin settings as a form.
54 View that displays the plugin settings as a form.
55 """
55 """
56 c = self.load_default_context()
56 c = self.load_default_context()
57 defaults = defaults or {}
57 defaults = defaults or {}
58 errors = errors or {}
58 errors = errors or {}
59 schema = self.plugin.get_settings_schema()
59 schema = self.plugin.get_settings_schema()
60
60
61 # Compute default values for the form. Priority is:
61 # Compute default values for the form. Priority is:
62 # 1. Passed to this method 2. DB value 3. Schema default
62 # 1. Passed to this method 2. DB value 3. Schema default
63 for node in schema:
63 for node in schema:
64 if node.name not in defaults:
64 if node.name not in defaults:
65 defaults[node.name] = self.plugin.get_setting_by_name(
65 defaults[node.name] = self.plugin.get_setting_by_name(
66 node.name, node.default, cache=False)
66 node.name, node.default, cache=False)
67
67
68 template_context = {
68 template_context = {
69 'defaults': defaults,
69 'defaults': defaults,
70 'errors': errors,
70 'errors': errors,
71 'plugin': self.context.plugin,
71 'plugin': self.context.plugin,
72 'resource': self.context,
72 'resource': self.context,
73 }
73 }
74
74
75 return self._get_template_context(c, **template_context)
75 return self._get_template_context(c, **template_context)
76
76
77 @LoginRequired()
77 @LoginRequired()
78 @HasPermissionAllDecorator('hg.admin')
78 @HasPermissionAllDecorator('hg.admin')
79 @CSRFRequired()
79 @CSRFRequired()
80 def settings_post(self):
80 def settings_post(self):
81 """
81 """
82 View that validates and stores the plugin settings.
82 View that validates and stores the plugin settings.
83 """
83 """
84 _ = self.request.translate
84 _ = self.request.translate
85 self.load_default_context()
85 self.load_default_context()
86 schema = self.plugin.get_settings_schema()
86 schema = self.plugin.get_settings_schema()
87 data = self.request.params
87 data = self.request.params
88
88
89 try:
89 try:
90 valid_data = schema.deserialize(data)
90 valid_data = schema.deserialize(data)
91 except colander.Invalid as e:
91 except colander.Invalid as e:
92 # Display error message and display form again.
92 # Display error message and display form again.
93 h.flash(
93 h.flash(
94 _('Errors exist when saving plugin settings. '
94 _('Errors exist when saving plugin settings. '
95 'Please check the form inputs.'),
95 'Please check the form inputs.'),
96 category='error')
96 category='error')
97 defaults = {key: data[key] for key in data if key in schema}
97 defaults = {key: data[key] for key in data if key in schema}
98 return self.settings_get(errors=e.asdict(), defaults=defaults)
98 return self.settings_get(errors=e.asdict(), defaults=defaults)
99
99
100 # Store validated data.
100 # Store validated data.
101 for name, value in valid_data.items():
101 for name, value in valid_data.items():
102 self.plugin.create_or_update_setting(name, value)
102 self.plugin.create_or_update_setting(name, value)
103 Session().commit()
103 Session().commit()
104
104
105 # cleanup cache managers in case of change for plugin
105 # cleanup cache managers in case of change for plugin
106 # TODO(marcink): because we can register multiple namespaces
106 # TODO(marcink): because we can register multiple namespaces
107 # we should at some point figure out how to retrieve ALL namespace
107 # we should at some point figure out how to retrieve ALL namespace
108 # cache managers and clear them...
108 # cache managers and clear them...
109 cache_manager = get_auth_cache_manager()
109 cache_manager = get_auth_cache_manager()
110 clear_cache_manager(cache_manager)
110 clear_cache_manager(cache_manager)
111
111
112 cache_manager = get_perms_cache_manager()
112 cache_manager = get_perms_cache_manager()
113 clear_cache_manager(cache_manager)
113 clear_cache_manager(cache_manager)
114
114
115 # Display success message and redirect.
115 # Display success message and redirect.
116 h.flash(_('Auth settings updated successfully.'), category='success')
116 h.flash(_('Auth settings updated successfully.'), category='success')
117 redirect_to = self.request.resource_path(
117 redirect_to = self.request.resource_path(
118 self.context, route_name='auth_home')
118 self.context, route_name='auth_home')
119 return HTTPFound(redirect_to)
119 return HTTPFound(redirect_to)
120
120
121
121
122 class AuthSettingsView(BaseAppView):
122 class AuthSettingsView(BaseAppView):
123 def load_default_context(self):
123 def load_default_context(self):
124 c = self._get_local_tmpl_context()
124 c = self._get_local_tmpl_context()
125 return c
125 return c
126
126
127 @LoginRequired()
127 @LoginRequired()
128 @HasPermissionAllDecorator('hg.admin')
128 @HasPermissionAllDecorator('hg.admin')
129 def index(self, defaults=None, errors=None, prefix_error=False):
129 def index(self, defaults=None, errors=None, prefix_error=False):
130 c = self.load_default_context()
130 c = self.load_default_context()
131
131
132 defaults = defaults or {}
132 defaults = defaults or {}
133 authn_registry = get_authn_registry(self.request.registry)
133 authn_registry = get_authn_registry(self.request.registry)
134 enabled_plugins = SettingsModel().get_auth_plugins()
134 enabled_plugins = SettingsModel().get_auth_plugins()
135
135
136 # Create template context and render it.
136 # Create template context and render it.
137 template_context = {
137 template_context = {
138 'resource': self.context,
138 'resource': self.context,
139 'available_plugins': authn_registry.get_plugins(),
139 'available_plugins': authn_registry.get_plugins(),
140 'enabled_plugins': enabled_plugins,
140 'enabled_plugins': enabled_plugins,
141 }
141 }
142 html = render('rhodecode:templates/admin/auth/auth_settings.mako',
142 html = render('rhodecode:templates/admin/auth/auth_settings.mako',
143 self._get_template_context(c, **template_context),
143 self._get_template_context(c, **template_context),
144 self.request)
144 self.request)
145
145
146 # Create form default values and fill the form.
146 # Create form default values and fill the form.
147 form_defaults = {
147 form_defaults = {
148 'auth_plugins': ','.join(enabled_plugins)
148 'auth_plugins': ',\n'.join(enabled_plugins)
149 }
149 }
150 form_defaults.update(defaults)
150 form_defaults.update(defaults)
151 html = formencode.htmlfill.render(
151 html = formencode.htmlfill.render(
152 html,
152 html,
153 defaults=form_defaults,
153 defaults=form_defaults,
154 errors=errors,
154 errors=errors,
155 prefix_error=prefix_error,
155 prefix_error=prefix_error,
156 encoding="UTF-8",
156 encoding="UTF-8",
157 force_defaults=False)
157 force_defaults=False)
158
158
159 return Response(html)
159 return Response(html)
160
160
161 @LoginRequired()
161 @LoginRequired()
162 @HasPermissionAllDecorator('hg.admin')
162 @HasPermissionAllDecorator('hg.admin')
163 @CSRFRequired()
163 @CSRFRequired()
164 def auth_settings(self):
164 def auth_settings(self):
165 _ = self.request.translate
165 _ = self.request.translate
166 try:
166 try:
167 form = AuthSettingsForm(self.request.translate)()
167 form = AuthSettingsForm(self.request.translate)()
168 form_result = form.to_python(self.request.POST)
168 form_result = form.to_python(self.request.POST)
169 plugins = ','.join(form_result['auth_plugins'])
169 plugins = ','.join(form_result['auth_plugins'])
170 setting = SettingsModel().create_or_update_setting(
170 setting = SettingsModel().create_or_update_setting(
171 'auth_plugins', plugins)
171 'auth_plugins', plugins)
172 Session().add(setting)
172 Session().add(setting)
173 Session().commit()
173 Session().commit()
174
174
175 h.flash(_('Auth settings updated successfully.'), category='success')
175 h.flash(_('Auth settings updated successfully.'), category='success')
176 except formencode.Invalid as errors:
176 except formencode.Invalid as errors:
177 e = errors.error_dict or {}
177 e = errors.error_dict or {}
178 h.flash(_('Errors exist when saving plugin setting. '
178 h.flash(_('Errors exist when saving plugin setting. '
179 'Please check the form inputs.'), category='error')
179 'Please check the form inputs.'), category='error')
180 return self.index(
180 return self.index(
181 defaults=errors.value,
181 defaults=errors.value,
182 errors=e,
182 errors=e,
183 prefix_error=False)
183 prefix_error=False)
184 except Exception:
184 except Exception:
185 log.exception('Exception in auth_settings')
185 log.exception('Exception in auth_settings')
186 h.flash(_('Error occurred during update of auth settings.'),
186 h.flash(_('Error occurred during update of auth settings.'),
187 category='error')
187 category='error')
188
188
189 redirect_to = self.request.resource_path(
189 redirect_to = self.request.resource_path(
190 self.context, route_name='auth_home')
190 self.context, route_name='auth_home')
191 return HTTPFound(redirect_to)
191 return HTTPFound(redirect_to)
@@ -1,116 +1,118 b''
1 ## -*- coding: utf-8 -*-
1 ## -*- coding: utf-8 -*-
2 <%inherit file="/base/base.mako"/>
2 <%inherit file="/base/base.mako"/>
3
3
4 <%def name="title()">
4 <%def name="title()">
5 ${_('Authentication Settings')}
5 ${_('Authentication Settings')}
6 %if c.rhodecode_name:
6 %if c.rhodecode_name:
7 &middot; ${h.branding(c.rhodecode_name)}}
7 &middot; ${h.branding(c.rhodecode_name)}}
8 %endif
8 %endif
9 </%def>
9 </%def>
10
10
11 <%def name="breadcrumbs_links()">
11 <%def name="breadcrumbs_links()">
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
12 ${h.link_to(_('Admin'),h.route_path('admin_home'))}
13 &raquo;
13 &raquo;
14 ${_('Authentication Plugins')}
14 ${_('Authentication Plugins')}
15 </%def>
15 </%def>
16
16
17 <%def name="menu_bar_nav()">
17 <%def name="menu_bar_nav()">
18 ${self.menu_items(active='admin')}
18 ${self.menu_items(active='admin')}
19 </%def>
19 </%def>
20
20
21 <%def name="main()">
21 <%def name="main()">
22
22
23 <div class="box">
23 <div class="box">
24 <div class="title">
24 <div class="title">
25 ${self.breadcrumbs()}
25 ${self.breadcrumbs()}
26 </div>
26 </div>
27
27
28 <div class='sidebar-col-wrapper'>
28 <div class='sidebar-col-wrapper'>
29
29
30 <div class="sidebar">
30 <div class="sidebar">
31 <ul class="nav nav-pills nav-stacked">
31 <ul class="nav nav-pills nav-stacked">
32 % for item in resource.get_root().get_nav_list():
32 % for item in resource.get_root().get_nav_list():
33 <li ${'class=active' if item == resource else ''}>
33 <li ${'class=active' if item == resource else ''}>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
34 <a href="${request.resource_path(item, route_name='auth_home')}">${item.display_name}</a>
35 </li>
35 </li>
36 % endfor
36 % endfor
37 </ul>
37 </ul>
38 </div>
38 </div>
39
39
40 <div class="main-content-full-width">
40 <div class="main-content-full-width">
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'), request=request)}
41 ${h.secure_form(request.resource_path(resource, route_name='auth_home'), request=request)}
42 <div class="form">
42 <div class="form">
43
43
44 <div class="panel panel-default">
44 <div class="panel panel-default">
45
45
46 <div class="panel-heading">
46 <div class="panel-heading">
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
47 <h3 class="panel-title">${_("Enabled and Available Plugins")}</h3>
48 </div>
48 </div>
49
49
50 <div class="fields panel-body">
50 <div class="fields panel-body">
51
51
52 <div class="field">
52 <div class="field">
53 <div class="label">${_("Enabled Plugins")}</div>
53 <div class="label">${_("Enabled Plugins")}</div>
54 <div class="textarea text-area editor">
54 <div class="textarea text-area editor">
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
55 ${h.textarea('auth_plugins',cols=23,rows=5,class_="medium")}
56 </div>
56 </div>
57 <p class="help-block">
57 <p class="help-block pre-formatting">${_('List of plugins, separated by commas.'
58 ${_('Add a list of plugins, separated by commas. '
58 '\nThe order of the plugins is also the order in which '
59 'The order of the plugins is also the order in which '
59 'RhodeCode Enterprise will try to authenticate a user.')}</p>
60 'RhodeCode Enterprise will try to authenticate a user.')}
61 </p>
62 </div>
60 </div>
63
61
64 <div class="field">
62 <div class="field">
65 <div class="label">${_('Available Built-in Plugins')}</div>
63 <div class="label">${_('Available Built-in Plugins')}</div>
66 <ul class="auth_plugins">
64 <ul class="auth_plugins">
67 %for plugin in available_plugins:
65 %for plugin in available_plugins:
68 <li>
66 <li>
69 <div class="auth_buttons">
67 <div class="auth_buttons">
70 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
68 <span plugin_id="${plugin.get_id()}" class="toggle-plugin btn ${'btn-success' if plugin.get_id() in enabled_plugins else ''}">
71 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
69 ${_('enabled') if plugin.get_id() in enabled_plugins else _('disabled')}
72 </span>
70 </span>
73 ${plugin.get_display_name()} (${plugin.get_id()})
71 ${plugin.get_display_name()} (${plugin.get_id()})
74 </div>
72 </div>
75 </li>
73 </li>
76 %endfor
74 %endfor
77 </ul>
75 </ul>
78 </div>
76 </div>
79
77
80 <div class="buttons">
78 <div class="buttons">
81 ${h.submit('save',_('Save'),class_="btn")}
79 ${h.submit('save',_('Save'),class_="btn")}
82 </div>
80 </div>
83 </div>
81 </div>
84 </div>
82 </div>
85 </div>
83 </div>
86 ${h.end_form()}
84 ${h.end_form()}
87 </div>
85 </div>
88 </div>
86 </div>
89 </div>
87 </div>
90
88
91 <script>
89 <script>
92 $('.toggle-plugin').click(function(e){
90 $('.toggle-plugin').click(function(e){
93 var auth_plugins_input = $('#auth_plugins');
91 var auth_plugins_input = $('#auth_plugins');
94 var notEmpty = function(element, index, array) {
92 var elems = [];
95 return (element != "");
93
96 };
94 $.each(auth_plugins_input.val().split(',') , function (index, element) {
97 var elems = auth_plugins_input.val().split(',').filter(notEmpty);
95 if (element !== "") {
96 elems.push(element.strip())
97 }
98 });
99
98 var cur_button = e.currentTarget;
100 var cur_button = e.currentTarget;
99 var plugin_id = $(cur_button).attr('plugin_id');
101 var plugin_id = $(cur_button).attr('plugin_id');
100 if($(cur_button).hasClass('btn-success')){
102 if($(cur_button).hasClass('btn-success')){
101 elems.splice(elems.indexOf(plugin_id), 1);
103 elems.splice(elems.indexOf(plugin_id), 1);
102 auth_plugins_input.val(elems.join(','));
104 auth_plugins_input.val(elems.join(',\n'));
103 $(cur_button).removeClass('btn-success');
105 $(cur_button).removeClass('btn-success');
104 cur_button.innerHTML = _gettext('disabled');
106 cur_button.innerHTML = _gettext('disabled');
105 }
107 }
106 else{
108 else{
107 if(elems.indexOf(plugin_id) == -1){
109 if(elems.indexOf(plugin_id) == -1){
108 elems.push(plugin_id);
110 elems.push(plugin_id);
109 }
111 }
110 auth_plugins_input.val(elems.join(','));
112 auth_plugins_input.val(elems.join(',\n'));
111 $(cur_button).addClass('btn-success');
113 $(cur_button).addClass('btn-success');
112 cur_button.innerHTML = _gettext('enabled');
114 cur_button.innerHTML = _gettext('enabled');
113 }
115 }
114 });
116 });
115 </script>
117 </script>
116 </%def>
118 </%def>
General Comments 0
You need to be logged in to leave comments. Login now