Passed
Pull Request — master (#3804)
by Lakshmi
05:58
created

RuleController.get_all()   A

Complexity

Conditions 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
1
# Licensed to the StackStorm, Inc ('StackStorm') under one or more
2
# contributor license agreements.  See the NOTICE file distributed with
3
# this work for additional information regarding copyright ownership.
4
# The ASF licenses this file to You under the Apache License, Version 2.0
5
# (the "License"); you may not use this file except in compliance with
6
# the License.  You may obtain 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,
12
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
# See the License for the specific language governing permissions and
14
# limitations under the License.
15
16
import six
17
import jsonschema
18
from mongoengine import ValidationError
19
20
from st2common import log as logging
21
from st2common.exceptions.apivalidation import ValueValidationException
22
from st2common.exceptions.db import StackStormDBObjectConflictError
23
from st2common.exceptions.rbac import AccessDeniedError
24
from st2common.exceptions.triggers import TriggerDoesNotExistException
25
from st2common.exceptions.triggers import TriggerParametersValidationException
26
from st2api.controllers import resource
27
from st2api.controllers.controller_transforms import transform_to_bool
28
from st2api.controllers.v1.ruleviews import RuleViewController
29
from st2common.models.api.rule import RuleAPI
30
from st2common.persistence.rule import Rule
31
from st2common.rbac.types import PermissionType
32
from st2common.rbac import utils as rbac_utils
33
from st2common.rbac.utils import assert_user_has_rule_trigger_and_action_permission
34
from st2common.router import exc
35
from st2common.router import abort
36
from st2common.router import Response
37
from st2common.services.triggers import cleanup_trigger_db_for_rule, increment_trigger_ref_count
38
39
http_client = six.moves.http_client
40
41
LOG = logging.getLogger(__name__)
42
43
44
class RuleController(resource.ContentPackResourceController):
45
    """
46
        Implements the RESTful web endpoint that handles
47
        the lifecycle of Rules in the system.
48
    """
49
    views = RuleViewController()
50
51
    model = RuleAPI
52
    access = Rule
53
    supported_filters = {
54
        'name': 'name',
55
        'pack': 'pack',
56
        'action': 'action.ref',
57
        'trigger': 'trigger',
58
        'enabled': 'enabled'
59
    }
60
61
    filter_transform_functions = {
62
        'enabled': transform_to_bool
63
    }
64
65
    query_options = {
66
        'sort': ['pack', 'name']
67
    }
68
69
    include_reference = True
70
71
    def get_all(self, sort=None, offset=0, limit=None, requester_user=None, **raw_filters):
72
        from_model_kwargs = {'ignore_missing_trigger': True}
73
        return super(RuleController, self)._get_all(from_model_kwargs=from_model_kwargs,
74
                                                    sort=sort,
75
                                                    offset=offset,
76
                                                    limit=limit,
77
                                                    raw_filters=raw_filters,
78
                                                    requester_user=requester_user)
79
80
    def get_one(self, ref_or_id, requester_user):
81
        from_model_kwargs = {'ignore_missing_trigger': True}
82
        return super(RuleController, self)._get_one(ref_or_id, from_model_kwargs=from_model_kwargs,
83
                                                    requester_user=requester_user,
84
                                                    permission_type=PermissionType.RULE_VIEW)
85
86
    def post(self, rule, requester_user):
87
        """
88
            Create a new rule.
89
90
            Handles requests:
91
                POST /rules/
92
        """
93
94
        permission_type = PermissionType.RULE_CREATE
95
        rbac_utils.assert_user_has_resource_api_permission(user_db=requester_user,
96
                                                           resource_api=rule,
97
                                                           permission_type=permission_type)
98
99
        try:
100
            rule_db = RuleAPI.to_model(rule)
101
            LOG.debug('/rules/ POST verified RuleAPI and formulated RuleDB=%s', rule_db)
102
103
            # Check referenced trigger and action permissions
104
            # Note: This needs to happen after "to_model" call since to_model performs some
105
            # validation (trigger exists, etc.)
106
            assert_user_has_rule_trigger_and_action_permission(user_db=requester_user,
107
                                                               rule_api=rule)
108
109
            rule_db = Rule.add_or_update(rule_db)
110
            # After the rule has been added modify the ref_count. This way a failure to add
111
            # the rule due to violated constraints will have no impact on ref_count.
112
            increment_trigger_ref_count(rule_api=rule)
113
        except (ValidationError, ValueError) as e:
114
            LOG.exception('Validation failed for rule data=%s.', rule)
115
            abort(http_client.BAD_REQUEST, str(e))
116
            return
117
        except (ValueValidationException, jsonschema.ValidationError) as e:
118
            LOG.exception('Validation failed for rule data=%s.', rule)
119
            abort(http_client.BAD_REQUEST, str(e))
120
            return
121
        except TriggerDoesNotExistException as e:
122
            msg = ('Trigger "%s" defined in the rule does not exist in system or it\'s missing '
123
                   'required "parameters" attribute' % (rule.trigger['type']))
124
            LOG.exception(msg)
125
            abort(http_client.BAD_REQUEST, msg)
126
            return
127
        except TriggerParametersValidationException as e:
128
            msg = str(e)
129
            LOG.exception(msg)
130
            abort(http_client.BAD_REQUEST, msg)
131
            return
132
        except StackStormDBObjectConflictError as e:
133
            raise
134
        except AccessDeniedError:
135
            raise
136
        except Exception as e:
137
            msg = e.message
138
            LOG.exception(msg)
139
            abort(http_client.INTERNAL_SERVER_ERROR, msg)
140
            return
141
142
        extra = {'rule_db': rule_db}
143
        LOG.audit('Rule created. Rule.id=%s' % (rule_db.id), extra=extra)
144
        rule_api = RuleAPI.from_model(rule_db)
145
146
        return Response(json=rule_api, status=exc.HTTPCreated.code)
147
148
    def put(self, rule, rule_ref_or_id, requester_user):
149
        rule_db = self._get_by_ref_or_id(rule_ref_or_id)
150
151
        permission_type = PermissionType.RULE_MODIFY
152
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
153
                                                          resource_db=rule,
154
                                                          permission_type=permission_type)
