Browse Source

Merge "Add a policy to control the right to publish resources"

tags/7.0.0.0b3
Zuul 11 months ago
parent
commit
08a61b6f10

+ 4
- 0
mistral/api/controllers/v2/action.py View File

@@ -83,6 +83,8 @@ class ActionsController(rest.RestController, hooks.HookController):
83 83
 
84 84
         scope = pecan.request.GET.get('scope', 'private')
85 85
         resources.Action.validate_scope(scope)
86
+        if scope == 'public':
87
+            acl.enforce('actions:publicize', context.ctx())
86 88
 
87 89
         @rest_utils.rest_retry_on_db_error
88 90
         def _update_actions():
@@ -116,6 +118,8 @@ class ActionsController(rest.RestController, hooks.HookController):
116 118
         pecan.response.status = 201
117 119
 
118 120
         resources.Action.validate_scope(scope)
121
+        if scope == 'public':
122
+            acl.enforce('actions:publicize', context.ctx())
119 123
 
120 124
         LOG.debug("Create action(s) [definition=%s]", definition)
121 125
 

+ 4
- 0
mistral/api/controllers/v2/workflow.py View File

@@ -117,6 +117,8 @@ class WorkflowsController(rest.RestController, hooks.HookController):
117 117
         scope = pecan.request.GET.get('scope', 'private')
118 118
 
119 119
         resources.Workflow.validate_scope(scope)
120
+        if scope == 'public':
121
+            acl.enforce('workflows:publicize', context.ctx())
120 122
 
121 123
         LOG.debug("Update workflow(s) [definition=%s]", definition)
122 124
 
@@ -153,6 +155,8 @@ class WorkflowsController(rest.RestController, hooks.HookController):
153 155
         pecan.response.status = 201
154 156
 
155 157
         resources.Workflow.validate_scope(scope)
158
+        if scope == 'public':
159
+            acl.enforce('workflows:publicize', context.ctx())
156 160
 
157 161
         LOG.debug("Create workflow(s) [definition=%s]", definition)
158 162
 

+ 15
- 0
mistral/policies/action.py View File

@@ -62,6 +62,21 @@ rules = [
62 62
             }
63 63
         ]
64 64
     ),
65
+    policy.DocumentedRuleDefault(
66
+        name=ACTIONS % 'publicize',
67
+        check_str=base.RULE_ADMIN_OR_OWNER,
68
+        description='Make an action publicly available',
69
+        operations=[
70
+            {
71
+                'path': '/v2/actions',
72
+                'method': 'POST'
73
+            },
74
+            {
75
+                'path': '/v2/actions',
76
+                'method': 'PUT'
77
+            }
78
+        ]
79
+    ),
65 80
     policy.DocumentedRuleDefault(
66 81
         name=ACTIONS % 'update',
67 82
         check_str=base.RULE_ADMIN_OR_OWNER,

+ 15
- 0
mistral/policies/workflow.py View File

@@ -73,6 +73,21 @@ rules = [
73 73
             }
74 74
         ]
75 75
     ),
