Show More
@@ -1,225 +1,223 b'' | |||||
1 | # -*- coding: utf-8 -*- |
|
1 | # -*- coding: utf-8 -*- | |
2 |
|
2 | |||
3 | # Copyright (C) 2016-2018 RhodeCode GmbH |
|
3 | # Copyright (C) 2016-2018 RhodeCode GmbH | |
4 | # |
|
4 | # | |
5 | # This program is free software: you can redistribute it and/or modify |
|
5 | # This program is free software: you can redistribute it and/or modify | |
6 | # it under the terms of the GNU Affero General Public License, version 3 |
|
6 | # it under the terms of the GNU Affero General Public License, version 3 | |
7 | # (only), as published by the Free Software Foundation. |
|
7 | # (only), as published by the Free Software Foundation. | |
8 | # |
|
8 | # | |
9 | # This program is distributed in the hope that it will be useful, |
|
9 | # This program is distributed in the hope that it will be useful, | |
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
10 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
11 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
12 | # GNU General Public License for more details. |
|
12 | # GNU General Public License for more details. | |
13 | # |
|
13 | # | |
14 | # You should have received a copy of the GNU Affero General Public License |
|
14 | # You should have received a copy of the GNU Affero General Public License | |
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
15 | # along with this program. If not, see <http://www.gnu.org/licenses/>. | |
16 | # |
|
16 | # | |
17 | # This program is dual-licensed. If you wish to learn more about the |
|
17 | # This program is dual-licensed. If you wish to learn more about the | |
18 | # RhodeCode Enterprise Edition, including its added features, Support services, |
|
18 | # RhodeCode Enterprise Edition, including its added features, Support services, | |
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ |
|
19 | # and proprietary license terms, please see https://rhodecode.com/licenses/ | |
20 |
|
20 | |||
21 | import os |
|
21 | import os | |
22 |
|
22 | |||
23 | import deform |
|
23 | import deform | |
24 | import colander |
|
24 | import colander | |
25 |
|
25 | |||
26 | from rhodecode.translation import _ |
|
26 | from rhodecode.translation import _ | |
27 | from rhodecode.model.db import Repository, RepoGroup |
|
27 | from rhodecode.model.db import Repository, RepoGroup | |
28 | from rhodecode.model.validation_schema import validators, preparers |
|
28 | from rhodecode.model.validation_schema import validators, preparers | |
29 |
|
29 | |||
30 |
|
30 | |||
31 | def integration_scope_choices(permissions): |
|
31 | def integration_scope_choices(permissions): | |
32 | """ |
|
32 | """ | |
33 | Return list of (value, label) choices for integration scopes depending on |
|
33 | Return list of (value, label) choices for integration scopes depending on | |
34 | the permissions |
|
34 | the permissions | |
35 | """ |
|
35 | """ | |
36 | result = [('', _('Pick a scope:'))] |
|
36 | result = [('', _('Pick a scope:'))] | |
37 | if 'hg.admin' in permissions['global']: |
|
37 | if 'hg.admin' in permissions['global']: | |
38 | result.extend([ |
|
38 | result.extend([ | |
39 | ('global', _('Global (all repositories)')), |
|
39 | ('global', _('Global (all repositories)')), | |
40 | ('root-repos', _('Top level repositories only')), |
|
40 | ('root-repos', _('Top level repositories only')), | |
41 | ]) |
|
41 | ]) | |
42 |
|
42 | |||
43 | repo_choices = [ |
|
43 | repo_choices = [ | |
44 | ('repo:%s' % repo_name, '/' + repo_name) |
|
44 | ('repo:%s' % repo_name, '/' + repo_name) | |
45 | for repo_name, repo_perm |
|
45 | for repo_name, repo_perm | |
46 | in permissions['repositories'].items() |
|
46 | in permissions['repositories'].items() | |
47 | if repo_perm == 'repository.admin' |
|
47 | if repo_perm == 'repository.admin' | |
48 | ] |
|
48 | ] | |
49 | repogroup_choices = [ |
|
49 | repogroup_choices = [ | |
50 | ('repogroup:%s' % repo_group_name, '/' + repo_group_name + '/ (child repos only)') |
|
50 | ('repogroup:%s' % repo_group_name, '/' + repo_group_name + '/ (child repos only)') | |
51 | for repo_group_name, repo_group_perm |
|
51 | for repo_group_name, repo_group_perm | |
52 | in permissions['repositories_groups'].items() |
|
52 | in permissions['repositories_groups'].items() | |
53 | if repo_group_perm == 'group.admin' |
|
53 | if repo_group_perm == 'group.admin' | |
54 | ] |
|
54 | ] | |
55 | repogroup_recursive_choices = [ |
|
55 | repogroup_recursive_choices = [ | |
56 | ('repogroup-recursive:%s' % repo_group_name, '/' + repo_group_name + '/ (recursive)') |
|
56 | ('repogroup-recursive:%s' % repo_group_name, '/' + repo_group_name + '/ (recursive)') | |
57 | for repo_group_name, repo_group_perm |
|
57 | for repo_group_name, repo_group_perm | |
58 | in permissions['repositories_groups'].items() |
|
58 | in permissions['repositories_groups'].items() | |
59 | if repo_group_perm == 'group.admin' |
|
59 | if repo_group_perm == 'group.admin' | |
60 | ] |
|
60 | ] | |
61 | result.extend( |
|
61 | result.extend( | |
62 | sorted(repogroup_recursive_choices + repogroup_choices + repo_choices, |
|
62 | sorted(repogroup_recursive_choices + repogroup_choices + repo_choices, | |
63 | key=lambda (choice, label): choice.split(':', 1)[1] |
|
63 | key=lambda (choice, label): choice.split(':', 1)[1] | |
64 | ) |
|
64 | ) | |
65 | ) |
|
65 | ) | |
66 | return result |
|
66 | return result | |
67 |
|
67 | |||
68 |
|
68 | |||
69 | @colander.deferred |
|
69 | @colander.deferred | |
70 | def deferred_integration_scopes_validator(node, kw): |
|
70 | def deferred_integration_scopes_validator(node, kw): | |
71 | perms = kw.get('permissions') |
|
71 | perms = kw.get('permissions') | |
72 | def _scope_validator(_node, scope): |
|
72 | def _scope_validator(_node, scope): | |
73 | is_super_admin = 'hg.admin' in perms['global'] |
|
73 | is_super_admin = 'hg.admin' in perms['global'] | |
74 |
|
74 | |||
75 | if scope.get('repo'): |
|
75 | if scope.get('repo'): | |
76 | if (is_super_admin or perms['repositories'].get( |
|
76 | if (is_super_admin or perms['repositories'].get( | |
77 | scope['repo'].repo_name) == 'repository.admin'): |
|
77 | scope['repo'].repo_name) == 'repository.admin'): | |
78 | return True |
|
78 | return True | |
79 | msg = _('Only repo admins can create integrations') |
|
79 | msg = _('Only repo admins can create integrations') | |
80 | raise colander.Invalid(_node, msg) |
|
80 | raise colander.Invalid(_node, msg) | |
81 | elif scope.get('repo_group'): |
|
81 | elif scope.get('repo_group'): | |
82 | if (is_super_admin or perms['repositories_groups'].get( |
|
82 | if (is_super_admin or perms['repositories_groups'].get( | |
83 | scope['repo_group'].group_name) == 'group.admin'): |
|
83 | scope['repo_group'].group_name) == 'group.admin'): | |
84 | return True |
|
84 | return True | |
85 |
|
85 | |||
86 | msg = _('Only repogroup admins can create integrations') |
|
86 | msg = _('Only repogroup admins can create integrations') | |
87 | raise colander.Invalid(_node, msg) |
|
87 | raise colander.Invalid(_node, msg) | |
88 | else: |
|
88 | else: | |
89 | if is_super_admin: |
|
89 | if is_super_admin: | |
90 | return True |
|
90 | return True | |
91 | msg = _('Only superadmins can create global integrations') |
|
91 | msg = _('Only superadmins can create global integrations') | |
92 | raise colander.Invalid(_node, msg) |
|
92 | raise colander.Invalid(_node, msg) | |
93 |
|
93 | |||
94 | return _scope_validator |
|
94 | return _scope_validator | |
95 |
|
95 | |||
96 |
|
96 | |||
97 | @colander.deferred |
|
97 | @colander.deferred | |
98 | def deferred_integration_scopes_widget(node, kw): |
|
98 | def deferred_integration_scopes_widget(node, kw): | |
99 | if kw.get('no_scope'): |
|
99 | if kw.get('no_scope'): | |
100 | return deform.widget.TextInputWidget(readonly=True) |
|
100 | return deform.widget.TextInputWidget(readonly=True) | |
101 |
|
101 | |||
102 | choices = integration_scope_choices(kw.get('permissions')) |
|
102 | choices = integration_scope_choices(kw.get('permissions')) | |
103 | widget = deform.widget.Select2Widget(values=choices) |
|
103 | widget = deform.widget.Select2Widget(values=choices) | |
104 | return widget |
|
104 | return widget | |
105 |
|
105 | |||
106 |
|
106 | |||
107 | class IntegrationScopeType(colander.SchemaType): |
|
107 | class IntegrationScopeType(colander.SchemaType): | |
108 | def serialize(self, node, appstruct): |
|
108 | def serialize(self, node, appstruct): | |
109 | if appstruct is colander.null: |
|
109 | if appstruct is colander.null: | |
110 | return colander.null |
|
110 | return colander.null | |
111 |
|
111 | |||
112 | if appstruct.get('repo'): |
|
112 | if appstruct.get('repo'): | |
113 | return 'repo:%s' % appstruct['repo'].repo_name |
|
113 | return 'repo:%s' % appstruct['repo'].repo_name | |
114 | elif appstruct.get('repo_group'): |
|
114 | elif appstruct.get('repo_group'): | |
115 | if appstruct.get('child_repos_only'): |
|
115 | if appstruct.get('child_repos_only'): | |
116 | return 'repogroup:%s' % appstruct['repo_group'].group_name |
|
116 | return 'repogroup:%s' % appstruct['repo_group'].group_name | |
117 | else: |
|
117 | else: | |
118 | return 'repogroup-recursive:%s' % ( |
|
118 | return 'repogroup-recursive:%s' % ( | |
119 | appstruct['repo_group'].group_name) |
|
119 | appstruct['repo_group'].group_name) | |
120 | else: |
|
120 | else: | |
121 | if appstruct.get('child_repos_only'): |
|
121 | if appstruct.get('child_repos_only'): | |
122 | return 'root-repos' |
|
122 | return 'root-repos' | |
123 | else: |
|
123 | else: | |
124 | return 'global' |
|
124 | return 'global' | |
125 |
|
125 | |||
126 | raise colander.Invalid(node, '%r is not a valid scope' % appstruct) |
|
|||
127 |
|
||||
128 | def deserialize(self, node, cstruct): |
|
126 | def deserialize(self, node, cstruct): | |
129 | if cstruct is colander.null: |
|
127 | if cstruct is colander.null: | |
130 | return colander.null |
|
128 | return colander.null | |
131 |
|
129 | |||
132 | if cstruct.startswith('repo:'): |
|
130 | if cstruct.startswith('repo:'): | |
133 | repo = Repository.get_by_repo_name(cstruct.split(':')[1]) |
|
131 | repo = Repository.get_by_repo_name(cstruct.split(':')[1]) | |
134 | if repo: |
|
132 | if repo: | |
135 | return { |
|
133 | return { | |
136 | 'repo': repo, |
|
134 | 'repo': repo, | |
137 | 'repo_group': None, |
|
135 | 'repo_group': None, | |
138 | 'child_repos_only': False, |
|
136 | 'child_repos_only': False, | |
139 | } |
|
137 | } | |
140 | elif cstruct.startswith('repogroup-recursive:'): |
|
138 | elif cstruct.startswith('repogroup-recursive:'): | |
141 | repo_group = RepoGroup.get_by_group_name(cstruct.split(':')[1]) |
|
139 | repo_group = RepoGroup.get_by_group_name(cstruct.split(':')[1]) | |
142 | if repo_group: |
|
140 | if repo_group: | |
143 | return { |
|
141 | return { | |
144 | 'repo': None, |
|
142 | 'repo': None, | |
145 | 'repo_group': repo_group, |
|
143 | 'repo_group': repo_group, | |
146 | 'child_repos_only': False |
|
144 | 'child_repos_only': False | |
147 | } |
|
145 | } | |
148 | elif cstruct.startswith('repogroup:'): |
|
146 | elif cstruct.startswith('repogroup:'): | |
149 | repo_group = RepoGroup.get_by_group_name(cstruct.split(':')[1]) |
|
147 | repo_group = RepoGroup.get_by_group_name(cstruct.split(':')[1]) | |
150 | if repo_group: |
|
148 | if repo_group: | |
151 | return { |
|
149 | return { | |
152 | 'repo': None, |
|
150 | 'repo': None, | |
153 | 'repo_group': repo_group, |
|
151 | 'repo_group': repo_group, | |
154 | 'child_repos_only': True |
|
152 | 'child_repos_only': True | |
155 | } |
|
153 | } | |
156 | elif cstruct == 'global': |
|
154 | elif cstruct == 'global': | |
157 | return { |
|
155 | return { | |
158 | 'repo': None, |
|
156 | 'repo': None, | |
159 | 'repo_group': None, |
|
157 | 'repo_group': None, | |
160 | 'child_repos_only': False |
|
158 | 'child_repos_only': False | |
161 | } |
|
159 | } | |
162 | elif cstruct == 'root-repos': |
|
160 | elif cstruct == 'root-repos': | |
163 | return { |
|
161 | return { | |
164 | 'repo': None, |
|
162 | 'repo': None, | |
165 | 'repo_group': None, |
|
163 | 'repo_group': None, | |
166 | 'child_repos_only': True |
|
164 | 'child_repos_only': True | |
167 | } |
|
165 | } | |
168 |
|
166 | |||
169 | raise colander.Invalid(node, '%r is not a valid scope' % cstruct) |
|
167 | raise colander.Invalid(node, '%r is not a valid scope' % cstruct) | |
170 |
|
168 | |||
171 |
|
169 | |||
172 | class IntegrationOptionsSchemaBase(colander.MappingSchema): |
|
170 | class IntegrationOptionsSchemaBase(colander.MappingSchema): | |
173 |
|
171 | |||
174 | name = colander.SchemaNode( |
|
172 | name = colander.SchemaNode( | |
175 | colander.String(), |
|
173 | colander.String(), | |
176 | description=_('Short name for this integration.'), |
|
174 | description=_('Short name for this integration.'), | |
177 | missing=colander.required, |
|
175 | missing=colander.required, | |
178 | title=_('Integration name'), |
|
176 | title=_('Integration name'), | |
179 | ) |
|
177 | ) | |
180 |
|
178 | |||
181 | scope = colander.SchemaNode( |
|
179 | scope = colander.SchemaNode( | |
182 | IntegrationScopeType(), |
|
180 | IntegrationScopeType(), | |
183 | description=_( |
|
181 | description=_( | |
184 | 'Scope of the integration. Recursive means the integration ' |
|
182 | 'Scope of the integration. Recursive means the integration ' | |
185 | ' runs on all repos of that group and children recursively.'), |
|
183 | ' runs on all repos of that group and children recursively.'), | |
186 | title=_('Integration scope'), |
|
184 | title=_('Integration scope'), | |
187 | validator=deferred_integration_scopes_validator, |
|
185 | validator=deferred_integration_scopes_validator, | |
188 | widget=deferred_integration_scopes_widget, |
|
186 | widget=deferred_integration_scopes_widget, | |
189 | missing=colander.required, |
|
187 | missing=colander.required, | |
190 | ) |
|
188 | ) | |
191 |
|
189 | |||
192 | enabled = colander.SchemaNode( |
|
190 | enabled = colander.SchemaNode( | |
193 | colander.Bool(), |
|
191 | colander.Bool(), | |
194 | default=True, |
|
192 | default=True, | |
195 | description=_('Enable or disable this integration.'), |
|
193 | description=_('Enable or disable this integration.'), | |
196 | missing=False, |
|
194 | missing=False, | |
197 | title=_('Enabled'), |
|
195 | title=_('Enabled'), | |
198 | ) |
|
196 | ) | |
199 |
|
197 | |||
200 |
|
198 | |||
201 | def make_integration_schema(IntegrationType, settings=None): |
|
199 | def make_integration_schema(IntegrationType, settings=None): | |
202 | """ |
|
200 | """ | |
203 | Return a colander schema for an integration type |
|
201 | Return a colander schema for an integration type | |
204 |
|
202 | |||
205 | :param IntegrationType: the integration type class |
|
203 | :param IntegrationType: the integration type class | |
206 | :param settings: existing integration settings dict (optional) |
|
204 | :param settings: existing integration settings dict (optional) | |
207 | """ |
|
205 | """ | |
208 |
|
206 | |||
209 | settings = settings or {} |
|
207 | settings = settings or {} | |
210 | settings_schema = IntegrationType(settings=settings).settings_schema() |
|
208 | settings_schema = IntegrationType(settings=settings).settings_schema() | |
211 |
|
209 | |||
212 | class IntegrationSchema(colander.Schema): |
|
210 | class IntegrationSchema(colander.Schema): | |
213 | options = IntegrationOptionsSchemaBase() |
|
211 | options = IntegrationOptionsSchemaBase() | |
214 |
|
212 | |||
215 | schema = IntegrationSchema() |
|
213 | schema = IntegrationSchema() | |
216 | schema['options'].title = _('General integration options') |
|
214 | schema['options'].title = _('General integration options') | |
217 |
|
215 | |||
218 | settings_schema.name = 'settings' |
|
216 | settings_schema.name = 'settings' | |
219 | settings_schema.title = _('{integration_type} settings').format( |
|
217 | settings_schema.title = _('{integration_type} settings').format( | |
220 | integration_type=IntegrationType.display_name) |
|
218 | integration_type=IntegrationType.display_name) | |
221 | schema.add(settings_schema) |
|
219 | schema.add(settings_schema) | |
222 |
|
220 | |||
223 | return schema |
|
221 | return schema | |
224 |
|
222 | |||
225 |
|
223 |
General Comments 0
You need to be logged in to leave comments.
Login now