155
156
        LOG.debug('PUT /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db)
157
158
        try:
159
            if rule.id is not None and rule.id is not '' and rule.id != rule_ref_or_id:
160
                LOG.warning('Discarding mismatched id=%s found in payload and using uri_id=%s.',
161
                            rule.id, rule_ref_or_id)
162
            old_rule_db = rule_db
163
164
            try:
165
                rule_db = RuleAPI.to_model(rule)
166
            except TriggerDoesNotExistException as e:
167
                abort(http_client.BAD_REQUEST, str(e))
168
                return
169
170
            # Check referenced trigger and action permissions
171
            # Note: This needs to happen after "to_model" call since to_model performs some
172
            # validation (trigger exists, etc.)
173
            assert_user_has_rule_trigger_and_action_permission(user_db=requester_user,
174
                                                               rule_api=rule)
175
176
            rule_db.id = rule_ref_or_id
177
            rule_db = Rule.add_or_update(rule_db)
178
            # After the rule has been added modify the ref_count. This way a failure to add
179
            # the rule due to violated constraints will have no impact on ref_count.
180
            increment_trigger_ref_count(rule_api=rule)
181
        except (ValueValidationException, jsonschema.ValidationError, ValueError) as e:
182
            LOG.exception('Validation failed for rule data=%s', rule)
183
            abort(http_client.BAD_REQUEST, str(e))
184
            return
185
186
        # use old_rule_db for cleanup.
187
        cleanup_trigger_db_for_rule(old_rule_db)
188
189
        extra = {'old_rule_db': old_rule_db, 'new_rule_db': rule_db}
190
        LOG.audit('Rule updated. Rule.id=%s.' % (rule_db.id), extra=extra)
191
        rule_api = RuleAPI.from_model(rule_db)
192
193
        return rule_api
194
195
    def delete(self, rule_ref_or_id, requester_user):
196
        """
197
            Delete a rule.
198
199
            Handles requests:
200
                DELETE /rules/1
201
        """
202
        rule_db = self._get_by_ref_or_id(ref_or_id=rule_ref_or_id)
203
204
        permission_type = PermissionType.RULE_DELETE
205
        rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user,
206
                                                          resource_db=rule_db,
207
                                                          permission_type=permission_type)
208
209
        LOG.debug('DELETE /rules/ lookup with id=%s found object: %s', rule_ref_or_id, rule_db)
210
        try:
211
            Rule.delete(rule_db)
212
        except Exception as e:
213
            LOG.exception('Database delete encountered exception during delete of id="%s".',
214
                          rule_ref_or_id)
215
            abort(http_client.INTERNAL_SERVER_ERROR, str(e))
216
            return
217
218
        # use old_rule_db for cleanup.
219
        cleanup_trigger_db_for_rule(rule_db)
220
221
        extra = {'rule_db': rule_db}
222
        LOG.audit('Rule deleted. Rule.id=%s.' % (rule_db.id), extra=extra)
223
224
        return Response(status=http_client.NO_CONTENT)
225
226
227
rule_controller = RuleController()
228