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