76
+    policy.DocumentedRuleDefault(
77
+        name=WORKFLOWS % 'publicize',
78
+        check_str=base.RULE_ADMIN_OR_OWNER,
79
+        description='Make a workflow publicly available',
80
+        operations=[
81
+            {
82
+                'path': '/v2/workflows',
83
+                'method': 'POST'
84
+            },
85
+            {
86
+                'path': '/v2/workflows',
87
+                'method': 'PUT'
88
+            }
89
+        ]
90
+    ),
76 91
     policy.DocumentedRuleDefault(
77 92
         name=WORKFLOWS % 'update',
78 93
         check_str=base.RULE_ADMIN_OR_OWNER,

+ 0
- 78
mistral/tests/unit/api/test_policies.py View File

@@ -1,78 +0,0 @@
1
-# Copyright 2016 NEC Corporation. All rights reserved.
2
-#
3
-# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
-# not use this file except in compliance with the License. You may obtain
5
-# a copy of the License at
6
-#
7
-# http://www.apache.org/licenses/LICENSE-2.0
8
-#
9
-# Unless required by applicable law or agreed to in writing, software
10
-# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
-# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
-# License for the specific language governing permissions and limitations
13
-# under the License.
14
-
15
-
16
-import datetime
17
-
18
-import mock
19
-
20
-from mistral.db.v2 import api as db_api
21
-from mistral.db.v2.sqlalchemy import models
22
-from mistral.tests.unit.api import base
23
-from mistral.tests.unit.mstrlfixtures import policy_fixtures
24
-
25
-WF_DEFINITION = """
26
----
27
-version: '2.0'
28
-
29
-flow:
30
-  type: direct
31
-  input:
32
-    - param1
33
-
34
-  tasks:
35
-    task1:
36
-      action: std.echo output="Hi"
37
-"""
38
-
39
-WF_DB = models.WorkflowDefinition(
40
-    id='123e4567-e89b-12d3-a456-426655440000',
41
-    name='flow',
42
-    definition=WF_DEFINITION,
43
-    created_at=datetime.datetime(1970, 1, 1),
44
-    updated_at=datetime.datetime(1970, 1, 1),
45
-    spec={'input': ['param1']}
46
-)
47
-
48
-WF = {
49
-    'id': '123e4567-e89b-12d3-a456-426655440000',
50
-    'name': 'flow',
51
-    'definition': WF_DEFINITION,
52
-    'created_at': '1970-01-01 00:00:00',
53
-    'updated_at': '1970-01-01 00:00:00',
54
-    'input': 'param1'
55
-}
56
-
57
-MOCK_WF = mock.MagicMock(return_value=WF_DB)
58
-
59
-
60
-class TestPolicies(base.APITest):
61
-    @mock.patch.object(db_api, "get_workflow_definition", MOCK_WF)
62
-    def get(self):
63
-        resp = self.app.get('/v2/workflows/123', expect_errors=True)
64
-        return resp.status_int
65
-
66
-    def test_disable_workflow_api(self):
67
-        self.policy = self.useFixture(policy_fixtures.PolicyFixture())
68
-        rules = {"workflows:get": "role:FAKE"}
69
-        self.policy.change_policy_definition(rules)
70
-        response_value = self.get()
71
-        self.assertEqual(403, response_value)
72
-
73
-    def test_enable_workflow_api(self):
74
-        self.policy = self.useFixture(policy_fixtures.PolicyFixture())
75
-        rules = {"workflows:get": "role:FAKE or rule:admin_or_owner"}
76
-        self.policy.change_policy_definition(rules)
77
-        response_value = self.get()
78
-        self.assertEqual(200, response_value)

+ 0
- 0
mistral/tests/unit/policies/__init__.py View File


+ 249
- 0
mistral/tests/unit/policies/test_actions.py View File

@@ -0,0 +1,249 @@
1
+# Copyright 2018 OVH SAS. All rights reserved.
2
+#
3
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
4
+# not use this file except in compliance with the License. You may obtain
5
+# a copy of the License at
6
+#
7
+# http://www.apache.org/licenses/LICENSE-2.0
8
+#
9
+# Unless required by applicable law or agreed to in writing, software
10
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12
+# License for the specific language governing permissions and limitations
13
+# under the License.
14
+
15
+
16
+import mock
17
+
18
+from mistral.db.v2 import api as db_api
19
+from mistral.db.v2.sqlalchemy import models
20
+from mistral.tests.unit.api import base
21
+from mistral.tests.unit.mstrlfixtures import policy_fixtures
22
+
23
+MOCK_DELETE = mock.MagicMock(return_value=None)
24
+
25
+ACTION_DEFINITION = """
26
+---
27
+version: '2.0'
28
+
29
+my_action:
30
+  description: My super cool action.
31
+  tags: ['test', 'v2']
32
+  base: std.echo
33
+  base-input:
34
+    output: "{$.str1}{$.str2}"
35
+"""
36
+ACTION_DB = models.ActionDefinition(
37
+    id='123e4567-e89b-12d3-a456-426655440000',
38
+    name='my_action',
39
+    is_system=False,
40
+    description='My super cool action.',
41
+    tags=['test', 'v2'],
42
+    definition=ACTION_DEFINITION
43
+)
44
+MOCK_ACTION = mock.MagicMock(return_value=ACTION_DB)
45
+
46
+
47
+class TestActionPolicy(base.APITest):
48
+    """Test action related policies
49
+
50
+    Policies to test:
51
+    - actions:create
52
+    - actions:delete
53
+    - actions:get
54
+    - actions:list
55
+    - actions:publicize (on POST & PUT)
56
+    - actions:update
57
+    """
58
+
59
+    def setUp(self):
60
+        self.policy = self.useFixture(policy_fixtures.PolicyFixture())
61
+        super(TestActionPolicy, self).setUp()
62
+
63
+    @mock.patch.object(db_api, "create_action_definition")
64
+    def test_action_create_not_allowed(self, mock_obj):
65
+        self.policy.change_policy_definition(
66
+            {"actions:create": "role:FAKE"}
67
+        )
68
+        resp = self.app.post(
69
+            '/v2/actions',
70
+            ACTION_DEFINITION,
71
+            headers={'Content-Type': 'text/plain'},
72
+            expect_errors=True
73
+        )
74
+
75
+        self.assertEqual(403, resp.status_int)
76
+
77
+    @mock.patch.object(db_api, "create_action_definition")
78
+    def test_action_create_allowed(self, mock_obj):
79
+        self.policy.change_policy_definition(
80
+            {"actions:create": "role:FAKE or rule:admin_or_owner"}
81
+        )
82
+        resp = self.app.post(
83
+            '/v2/actions',
84
+            ACTION_DEFINITION,
85
+            headers={'Content-Type': 'text/plain'},
86
+            expect_errors=True
87
+        )
88
+
89
+        self.assertEqual(201, resp.status_int)
90
+
91
+    @mock.patch.object(db_api, "create_action_definition")
92
+    def test_action_create_public_not_allowed(self, mock_obj):
93
+        self.policy.change_policy_definition({
94
+            "actions:create": "role:FAKE or rule:admin_or_owner",
95
+            "actions:publicize": "role:FAKE"
96
+        })
97
+        resp = self.app.post(
98
+            '/v2/actions?scope=public',
99
+            ACTION_DEFINITION,
100
+            headers={'Content-Type': 'text/plain'},
101
+            expect_errors=True
102
+        )
103
+
104
+        self.assertEqual(403, resp.status_int)
105
+
106
+    @mock.patch.object(db_api, "create_action_definition")
107
+    def test_action_create_public_allowed(self, mock_obj):
108
+        self.policy.change_policy_definition({
109
+            "actions:create": "role:FAKE or rule:admin_or_owner",
110
+            "actions:publicize": "role:FAKE or rule:admin_or_owner"
111
+        })
112
+        resp = self.app.post(
113
+            '/v2/actions?scope=public',
114
+            ACTION_DEFINITION,
115
+            headers={'Content-Type': 'text/plain'},
116
+            expect_errors=True
117
+        )
118
+
119
+        self.assertEqual(201, resp.status_int)
120
+
121
+    @mock.patch.object(db_api, "delete_action_definition", MOCK_DELETE)
122
+    @mock.patch.object(db_api, "get_action_definition", MOCK_ACTION)
123
+    def test_action_delete_not_allowed(self):
124
+        self.policy.change_policy_definition(
125
+            {"actions:delete": "role:FAKE"}
126
+        )
127
+        resp = self.app.delete(
128
+            '/v2/actions/123',
129
+            expect_errors=True
130
+        )
131
+
132
+        self.assertEqual(403, resp.status_int)
133
+
134
+    @mock.patch.object(db_api, "delete_action_definition", MOCK_DELETE)
135
+    @mock.patch.object(db_api, "get_action_definition", MOCK_ACTION)
136
+    def test_action_delete_allowed(self):
137
+        self.policy.change_policy_definition(
138
+            {"actions:delete": "role:FAKE or rule:admin_or_owner"}
139
+        )
140
+        resp = self.app.delete(
141
+            '/v2/actions/123',
142
+            expect_errors=True
143
+        )
144
+
145
+        self.assertEqual(204, resp.status_int)
146
+
147
+    @mock.patch.object(db_api, "get_action_definition", MOCK_ACTION)
148
+    def test_action_get_not_allowed(self):
149
+        self.policy.change_policy_definition(
150
+            {"actions:get": "role:FAKE"}
151
+        )
152
+        resp = self.app.get(
153
+            '/v2/actions/123',
154
+            expect_errors=True
155
+        )
156
+
157
+        self.assertEqual(403, resp.status_int)
158
+
159
+    @mock.patch.object(db_api, "get_action_definition", MOCK_ACTION)
160
+    def test_action_get_allowed(self):
161
+        self.policy.change_policy_definition(
162
+            {"actions:get": "role:FAKE or rule:admin_or_owner"}
163
+        )
164
+        resp = self.app.get(
165
+            '/v2/actions/123',
166
+            expect_errors=True
167
+        )
168
+
169
+        self.assertEqual(200, resp.status_int)
170
+
171
+    def test_action_list_not_allowed(self):
172
+        self.policy.change_policy_definition(
173
+            {"actions:list": "role:FAKE"}
174
+        )
175
+        resp = self.app.get(
176
+            '/v2/actions',
177
+            expect_errors=True
178
+        )
179
+
180
+        self.assertEqual(403, resp.status_int)
181
+
182
+    def test_action_list_allowed(self):
183
+        self.policy.change_policy_definition(
184
+            {"actions:list": "role:FAKE or rule:admin_or_owner"}
185
+        )
186
+        resp = self.app.get(
187
+            '/v2/actions',
188
+            expect_errors=True
189
+        )
190
+
191
+        self.assertEqual(200, resp.status_int)
192
+
193
+    @mock.patch.object(db_api, "update_action_definition")
194
+    def test_action_update_not_allowed(self, mock_obj):
195
+        self.policy.change_policy_definition(
196
+            {"actions:update": "role:FAKE"}
197
+        )
198
+        resp = self.app.put(
199
+            '/v2/actions',
200
+            ACTION_DEFINITION,
201
+            headers={'Content-Type': 'text/plain'},
202
+            expect_errors=True
203
+        )
204
+
205
+        self.assertEqual(403, resp.status_int)
206
+
207
+    @mock.patch.object(db_api, "update_action_definition")
208
+    def test_action_update_allowed(self, mock_obj):
209
+        self.policy.change_policy_definition(
210
+            {"actions:update": "role:FAKE or rule:admin_or_owner"}
211
+        )
212
+        resp = self.app.put(
213
+            '/v2/actions',
214
+            ACTION_DEFINITION,
215
+            headers={'Content-Type': 'text/plain'},
216
+            expect_errors=True
217
+        )
218
+
219
+        self.assertEqual(200, resp.status_int)
220
+
221
+    @mock.patch.object(db_api, "update_action_definition")
222
+    def test_action_update_public_not_allowed(self, mock_obj):
223
+        self.policy.change_policy_definition({
224
+            "actions:update": "role:FAKE or rule:admin_or_owner",
225
+            "actions:publicize": "role:FAKE"
226
+        })
227
+        resp = self.app.put(
228
+            '/v2/actions?scope=public',
229
+            ACTION_DEFINITION,
230
+            headers={'Content-Type': 'text/plain'},
231
+            expect_errors=True
232
+        )
233
+
234
+        self.assertEqual(403, resp.status_int)
235
+
236
+    @mock.patch.object(db_api, "update_action_definition")
237
+    def test_action_update_public_allowed(self, mock_obj):
238
+        self.policy.change_policy_definition({
239
+            "actions:update": "role:FAKE or rule:admin_or_owner",
240
+            "actions:publicize": "role:FAKE or rule:admin_or_owner"
241
+        })
242
+        resp = self.app.put(
243
+            '/v2/actions?scope=public',
244
+            ACTION_DEFINITION,
245
+            headers={'Content-Type': 'text/plain'},
246
+            expect_errors=True
247
+        )
248
+
249
+        self.assertEqual(200, resp.status_int)

+ 279
- 0
mistral/tests/unit/policies/test_workflows.py View File

@@ -0,0 +1,279 @@
1
+# Copyright 2016 NEC Corporation. All rights reserved.
2
+# Copyright 2018 OVH SAS. All rights reserved.
3
+#
4
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
5
+# not use this file except in compliance with the License. You may obtain
6
+# a copy of the License at
7
+#
8
+# http://www.apache.org/licenses/LICENSE-2.0
9
+#
10
+# Unless required by applicable law or agreed to in writing, software
11
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13
+# License for the specific language governing permissions and limitations
14
+# under the License.
15
+
16
+
17
+import datetime
18
+
19
+import mock
20
+
21
+from mistral.db.v2 import api as db_api
22
+from mistral.db.v2.sqlalchemy import models
23
+from mistral.tests.unit.api import base
24
+from mistral.tests.unit.mstrlfixtures import policy_fixtures
25
+
26
+MOCK_DELETE = mock.MagicMock(return_value=None)
27
+
28
+WF_DEFINITION = """
29
+---
30
+version: '2.0'
31
+
32
+flow:
33
+  type: direct
34
+  input:
35
+    - param1
36
+
37
+  tasks:
38
+    task1:
39
+      action: std.echo output="Hi"
40
+"""
41
+WF_DB = models.WorkflowDefinition(
42
+    id='123e4567-e89b-12d3-a456-426655440000',
43
+    name='flow',
44
+    definition=WF_DEFINITION,
45
+    created_at=datetime.datetime(1970, 1, 1),
46
+    updated_at=datetime.datetime(1970, 1, 1),
47
+    spec={'input': ['param1']}
48
+)
49
+MOCK_WF = mock.MagicMock(return_value=WF_DB)
50
+
51
+
52
+class TestWorkflowPolicy(base.APITest):
53
+    """Test workflow related policies
54
+
55
+    Policies to test:
56
+    - workflows:create
57
+    - workflows:delete
58
+    - workflows:get
59
+    - workflows:list
60
+    - workflows:list:all_projects
61
+    - workflows:publicize (on POST & PUT)
62
+    - workflows:update
63
+    """
64
+
65
+    def setUp(self):
66
+        self.policy = self.useFixture(policy_fixtures.PolicyFixture())
67
+        super(TestWorkflowPolicy, self).setUp()
68
+
69
+    @mock.patch.object(db_api, "create_workflow_definition")
70
+    def test_workflow_create_not_allowed(self, mock_obj):
71
+        self.policy.change_policy_definition(
72
+            {"workflows:create": "role:FAKE"}
73
+        )
74
+        resp = self.app.post(
75
+            '/v2/workflows',
76
+            WF_DEFINITION,
77
+            headers={'Content-Type': 'text/plain'},
78
+            expect_errors=True
79
+        )
80
+
81
+        self.assertEqual(403, resp.status_int)
82
+
83
+    @mock.patch.object(db_api, "create_workflow_definition")
84
+    def test_workflow_create_allowed(self, mock_obj):
85
+        self.policy.change_policy_definition(
86
+            {"workflows:create": "role:FAKE or rule:admin_or_owner"}
87
+        )
88
+        resp = self.app.post(
89
+            '/v2/workflows',
90
+            WF_DEFINITION,
91
+            headers={'Content-Type': 'text/plain'},
92
+            expect_errors=True
93
+        )
94
+
95
+        self.assertEqual(201, resp.status_int)
96
+
97
+    @mock.patch.object(db_api, "create_workflow_definition")
98
+    def test_workflow_create_public_not_allowed(self, mock_obj):
99
+        self.policy.change_policy_definition({
100
+            "workflows:create": "role:FAKE or rule:admin_or_owner",
101
+            "workflows:publicize": "role:FAKE"
102
+        })
103
+        resp = self.app.post(
104
+            '/v2/workflows?scope=public',
105
+            WF_DEFINITION,
106
+            headers={'Content-Type': 'text/plain'},
107
+            expect_errors=True
108
+        )
109
+
110
+        self.assertEqual(403, resp.status_int)
111
+
112
+    @mock.patch.object(db_api, "create_workflow_definition")
113
+    def test_workflow_create_public_allowed(self, mock_obj):
114
+        self.policy.change_policy_definition({
115
+            "workflows:create": "role:FAKE or rule:admin_or_owner",
116
+            "workflows:publicize": "role:FAKE or rule:admin_or_owner"
117
+        })
118
+        resp = self.app.post(
119
+            '/v2/workflows?scope=public',
120
+            WF_DEFINITION,
121
+            headers={'Content-Type': 'text/plain'},
122
+            expect_errors=True
123
+        )
124
+
125
+        self.assertEqual(201, resp.status_int)
126
+
127
+    @mock.patch.object(db_api, "delete_workflow_definition", MOCK_DELETE)
128
+    @mock.patch.object(db_api, "get_workflow_definition", MOCK_WF)
129
+    def test_workflow_delete_not_allowed(self):
130
+        self.policy.change_policy_definition(
131
+            {"workflows:delete": "role:FAKE"}
132
+        )
133
+        resp = self.app.delete(
134
+            '/v2/workflows/123',
135
+            expect_errors=True
136
+        )
137
+
138
+        self.assertEqual(403, resp.status_int)
139
+
140
+    @mock.patch.object(db_api, "delete_workflow_definition", MOCK_DELETE)
141
+    @mock.patch.object(db_api, "get_workflow_definition", MOCK_WF)
142
+    def test_workflow_delete_allowed(self):
143
+        self.policy.change_policy_definition(
144
+            {"workflows:delete": "role:FAKE or rule:admin_or_owner"}
145
+        )
146
+        resp = self.app.delete(
147
+            '/v2/workflows/123',
148
+            expect_errors=True
149
+        )
150
+
151
+        self.assertEqual(204, resp.status_int)
152
+
153
+    @mock.patch.object(db_api, "get_workflow_definition", MOCK_WF)
154
+    def test_workflow_get_not_allowed(self):
155
+        self.policy.change_policy_definition(
156
+            {"workflows:get": "role:FAKE"}
157
+        )
158
+        resp = self.app.get(
159
+            '/v2/workflows/123',
160
+            expect_errors=True
161
+        )
162
+
163
+        self.assertEqual(403, resp.status_int)
164
+
165
+    @mock.patch.object(db_api, "get_workflow_definition", MOCK_WF)
166
+    def test_workflow_get_allowed(self):
167
+        self.policy.change_policy_definition(
168
+            {"workflows:get": "role:FAKE or rule:admin_or_owner"}
169
+        )
170
+        resp = self.app.get(
171
+            '/v2/workflows/123',
172
+            expect_errors=True
173
+        )
174
+
175
+        self.assertEqual(200, resp.status_int)
176
+
177
+    def test_workflow_list_not_allowed(self):
178
+        self.policy.change_policy_definition(
179
+            {"workflows:list": "role:FAKE"}
180
+        )
181
+        resp = self.app.get(
182
+            '/v2/workflows',
183
+            expect_errors=True
184
+        )
185
+
186
+        self.assertEqual(403, resp.status_int)
187
+
188
+    def test_workflow_list_allowed(self):
189
+        self.policy.change_policy_definition(
190
+            {"workflows:list": "role:FAKE or rule:admin_or_owner"}
191
+        )
192
+        resp = self.app.get(
193
+            '/v2/workflows',
194
+            expect_errors=True
195
+        )
196
+
197
+        self.assertEqual(200, resp.status_int)
198
+
199
+    def test_workflow_list_all_not_allowed(self):
200
+        self.policy.change_policy_definition({
201
+            "workflows:list": "role:FAKE or rule:admin_or_owner",
202
+            "workflows:list:all_projects": "role:FAKE"
203
+        })
204
+        resp = self.app.get(
205
+            '/v2/workflows?all_projects=1',
206
+            expect_errors=True
207
+        )
208
+
209
+        self.assertEqual(403, resp.status_int)
210
+
211
+    def test_workflow_list_all_allowed(self):
212
+        self.policy.change_policy_definition({
213
+            "workflows:list": "role:FAKE or rule:admin_or_owner",
214
+            "workflows:list:all_projects": "role:FAKE or rule:admin_or_owner"
215
+        })
216
+        resp = self.app.get(
217
+            '/v2/workflows?all_projects=1',
218
+            expect_errors=True
219
+        )
220
+
221
+        self.assertEqual(200, resp.status_int)
222
+
223
+    @mock.patch.object(db_api, "update_workflow_definition")
224
+    def test_workflow_update_not_allowed(self, mock_obj):
225
+        self.policy.change_policy_definition(
226
+            {"workflows:update": "role:FAKE"}
227
+        )
228
+        resp = self.app.put(
229
+            '/v2/workflows',
230
+            WF_DEFINITION,
231
+            headers={'Content-Type': 'text/plain'},
232
+            expect_errors=True
233
+        )
234
+
235
+        self.assertEqual(403, resp.status_int)
236
+
237
+    @mock.patch.object(db_api, "update_workflow_definition")
238
+    def test_workflow_update_allowed(self, mock_obj):
239
+        self.policy.change_policy_definition(
240
+            {"workflows:update": "role:FAKE or rule:admin_or_owner"}
241
+        )
242
+        resp = self.app.put(
243
+            '/v2/workflows',
244
+            WF_DEFINITION,
245
+            headers={'Content-Type': 'text/plain'},
246
+            expect_errors=True
247
+        )
248
+
249
+        self.assertEqual(200, resp.status_int)
250
+
251
+    @mock.patch.object(db_api, "update_workflow_definition")
252
+    def test_workflow_update_public_not_allowed(self, mock_obj):
253
+        self.policy.change_policy_definition({
254
+            "workflows:update": "role:FAKE or rule:admin_or_owner",
255
+            "workflows:publicize": "role:FAKE"
256
+        })
257
+        resp = self.app.put(
258
+            '/v2/workflows?scope=public',
259
+            WF_DEFINITION,
260
+            headers={'Content-Type': 'text/plain'},
261
+            expect_errors=True
262
+        )
263
+
264
+        self.assertEqual(403, resp.status_int)
265
+
266
+    @mock.patch.object(db_api, "update_workflow_definition")
267
+    def test_workflow_update_public_allowed(self, mock_obj):
268
+        self.policy.change_policy_definition({
269
+            "workflows:update": "role:FAKE or rule:admin_or_owner",
270
+            "workflows:publicize": "role:FAKE or rule:admin_or_owner"
271
+        })
272
+        resp = self.app.put(
273
+            '/v2/workflows?scope=public',
274
+            WF_DEFINITION,
275
+            headers={'Content-Type': 'text/plain'},
276
+            expect_errors=True
277
+        )
278
+
279
+        self.assertEqual(200, resp.status_int)

+ 7
- 0
releasenotes/notes/add-publicize-policy-d3b44590286c7fdd.yaml View File

@@ -0,0 +1,7 @@
1
+---
2
+features:
3
+  - |
4
+    Mistral now supports a `publicize` policy on actions and workflows which
5
+    controls whether the users are allowed to create or update them. The
6
+    default policy does not change which means that everyone can publish
7
+    action or workflow unless specified differently in the policy.

Loading…
Cancel
Save