##// END OF EJS Templates
flash: use consistent use of h.flash across the application.
marcink -
r2366:38c79c48 default
parent child Browse files
Show More
@@ -1,102 +1,101 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 logging
22 22
23 23 from pyramid.view import view_config
24 24 from pyramid.httpexceptions import HTTPFound
25 25
26 26 from rhodecode.apps._base import BaseAppView
27 27 from rhodecode.apps.admin.navigation import navigation_list
28 28 from rhodecode.lib.auth import (
29 29 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
30 30 from rhodecode.lib.utils2 import safe_int
31 31 from rhodecode.lib import system_info
32 32 from rhodecode.lib import user_sessions
33 from rhodecode.lib import helpers as h
33 34
34 35
35 36 log = logging.getLogger(__name__)
36 37
37 38
38 39 class AdminSessionSettingsView(BaseAppView):
39 40 def load_default_context(self):
40 41 c = self._get_local_tmpl_context()
41 42
42 43
43 44 return c
44 45
45 46 @LoginRequired()
46 47 @HasPermissionAllDecorator('hg.admin')
47 48 @view_config(
48 49 route_name='admin_settings_sessions', request_method='GET',
49 50 renderer='rhodecode:templates/admin/settings/settings.mako')
50 51 def settings_sessions(self):
51 52 c = self.load_default_context()
52 53
53 54 c.active = 'sessions'
54 55 c.navlist = navigation_list(self.request)
55 56
56 57 c.cleanup_older_days = 60
57 58 older_than_seconds = 60 * 60 * 24 * c.cleanup_older_days
58 59
59 60 config = system_info.rhodecode_config().get_value()['value']['config']
60 61 c.session_model = user_sessions.get_session_handler(
61 62 config.get('beaker.session.type', 'memory'))(config)
62 63
63 64 c.session_conf = c.session_model.config
64 65 c.session_count = c.session_model.get_count()
65 66 c.session_expired_count = c.session_model.get_expired_count(
66 67 older_than_seconds)
67 68
68 69 return self._get_template_context(c)
69 70
70 71 @LoginRequired()
71 72 @HasPermissionAllDecorator('hg.admin')
72 73 @CSRFRequired()
73 74 @view_config(
74 75 route_name='admin_settings_sessions_cleanup', request_method='POST')
75 76 def settings_sessions_cleanup(self):
76 77 _ = self.request.translate
77 78 expire_days = safe_int(self.request.params.get('expire_days'))
78 79
79 80 if expire_days is None:
80 81 expire_days = 60
81 82
82 83 older_than_seconds = 60 * 60 * 24 * expire_days
83 84
84 85 config = system_info.rhodecode_config().get_value()['value']['config']
85 86 session_model = user_sessions.get_session_handler(
86 87 config.get('beaker.session.type', 'memory'))(config)
87 88
88 89 try:
89 90 session_model.clean_sessions(
90 91 older_than_seconds=older_than_seconds)
91 self.request.session.flash(
92 _('Cleaned up old sessions'), queue='success')
92 h.flash(_('Cleaned up old sessions'), category='success')
93 93 except user_sessions.CleanupCommand as msg:
94 self.request.session.flash(msg.message, queue='warning')
94 h.flash(msg.message, category='warning')
95 95 except Exception as e:
96 96 log.exception('Failed session cleanup')
97 self.request.session.flash(
98 _('Failed to cleanup up old sessions'), queue='error')
97 h.flash(_('Failed to cleanup up old sessions'), category='error')
99 98
100 99 redirect_to = self.request.resource_path(
101 100 self.context, route_name='admin_settings_sessions')
102 101 return HTTPFound(redirect_to)
@@ -1,209 +1,208 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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 logging
22 22 import urllib2
23 23 import packaging.version
24 24
25 25 from pyramid.view import view_config
26 26
27 27 import rhodecode
28 28 from rhodecode.apps._base import BaseAppView
29 29 from rhodecode.apps.admin.navigation import navigation_list
30 30 from rhodecode.lib import helpers as h
31 31 from rhodecode.lib.auth import (LoginRequired, HasPermissionAllDecorator)
32 32 from rhodecode.lib.utils2 import str2bool
33 33 from rhodecode.lib import system_info
34 34 from rhodecode.lib.ext_json import json
35 35 from rhodecode.model.settings import SettingsModel
36 36
37 37 log = logging.getLogger(__name__)
38 38
39 39
40 40 class AdminSystemInfoSettingsView(BaseAppView):
41 41 def load_default_context(self):
42 42 c = self._get_local_tmpl_context()
43 43
44 44 return c
45 45
46 46 @staticmethod
47 47 def get_update_data(update_url):
48 48 """Return the JSON update data."""
49 49 ver = rhodecode.__version__
50 50 log.debug('Checking for upgrade on `%s` server', update_url)
51 51 opener = urllib2.build_opener()
52 52 opener.addheaders = [('User-agent', 'RhodeCode-SCM/%s' % ver)]
53 53 response = opener.open(update_url)
54 54 response_data = response.read()
55 55 data = json.loads(response_data)
56 56
57 57 return data
58 58
59 59 def get_update_url(self):
60 60 settings = SettingsModel().get_all_settings()
61 61 return settings.get('rhodecode_update_url')
62 62
63 63 @LoginRequired()
64 64 @HasPermissionAllDecorator('hg.admin')
65 65 @view_config(
66 66 route_name='admin_settings_system', request_method='GET',
67 67 renderer='rhodecode:templates/admin/settings/settings.mako')
68 68 def settings_system_info(self):
69 69 _ = self.request.translate
70 70 c = self.load_default_context()
71 71
72 72 c.active = 'system'
73 73 c.navlist = navigation_list(self.request)
74 74
75 75 # TODO(marcink), figure out how to allow only selected users to do this
76 76 c.allowed_to_snapshot = self._rhodecode_user.admin
77 77
78 78 snapshot = str2bool(self.request.params.get('snapshot'))
79 79
80 80 c.rhodecode_update_url = self.get_update_url()
81 81 server_info = system_info.get_system_info(self.request.environ)
82 82
83 83 for key, val in server_info.items():
84 84 setattr(c, key, val)
85 85
86 86 def val(name, subkey='human_value'):
87 87 return server_info[name][subkey]
88 88
89 89 def state(name):
90 90 return server_info[name]['state']
91 91
92 92 def val2(name):
93 93 val = server_info[name]['human_value']
94 94 state = server_info[name]['state']
95 95 return val, state
96 96
97 97 update_info_msg = _('Note: please make sure this server can '
98 98 'access `${url}` for the update link to work',
99 99 mapping=dict(url=c.rhodecode_update_url))
100 100 c.data_items = [
101 101 # update info
102 102 (_('Update info'), h.literal(
103 103 '<span class="link" id="check_for_update" >%s.</span>' % (
104 104 _('Check for updates')) +
105 105 '<br/> <span >%s.</span>' % (update_info_msg)
106 106 ), ''),
107 107
108 108 # RhodeCode specific
109 109 (_('RhodeCode Version'), val('rhodecode_app')['text'], state('rhodecode_app')),
110 110 (_('RhodeCode Server IP'), val('server')['server_ip'], state('server')),
111 111 (_('RhodeCode Server ID'), val('server')['server_id'], state('server')),
112 112 (_('RhodeCode Configuration'), val('rhodecode_config')['path'], state('rhodecode_config')),
113 113 (_('RhodeCode Certificate'), val('rhodecode_config')['cert_path'], state('rhodecode_config')),
114 114 (_('Workers'), val('rhodecode_config')['config']['server:main'].get('workers', '?'), state('rhodecode_config')),
115 115 (_('Worker Type'), val('rhodecode_config')['config']['server:main'].get('worker_class', 'sync'), state('rhodecode_config')),
116 116 ('', '', ''), # spacer
117 117
118 118 # Database
119 119 (_('Database'), val('database')['url'], state('database')),
120 120 (_('Database version'), val('database')['version'], state('database')),
121 121 ('', '', ''), # spacer
122 122
123 123 # Platform/Python
124 124 (_('Platform'), val('platform')['name'], state('platform')),
125 125 (_('Platform UUID'), val('platform')['uuid'], state('platform')),
126 126 (_('Python version'), val('python')['version'], state('python')),
127 127 (_('Python path'), val('python')['executable'], state('python')),
128 128 ('', '', ''), # spacer
129 129
130 130 # Systems stats
131 131 (_('CPU'), val('cpu')['text'], state('cpu')),
132 132 (_('Load'), val('load')['text'], state('load')),
133 133 (_('Memory'), val('memory')['text'], state('memory')),
134 134 (_('Uptime'), val('uptime')['text'], state('uptime')),
135 135 ('', '', ''), # spacer
136 136
137 137 # Repo storage
138 138 (_('Storage location'), val('storage')['path'], state('storage')),
139 139 (_('Storage info'), val('storage')['text'], state('storage')),
140 140 (_('Storage inodes'), val('storage_inodes')['text'], state('storage_inodes')),
141 141
142 142 (_('Gist storage location'), val('storage_gist')['path'], state('storage_gist')),
143 143 (_('Gist storage info'), val('storage_gist')['text'], state('storage_gist')),
144 144
145 145 (_('Archive cache storage location'), val('storage_archive')['path'], state('storage_archive')),
146 146 (_('Archive cache info'), val('storage_archive')['text'], state('storage_archive')),
147 147
148 148 (_('Temp storage location'), val('storage_temp')['path'], state('storage_temp')),
149 149 (_('Temp storage info'), val('storage_temp')['text'], state('storage_temp')),
150 150
151 151 (_('Search info'), val('search')['text'], state('search')),
152 152 (_('Search location'), val('search')['location'], state('search')),
153 153 ('', '', ''), # spacer
154 154
155 155 # VCS specific
156 156 (_('VCS Backends'), val('vcs_backends'), state('vcs_backends')),
157 157 (_('VCS Server'), val('vcs_server')['text'], state('vcs_server')),
158 158 (_('GIT'), val('git'), state('git')),
159 159 (_('HG'), val('hg'), state('hg')),
160 160 (_('SVN'), val('svn'), state('svn')),
161 161
162 162 ]
163 163
164 164 if snapshot:
165 165 if c.allowed_to_snapshot:
166 166 c.data_items.pop(0) # remove server info
167 167 self.request.override_renderer = 'admin/settings/settings_system_snapshot.mako'
168 168 else:
169 self.request.session.flash(
170 'You are not allowed to do this', queue='warning')
169 h.flash('You are not allowed to do this', category='warning')
171 170 return self._get_template_context(c)
172 171
173 172 @LoginRequired()
174 173 @HasPermissionAllDecorator('hg.admin')
175 174 @view_config(
176 175 route_name='admin_settings_system_update', request_method='GET',
177 176 renderer='rhodecode:templates/admin/settings/settings_system_update.mako')
178 177 def settings_system_info_check_update(self):
179 178 _ = self.request.translate
180 179 c = self.load_default_context()
181 180
182 181 update_url = self.get_update_url()
183 182
184 183 _err = lambda s: '<div style="color:#ff8888; padding:4px 0px">{}</div>'.format(s)
185 184 try:
186 185 data = self.get_update_data(update_url)
187 186 except urllib2.URLError as e:
188 187 log.exception("Exception contacting upgrade server")
189 188 self.request.override_renderer = 'string'
190 189 return _err('Failed to contact upgrade server: %r' % e)
191 190 except ValueError as e:
192 191 log.exception("Bad data sent from update server")
193 192 self.request.override_renderer = 'string'
194 193 return _err('Bad data sent from update server')
195 194
196 195 latest = data['versions'][0]
197 196
198 197 c.update_url = update_url
199 198 c.latest_data = latest
200 199 c.latest_ver = latest['version']
201 200 c.cur_ver = rhodecode.__version__
202 201 c.should_upgrade = False
203 202
204 203 if (packaging.version.Version(c.latest_ver) >
205 204 packaging.version.Version(c.cur_ver)):
206 205 c.should_upgrade = True
207 206 c.important_notices = latest['general']
208 207
209 208 return self._get_template_context(c)
@@ -1,92 +1,92 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2016-2017 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
22 22 import pytest
23 23
24 24
25 class EnabledAuthPlugin():
25 class EnabledAuthPlugin(object):
26 26 """
27 27 Context manager that updates the 'auth_plugins' setting in DB to enable
28 28 a plugin. Previous setting is restored on exit. The rhodecode auth plugin
29 29 is also enabled because it is needed to login the test users.
30 30 """
31 31
32 32 def __init__(self, plugin):
33 33 self.new_value = set([
34 34 'egg:rhodecode-enterprise-ce#rhodecode',
35 35 plugin.get_id()
36 36 ])
37 37
38 38 def __enter__(self):
39 39 from rhodecode.model.settings import SettingsModel
40 40 self._old_value = SettingsModel().get_auth_plugins()
41 41 SettingsModel().create_or_update_setting(
42 42 'auth_plugins', ','.join(self.new_value))
43 43
44 44 def __exit__(self, type, value, traceback):
45 45 from rhodecode.model.settings import SettingsModel
46 46 SettingsModel().create_or_update_setting(
47 47 'auth_plugins', ','.join(self._old_value))
48 48
49 49
50 50 class DisabledAuthPlugin():
51 51 """
52 52 Context manager that updates the 'auth_plugins' setting in DB to disable
53 53 a plugin. Previous setting is restored on exit.
54 54 """
55 55
56 56 def __init__(self, plugin):
57 57 self.plugin_id = plugin.get_id()
58 58
59 59 def __enter__(self):
60 60 from rhodecode.model.settings import SettingsModel
61 61 self._old_value = SettingsModel().get_auth_plugins()
62 62 new_value = [id_ for id_ in self._old_value if id_ != self.plugin_id]
63 63 SettingsModel().create_or_update_setting(
64 64 'auth_plugins', ','.join(new_value))
65 65
66 66 def __exit__(self, type, value, traceback):
67 67 from rhodecode.model.settings import SettingsModel
68 68 SettingsModel().create_or_update_setting(
69 69 'auth_plugins', ','.join(self._old_value))
70 70
71 71
72 72 @pytest.fixture(params=[
73 73 ('rhodecode.authentication.plugins.auth_crowd', 'egg:rhodecode-enterprise-ce#crowd'),
74 74 ('rhodecode.authentication.plugins.auth_headers', 'egg:rhodecode-enterprise-ce#headers'),
75 75 ('rhodecode.authentication.plugins.auth_jasig_cas', 'egg:rhodecode-enterprise-ce#jasig_cas'),
76 76 ('rhodecode.authentication.plugins.auth_ldap', 'egg:rhodecode-enterprise-ce#ldap'),
77 77 ('rhodecode.authentication.plugins.auth_pam', 'egg:rhodecode-enterprise-ce#pam'),
78 78 ('rhodecode.authentication.plugins.auth_rhodecode', 'egg:rhodecode-enterprise-ce#rhodecode'),
79 79 ('rhodecode.authentication.plugins.auth_token', 'egg:rhodecode-enterprise-ce#token'),
80 80 ])
81 81 def auth_plugin(request):
82 82 """
83 83 Fixture that provides instance for each authentication plugin. These
84 84 instances are NOT the instances which are registered to the authentication
85 85 registry.
86 86 """
87 87 from importlib import import_module
88 88
89 89 # Create plugin instance.
90 90 module, plugin_id = request.param
91 91 plugin_module = import_module(module)
92 92 return plugin_module.plugin_factory(plugin_id)
@@ -1,192 +1,186 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2017 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.apps._base import BaseAppView
30 30 from rhodecode.authentication.base import (
31 31 get_auth_cache_manager, get_perms_cache_manager, get_authn_registry)
32 from rhodecode.lib import helpers as h
32 33 from rhodecode.lib.auth import (
33 34 LoginRequired, HasPermissionAllDecorator, CSRFRequired)
34 35 from rhodecode.model.forms import AuthSettingsForm
35 36 from rhodecode.model.meta import Session
36 37 from rhodecode.model.settings import SettingsModel
37 38
38 39 log = logging.getLogger(__name__)
39 40
40 41
41 42 class AuthnPluginViewBase(BaseAppView):
42 43
43 44 def load_default_context(self):
44 45 c = self._get_local_tmpl_context()
45 46 self.plugin = self.context.plugin
46 47 return c
47 48
48 49 @LoginRequired()
49 50 @HasPermissionAllDecorator('hg.admin')
50 51 def settings_get(self, defaults=None, errors=None):
51 52 """
52 53 View that displays the plugin settings as a form.
53 54 """
54 55 c = self.load_default_context()
55 56 defaults = defaults or {}
56 57 errors = errors or {}
57 58 schema = self.plugin.get_settings_schema()
58 59
59 60 # Compute default values for the form. Priority is:
60 61 # 1. Passed to this method 2. DB value 3. Schema default
61 62 for node in schema:
62 63 if node.name not in defaults:
63 64 defaults[node.name] = self.plugin.get_setting_by_name(
64 65 node.name, node.default, cache=False)
65 66
66 67 template_context = {
67 68 'defaults': defaults,
68 69 'errors': errors,
69 70 'plugin': self.context.plugin,
70 71 'resource': self.context,
71 72 }
72 73
73 74 return self._get_template_context(c, **template_context)
74 75
75 76 @LoginRequired()
76 77 @HasPermissionAllDecorator('hg.admin')
77 78 @CSRFRequired()
78 79 def settings_post(self):
79 80 """
80 81 View that validates and stores the plugin settings.
81 82 """
82 83 _ = self.request.translate
83 84 self.load_default_context()
84 85 schema = self.plugin.get_settings_schema()
85 86 data = self.request.params
86 87
87 88 try:
88 89 valid_data = schema.deserialize(data)
89 90 except colander.Invalid as e:
90 91 # Display error message and display form again.
91 self.request.session.flash(
92 h.flash(
92 93 _('Errors exist when saving plugin settings. '
93 94 'Please check the form inputs.'),
94 queue='error')
95 category='error')
95 96 defaults = {key: data[key] for key in data if key in schema}
96 97 return self.settings_get(errors=e.asdict(), defaults=defaults)
97 98
98 99 # Store validated data.
99 100 for name, value in valid_data.items():
100 101 self.plugin.create_or_update_setting(name, value)
101 102 Session().commit()
102 103
103 104 # Display success message and redirect.
104 self.request.session.flash(
105 _('Auth settings updated successfully.'),
106 queue='success')
105 h.flash(_('Auth settings updated successfully.'), category='success')
107 106 redirect_to = self.request.resource_path(
108 107 self.context, route_name='auth_home')
109 108 return HTTPFound(redirect_to)
110 109
111 110
112 111 class AuthSettingsView(BaseAppView):
113 112 def load_default_context(self):
114 113 c = self._get_local_tmpl_context()
115 114 return c
116 115
117 116 @LoginRequired()
118 117 @HasPermissionAllDecorator('hg.admin')
119 118 def index(self, defaults=None, errors=None, prefix_error=False):
120 119 c = self.load_default_context()
121 120
122 121 defaults = defaults or {}
123 122 authn_registry = get_authn_registry(self.request.registry)
124 123 enabled_plugins = SettingsModel().get_auth_plugins()
125 124
126 125 # Create template context and render it.
127 126 template_context = {
128 127 'resource': self.context,
129 128 'available_plugins': authn_registry.get_plugins(),
130 129 'enabled_plugins': enabled_plugins,
131 130 }
132 131 html = render('rhodecode:templates/admin/auth/auth_settings.mako',
133 132 self._get_template_context(c, **template_context),
134 133 self.request)
135 134
136 135 # Create form default values and fill the form.
137 136 form_defaults = {
138 137 'auth_plugins': ','.join(enabled_plugins)
139 138 }
140 139 form_defaults.update(defaults)
141 140 html = formencode.htmlfill.render(
142 141 html,
143 142 defaults=form_defaults,
144 143 errors=errors,
145 144 prefix_error=prefix_error,
146 145 encoding="UTF-8",
147 146 force_defaults=False)
148 147
149 148 return Response(html)
150 149
151 150 @LoginRequired()
152 151 @HasPermissionAllDecorator('hg.admin')
153 152 @CSRFRequired()
154 153 def auth_settings(self):
155 154 _ = self.request.translate
156 155 try:
157 156 form = AuthSettingsForm(self.request.translate)()
158 157 form_result = form.to_python(self.request.POST)
159 158 plugins = ','.join(form_result['auth_plugins'])
160 159 setting = SettingsModel().create_or_update_setting(
161 160 'auth_plugins', plugins)
162 161 Session().add(setting)
163 162 Session().commit()
164 163
165 164 cache_manager = get_auth_cache_manager()
166 165 cache_manager.clear()
167 166
168 167 cache_manager = get_perms_cache_manager()
169 168 cache_manager.clear()
170 169
171 self.request.session.flash(
172 _('Auth settings updated successfully.'),
173 queue='success')
170 h.flash(_('Auth settings updated successfully.'), category='success')
174 171 except formencode.Invalid as errors:
175 172 e = errors.error_dict or {}
176 self.request.session.flash(
177 _('Errors exist when saving plugin setting. '
178 'Please check the form inputs.'),
179 queue='error')
173 h.flash(_('Errors exist when saving plugin setting. '
174 'Please check the form inputs.'), category='error')
180 175 return self.index(
181 176 defaults=errors.value,
182 177 errors=e,
183 178 prefix_error=False)
184 179 except Exception:
185 180 log.exception('Exception in auth_settings')
186 self.request.session.flash(
187 _('Error occurred during update of auth settings.'),
188 queue='error')
181 h.flash(_('Error occurred during update of auth settings.'),
182 category='error')
189 183
190 184 redirect_to = self.request.resource_path(
191 185 self.context, route_name='auth_home')
192 186 return HTTPFound(redirect_to)
@@ -1,459 +1,460 b''
1 1 # -*- coding: utf-8 -*-
2 2
3 3 # Copyright (C) 2012-2017 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 deform
22 22 import logging
23 23 import peppercorn
24 24 import webhelpers.paginate
25 25
26 26 from pyramid.httpexceptions import HTTPFound, HTTPForbidden, HTTPNotFound
27 27
28 28 from rhodecode.apps._base import BaseAppView
29 29 from rhodecode.integrations import integration_type_registry
30 30 from rhodecode.apps.admin.navigation import navigation_list
31 31 from rhodecode.lib.auth import (
32 32 LoginRequired, CSRFRequired, HasPermissionAnyDecorator,
33 33 HasRepoPermissionAnyDecorator, HasRepoGroupPermissionAnyDecorator)
34 34 from rhodecode.lib.utils2 import safe_int
35 from rhodecode.lib.helpers import Page
35 from rhodecode.lib import helpers as h
36 36 from rhodecode.model.db import Repository, RepoGroup, Session, Integration
37 37 from rhodecode.model.scm import ScmModel
38 38 from rhodecode.model.integration import IntegrationModel
39 39 from rhodecode.model.validation_schema.schemas.integration_schema import (
40 40 make_integration_schema, IntegrationScopeType)
41 41
42 42 log = logging.getLogger(__name__)
43 43
44 44
45 45 class IntegrationSettingsViewBase(BaseAppView):
46 46 """
47 47 Base Integration settings view used by both repo / global settings
48 48 """
49 49
50 50 def __init__(self, context, request):
51 51 super(IntegrationSettingsViewBase, self).__init__(context, request)
52 52 self._load_view_context()
53 53
54 54 def _load_view_context(self):
55 55 """
56 56 This avoids boilerplate for repo/global+list/edit+views/templates
57 57 by doing all possible contexts at the same time however it should
58 58 be split up into separate functions once more "contexts" exist
59 59 """
60 60
61 61 self.IntegrationType = None
62 62 self.repo = None
63 63 self.repo_group = None
64 64 self.integration = None
65 65 self.integrations = {}
66 66
67 67 request = self.request
68 68
69 69 if 'repo_name' in request.matchdict: # in repo settings context
70 70 repo_name = request.matchdict['repo_name']
71 71 self.repo = Repository.get_by_repo_name(repo_name)
72 72
73 73 if 'repo_group_name' in request.matchdict: # in group settings context
74 74 repo_group_name = request.matchdict['repo_group_name']
75 75 self.repo_group = RepoGroup.get_by_group_name(repo_group_name)
76 76
77 77 if 'integration' in request.matchdict: # integration type context
78 78 integration_type = request.matchdict['integration']
79 79 if integration_type not in integration_type_registry:
80 80 raise HTTPNotFound()
81 81
82 82 self.IntegrationType = integration_type_registry[integration_type]
83 83 if self.IntegrationType.is_dummy:
84 84 raise HTTPNotFound()
85 85
86 86 if 'integration_id' in request.matchdict: # single integration context
87 87 integration_id = request.matchdict['integration_id']
88 88 self.integration = Integration.get(integration_id)
89 89
90 90 # extra perms check just in case
91 91 if not self._has_perms_for_integration(self.integration):
92 92 raise HTTPForbidden()
93 93
94 94 self.settings = self.integration and self.integration.settings or {}
95 95 self.admin_view = not (self.repo or self.repo_group)
96 96
97 97 def _has_perms_for_integration(self, integration):
98 98 perms = self.request.user.permissions
99 99
100 100 if 'hg.admin' in perms['global']:
101 101 return True
102 102
103 103 if integration.repo:
104 104 return perms['repositories'].get(
105 105 integration.repo.repo_name) == 'repository.admin'
106 106
107 107 if integration.repo_group:
108 108 return perms['repositories_groups'].get(
109 109 integration.repo_group.group_name) == 'group.admin'
110 110
111 111 return False
112 112
113 113 def _get_local_tmpl_context(self, include_app_defaults=True):
114 114 _ = self.request.translate
115 115 c = super(IntegrationSettingsViewBase, self)._get_local_tmpl_context(
116 116 include_app_defaults=include_app_defaults)
117 117
118 118 c.active = 'integrations'
119 119
120 120 return c
121 121
122 122 def _form_schema(self):
123 123 schema = make_integration_schema(IntegrationType=self.IntegrationType,
124 124 settings=self.settings)
125 125
126 126 # returns a clone, important if mutating the schema later
127 127 return schema.bind(
128 128 permissions=self.request.user.permissions,
129 129 no_scope=not self.admin_view)
130 130
131 131 def _form_defaults(self):
132 132 _ = self.request.translate
133 133 defaults = {}
134 134
135 135 if self.integration:
136 136 defaults['settings'] = self.integration.settings or {}
137 137 defaults['options'] = {
138 138 'name': self.integration.name,
139 139 'enabled': self.integration.enabled,
140 140 'scope': {
141 141 'repo': self.integration.repo,
142 142 'repo_group': self.integration.repo_group,
143 143 'child_repos_only': self.integration.child_repos_only,
144 144 },
145 145 }
146 146 else:
147 147 if self.repo:
148 148 scope = _('{repo_name} repository').format(
149 149 repo_name=self.repo.repo_name)
150 150 elif self.repo_group:
151 151 scope = _('{repo_group_name} repo group').format(
152 152 repo_group_name=self.repo_group.group_name)
153 153 else:
154 154 scope = _('Global')
155 155
156 156 defaults['options'] = {
157 157 'enabled': True,
158 158 'name': _('{name} integration').format(
159 159 name=self.IntegrationType.display_name),
160 160 }
161 161 defaults['options']['scope'] = {
162 162 'repo': self.repo,
163 163 'repo_group': self.repo_group,
164 164 }
165 165
166 166 return defaults
167 167
168 168 def _delete_integration(self, integration):
169 169 _ = self.request.translate
170 170 Session().delete(integration)
171 171 Session().commit()
172 self.request.session.flash(
172 h.flash(
173 173 _('Integration {integration_name} deleted successfully.').format(
174 174 integration_name=integration.name),
175 queue='success')
175 category='success')
176 176
177 177 if self.repo:
178 178 redirect_to = self.request.route_path(
179 179 'repo_integrations_home', repo_name=self.repo.repo_name)
180 180 elif self.repo_group:
181 181 redirect_to = self.request.route_path(
182 182 'repo_group_integrations_home',
183 183 repo_group_name=self.repo_group.group_name)
184 184 else:
185 185 redirect_to = self.request.route_path('global_integrations_home')
186 186 raise HTTPFound(redirect_to)
187 187
188 188 def _integration_list(self):
189 189 """ List integrations """
190 190
191 191 c = self.load_default_context()
192 192 if self.repo:
193 193 scope = self.repo
194 194 elif self.repo_group:
195 195 scope = self.repo_group
196 196 else:
197 197 scope = 'all'
198 198
199 199 integrations = []
200 200
201 201 for IntType, integration in IntegrationModel().get_integrations(
202 202 scope=scope, IntegrationType=self.IntegrationType):
203 203
204 204 # extra permissions check *just in case*
205 205 if not self._has_perms_for_integration(integration):
206 206 continue
207 207
208 208 integrations.append((IntType, integration))
209 209
210 210 sort_arg = self.request.GET.get('sort', 'name:asc')
211 sort_dir = 'asc'
211 212 if ':' in sort_arg:
212 213 sort_field, sort_dir = sort_arg.split(':')
213 214 else:
214 215 sort_field = sort_arg, 'asc'
215 216
216 217 assert sort_field in ('name', 'integration_type', 'enabled', 'scope')
217 218
218 219 integrations.sort(
219 220 key=lambda x: getattr(x[1], sort_field),
220 221 reverse=(sort_dir == 'desc'))
221 222
222 223 page_url = webhelpers.paginate.PageURL(
223 224 self.request.path, self.request.GET)
224 225 page = safe_int(self.request.GET.get('page', 1), 1)
225 226
226 integrations = Page(
227 integrations = h.Page(
227 228 integrations, page=page, items_per_page=10, url=page_url)
228 229
229 230 c.rev_sort_dir = sort_dir != 'desc' and 'desc' or 'asc'
230 231
231 232 c.current_IntegrationType = self.IntegrationType
232 233 c.integrations_list = integrations
233 234 c.available_integrations = integration_type_registry
234 235
235 236 return self._get_template_context(c)
236 237
237 238 def _settings_get(self, defaults=None, form=None):
238 239 """
239 240 View that displays the integration settings as a form.
240 241 """
241 242 c = self.load_default_context()
242 243
243 244 defaults = defaults or self._form_defaults()
244 245 schema = self._form_schema()
245 246
246 247 if self.integration:
247 248 buttons = ('submit', 'delete')
248 249 else:
249 250 buttons = ('submit',)
250 251
251 252 form = form or deform.Form(schema, appstruct=defaults, buttons=buttons)
252 253
253 254 c.form = form
254 255 c.current_IntegrationType = self.IntegrationType
255 256 c.integration = self.integration
256 257
257 258 return self._get_template_context(c)
258 259
259 260 def _settings_post(self):
260 261 """
261 262 View that validates and stores the integration settings.
262 263 """
263 264 _ = self.request.translate
264 265
265 266 controls = self.request.POST.items()
266 267 pstruct = peppercorn.parse(controls)
267 268
268 269 if self.integration and pstruct.get('delete'):
269 270 return self._delete_integration(self.integration)
270 271
271 272 schema = self._form_schema()
272 273
273 274 skip_settings_validation = False
274 275 if self.integration and 'enabled' not in pstruct.get('options', {}):
275 276 skip_settings_validation = True
276 277 schema['settings'].validator = None
277 278 for field in schema['settings'].children:
278 279 field.validator = None
279 280 field.missing = ''
280 281
281 282 if self.integration:
282 283 buttons = ('submit', 'delete')
283 284 else:
284 285 buttons = ('submit',)
285 286
286 287 form = deform.Form(schema, buttons=buttons)
287 288
288 289 if not self.admin_view:
289 290 # scope is read only field in these cases, and has to be added
290 291 options = pstruct.setdefault('options', {})
291 292 if 'scope' not in options:
292 293 options['scope'] = IntegrationScopeType().serialize(None, {
293 294 'repo': self.repo,
294 295 'repo_group': self.repo_group,
295 296 })
296 297
297 298 try:
298 299 valid_data = form.validate_pstruct(pstruct)
299 300 except deform.ValidationFailure as e:
300 self.request.session.flash(
301 h.flash(
301 302 _('Errors exist when saving integration settings. '
302 303 'Please check the form inputs.'),
303 queue='error')
304 category='error')
304 305 return self._settings_get(form=e)
305 306
306 307 if not self.integration:
307 308 self.integration = Integration()
308 309 self.integration.integration_type = self.IntegrationType.key
309 310 Session().add(self.integration)
310 311
311 312 scope = valid_data['options']['scope']
312 313
313 314 IntegrationModel().update_integration(self.integration,
314 315 name=valid_data['options']['name'],
315 316 enabled=valid_data['options']['enabled'],
316 317 settings=valid_data['settings'],
317 318 repo=scope['repo'],
318 319 repo_group=scope['repo_group'],
319 320 child_repos_only=scope['child_repos_only'],
320 321 )
321 322
322 323 self.integration.settings = valid_data['settings']
323 324 Session().commit()
324 325 # Display success message and redirect.
325 self.request.session.flash(
326 h.flash(
326 327 _('Integration {integration_name} updated successfully.').format(
327 328 integration_name=self.IntegrationType.display_name),
328 queue='success')
329 category='success')
329 330
330 331 # if integration scope changes, we must redirect to the right place
331 332 # keeping in mind if the original view was for /repo/ or /_admin/
332 333 admin_view = not (self.repo or self.repo_group)
333 334
334 335 if self.integration.repo and not admin_view:
335 336 redirect_to = self.request.route_path(
336 337 'repo_integrations_edit',
337 338 repo_name=self.integration.repo.repo_name,
338 339 integration=self.integration.integration_type,
339 340 integration_id=self.integration.integration_id)
340 341 elif self.integration.repo_group and not admin_view:
341 342 redirect_to = self.request.route_path(
342 343 'repo_group_integrations_edit',
343 344 repo_group_name=self.integration.repo_group.group_name,
344 345 integration=self.integration.integration_type,
345 346 integration_id=self.integration.integration_id)
346 347 else:
347 348 redirect_to = self.request.route_path(
348 349 'global_integrations_edit',
349 350 integration=self.integration.integration_type,
350 351 integration_id=self.integration.integration_id)
351 352
352 353 return HTTPFound(redirect_to)
353 354
354 355 def _new_integration(self):
355 356 c = self.load_default_context()
356 357 c.available_integrations = integration_type_registry
357 358 return self._get_template_context(c)
358 359
359 360 def load_default_context(self):
360 361 raise NotImplementedError()
361 362
362 363
363 364 class GlobalIntegrationsView(IntegrationSettingsViewBase):
364 365 def load_default_context(self):
365 366 c = self._get_local_tmpl_context()
366 367 c.repo = self.repo
367 368 c.repo_group = self.repo_group
368 369 c.navlist = navigation_list(self.request)
369 370
370 371 return c
371 372
372 373 @LoginRequired()
373 374 @HasPermissionAnyDecorator('hg.admin')
374 375 def integration_list(self):
375 376 return self._integration_list()
376 377
377 378 @LoginRequired()
378 379 @HasPermissionAnyDecorator('hg.admin')
379 380 def settings_get(self):
380 381 return self._settings_get()
381 382
382 383 @LoginRequired()
383 384 @HasPermissionAnyDecorator('hg.admin')
384 385 @CSRFRequired()
385 386 def settings_post(self):
386 387 return self._settings_post()
387 388
388 389 @LoginRequired()
389 390 @HasPermissionAnyDecorator('hg.admin')
390 391 def new_integration(self):
391 392 return self._new_integration()
392 393
393 394
394 395 class RepoIntegrationsView(IntegrationSettingsViewBase):
395 396 def load_default_context(self):
396 397 c = self._get_local_tmpl_context()
397 398
398 399 c.repo = self.repo
399 400 c.repo_group = self.repo_group
400 401
401 402 self.db_repo = self.repo
402 403 c.rhodecode_db_repo = self.repo
403 404 c.repo_name = self.db_repo.repo_name
404 405 c.repository_pull_requests = ScmModel().get_pull_requests(self.repo)
405 406
406 407
407 408 return c
408 409
409 410 @LoginRequired()
410 411 @HasRepoPermissionAnyDecorator('repository.admin')
411 412 def integration_list(self):
412 413 return self._integration_list()
413 414
414 415 @LoginRequired()
415 416 @HasRepoPermissionAnyDecorator('repository.admin')
416 417 def settings_get(self):
417 418 return self._settings_get()
418 419
419 420 @LoginRequired()
420 421 @HasRepoPermissionAnyDecorator('repository.admin')
421 422 @CSRFRequired()
422 423 def settings_post(self):
423 424 return self._settings_post()
424 425
425 426 @LoginRequired()
426 427 @HasRepoPermissionAnyDecorator('repository.admin')
427 428 def new_integration(self):
428 429 return self._new_integration()
429 430
430 431
431 432 class RepoGroupIntegrationsView(IntegrationSettingsViewBase):
432 433 def load_default_context(self):
433 434 c = self._get_local_tmpl_context()
434 435 c.repo = self.repo
435 436 c.repo_group = self.repo_group
436 437 c.navlist = navigation_list(self.request)
437 438
438 439 return c
439 440
440 441 @LoginRequired()
441 442 @HasRepoGroupPermissionAnyDecorator('group.admin')
442 443 def integration_list(self):
443 444 return self._integration_list()
444 445
445 446 @LoginRequired()
446 447 @HasRepoGroupPermissionAnyDecorator('group.admin')
447 448 def settings_get(self):
448 449 return self._settings_get()
449 450
450 451 @LoginRequired()
451 452 @HasRepoGroupPermissionAnyDecorator('group.admin')
452 453 @CSRFRequired()
453 454 def settings_post(self):
454 455 return self._settings_post()
455 456
456 457 @LoginRequired()
457 458 @HasRepoGroupPermissionAnyDecorator('group.admin')
458 459 def new_integration(self):
459 460 return self._new_integration()
General Comments 0
You need to be logged in to leave comments. Login now