def setUp(self): super(WebhookControllerRBACTestCase, self).setUp() # Insert mock users, roles and assignments # Users user_1_db = UserDB(name='webhook_list') user_1_db = User.add_or_update(user_1_db) self.users['webhook_list'] = user_1_db user_2_db = UserDB(name='webhook_view') user_2_db = User.add_or_update(user_2_db) self.users['webhook_view'] = user_2_db # Roles # webhook_list grant_db = PermissionGrantDB( resource_uid=None, resource_type=ResourceType.WEBHOOK, permission_types=[PermissionType.WEBHOOK_LIST]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_1_db = RoleDB(name='webhook_list', permission_grants=permission_grants) role_1_db = Role.add_or_update(role_1_db) self.roles['webhook_list'] = role_1_db # webhook_view on webhook 1 (git) name = 'git' webhook_db = WebhookDB(name=name) webhook_uid = webhook_db.get_uid() grant_db = PermissionGrantDB( resource_uid=webhook_uid, resource_type=ResourceType.WEBHOOK, permission_types=[PermissionType.WEBHOOK_VIEW]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_1_db = RoleDB(name='webhook_view', permission_grants=permission_grants) role_1_db = Role.add_or_update(role_1_db) self.roles['webhook_view'] = role_1_db # Role assignments role_assignment_db = UserRoleAssignmentDB( user=self.users['webhook_list'].name, role=self.roles['webhook_list'].name, source='assignments/%s.yaml' % self.users['webhook_list'].name) UserRoleAssignment.add_or_update(role_assignment_db) role_assignment_db = UserRoleAssignmentDB( user=self.users['webhook_view'].name, role=self.roles['webhook_view'].name, source='assignments/%s.yaml' % self.users['webhook_view'].name) UserRoleAssignment.add_or_update(role_assignment_db)
def setUp(self): super(WebhookControllerRBACTestCase, self).setUp() # Insert mock users, roles and assignments # Users user_1_db = UserDB(name='webhook_list') user_1_db = User.add_or_update(user_1_db) self.users['webhook_list'] = user_1_db user_2_db = UserDB(name='webhook_view') user_2_db = User.add_or_update(user_2_db) self.users['webhook_view'] = user_2_db # Roles # webhook_list grant_db = PermissionGrantDB(resource_uid=None, resource_type=ResourceType.WEBHOOK, permission_types=[PermissionType.WEBHOOK_LIST]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_1_db = RoleDB(name='webhook_list', permission_grants=permission_grants) role_1_db = Role.add_or_update(role_1_db) self.roles['webhook_list'] = role_1_db # webhook_view on webhook 1 (git) name = 'git' webhook_db = WebhookDB(name=name) webhook_uid = webhook_db.get_uid() grant_db = PermissionGrantDB(resource_uid=webhook_uid, resource_type=ResourceType.WEBHOOK, permission_types=[PermissionType.WEBHOOK_VIEW]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_1_db = RoleDB(name='webhook_view', permission_grants=permission_grants) role_1_db = Role.add_or_update(role_1_db) self.roles['webhook_view'] = role_1_db # Role assignments role_assignment_db = UserRoleAssignmentDB( user=self.users['webhook_list'].name, role=self.roles['webhook_list'].name, source='assignments/%s.yaml' % self.users['webhook_list'].name) UserRoleAssignment.add_or_update(role_assignment_db) role_assignment_db = UserRoleAssignmentDB( user=self.users['webhook_view'].name, role=self.roles['webhook_view'].name, source='assignments/%s.yaml' % self.users['webhook_view'].name) UserRoleAssignment.add_or_update(role_assignment_db)
def test_get_one_no_permissions(self): user_db = self.users['no_permissions'] self.use_user(user_db) name = 'git' webhook_db = WebhookDB(name=name) webhook_id = name webhook_uid = webhook_db.get_uid() resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True) expected_msg = ('User "no_permissions" doesn\'t have required permission "webhook_view"' ' on resource "%s"' % (webhook_uid)) self.assertEqual(resp.status_code, httplib.FORBIDDEN) self.assertEqual(resp.json['faultstring'], expected_msg)
def test_get_one_no_permissions(self): user_db = self.users['no_permissions'] self.use_user(user_db) name = 'git' webhook_db = WebhookDB(name=name) webhook_id = name webhook_uid = webhook_db.get_uid() resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True) expected_msg = ('User "no_permissions" doesn\'t have required permission "webhook_view"' ' on resource "%s"' % (webhook_uid)) self.assertEqual(resp.status_code, http_client.FORBIDDEN) self.assertEqual(resp.json['faultstring'], expected_msg)
def setUp(self): super(WebhookPermissionsResolverTestCase, self).setUp() # Create some mock users user_1_db = UserDB(name='custom_role_webhook_grant') user_1_db = User.add_or_update(user_1_db) self.users['custom_role_webhook_grant'] = user_1_db # Create some mock resources on which permissions can be granted webhook_1_db = WebhookDB(name='st2/') self.resources['webhook_1'] = webhook_1_db # Create some mock roles with associated permission grants # Custom role - "webhook_send" grant on webhook_1 grant_db = PermissionGrantDB( resource_uid=self.resources['webhook_1'].get_uid(), resource_type=ResourceType.WEBHOOK, permission_types=[PermissionType.WEBHOOK_SEND]) grant_db = PermissionGrant.add_or_update(grant_db) permission_grants = [str(grant_db.id)] role_db = RoleDB(name='custom_role_webhook_grant', permission_grants=permission_grants) role_db = Role.add_or_update(role_db) self.roles['custom_role_webhook_grant'] = role_db # Create some mock role assignments user_db = self.users['custom_role_webhook_grant'] role_assignment_db = UserRoleAssignmentDB( user=user_db.name, role=self.roles['custom_role_webhook_grant'].name) UserRoleAssignment.add_or_update(role_assignment_db)
def post(self, hook, webhook_body_api, headers, requester_user): body = webhook_body_api.data permission_type = PermissionType.WEBHOOK_SEND rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=WebhookDB(name=hook), permission_type=permission_type) headers = self._get_headers_as_dict(headers) # If webhook contains a trace-tag use that else create create a unique trace-tag. trace_context = self._create_trace_context(trace_tag=headers.pop( TRACE_TAG_HEADER, None), hook=hook) if hook == 'st2' or hook == 'st2/': # When using st2 or system webhook, body needs to always be a dict if not isinstance(body, dict): type_string = get_json_type_for_python_value(body) msg = ('Webhook body needs to be an object, got: %s' % (type_string)) raise ValueError(msg) trigger = body.get('trigger', None) payload = body.get('payload', None) if not trigger: msg = 'Trigger not specified.' return abort(http_client.BAD_REQUEST, msg) self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=True) else: if not self._is_valid_hook(hook): self._log_request('Invalid hook.', headers, body) msg = 'Webhook %s not registered with st2' % hook return abort(http_client.NOT_FOUND, msg) triggers = self._hooks.get_triggers_for_hook(hook) payload = {} payload['headers'] = headers payload['body'] = body # Dispatch trigger instance for each of the trigger found for trigger_dict in triggers: # TODO: Instead of dispatching the whole dict we should just # dispatch TriggerDB.ref or similar self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger_dict, payload=payload, trace_context=trace_context, throw_on_validation_error=True) return Response(json=body, status=http_client.ACCEPTED)
def test_get_all_permission_success_get_one_no_permission_failure(self): user_db = self.users['webhook_list'] self.use_user(user_db) # webhook_list permission, but no webhook_view permission resp = self.app.get('/v1/webhooks') self.assertEqual(resp.status_code, httplib.OK) self.assertEqual(len(resp.json), 1) name = 'git' webhook_db = WebhookDB(name=name) webhook_id = name webhook_uid = webhook_db.get_uid() resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True) expected_msg = ('User "webhook_list" doesn\'t have required permission "webhook_view"' ' on resource "%s"' % (webhook_uid)) self.assertEqual(resp.status_code, httplib.FORBIDDEN) self.assertEqual(resp.json['faultstring'], expected_msg)
def test_get_all_permission_success_get_one_no_permission_failure(self): user_db = self.users['webhook_list'] self.use_user(user_db) # webhook_list permission, but no webhook_view permission resp = self.app.get('/v1/webhooks') self.assertEqual(resp.status_code, http_client.OK) self.assertEqual(len(resp.json), 1) name = 'git' webhook_db = WebhookDB(name=name) webhook_id = name webhook_uid = webhook_db.get_uid() resp = self.app.get('/v1/webhooks/%s' % (webhook_id), expect_errors=True) expected_msg = ('User "webhook_list" doesn\'t have required permission "webhook_view"' ' on resource "%s"' % (webhook_uid)) self.assertEqual(resp.status_code, http_client.FORBIDDEN) self.assertEqual(resp.json['faultstring'], expected_msg)
def func_wrapper(*args, **kwargs): hook = '/'.join( args[1:]) # TODO: There must be a better way to do this. webhook_db = WebhookDB(name=hook) resource_db = webhook_db utils.assert_request_user_has_resource_permission( request=pecan.request, resource_db=resource_db, permission_type=permission_type) return func(*args, **kwargs)
def get_one(self, name, requester_user): triggers = self._hooks.get_triggers_for_hook(name) if not triggers: abort(http_client.NOT_FOUND) return permission_type = PermissionType.WEBHOOK_VIEW rbac_utils.assert_user_has_resource_db_permission(user_db=requester_user, resource_db=WebhookDB(name=name), permission_type=permission_type) # For demonstration purpose return 1st return triggers[0]
def post(self, hook, body, headers, requester_user): body = vars(body) permission_type = PermissionType.WEBHOOK_SEND rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=WebhookDB(name=hook), permission_type=permission_type) headers = self._get_headers_as_dict(headers) # If webhook contains a trace-tag use that else create create a unique trace-tag. trace_context = self._create_trace_context(trace_tag=headers.pop( TRACE_TAG_HEADER, None), hook=hook) if hook == 'st2' or hook == 'st2/': trigger = body.get('trigger', None) payload = body.get('payload', None) if not trigger: msg = 'Trigger not specified.' return abort(http_client.BAD_REQUEST, msg) self._trigger_dispatcher.dispatch(trigger, payload=payload, trace_context=trace_context) else: if not self._is_valid_hook(hook): self._log_request('Invalid hook.', headers, body) msg = 'Webhook %s not registered with st2' % hook return abort(http_client.NOT_FOUND, msg) triggers = self._hooks.get_triggers_for_hook(hook) payload = {} payload['headers'] = headers payload['body'] = body # Dispatch trigger instance for each of the trigger found for trigger in triggers: self._trigger_dispatcher.dispatch(trigger, payload=payload, trace_context=trace_context) return Response(json=body, status=http_client.ACCEPTED)
def user_has_trigger_permission(self, user_db, trigger): """ Check if the user has access to the provided trigger. This method is to be used during rule create and update where we check if the user has the necessary trigger permissions. Note: Right now we only support webhook triggers. :param trigger: "trigger" attribute of the RuleAPI object. :type trigger: ``dict`` """ log_context = { 'user_db': user_db, 'trigger': trigger, 'resolver': self.__class__.__name__ } trigger_type = trigger['type'] trigger_parameters = trigger.get('parameters', {}) if trigger_type != WEBHOOK_TRIGGER_TYPE: self._log( 'Not a webhook trigger type, ignoring trigger permission checking', extra=log_context) return True resolver = get_resolver_for_resource_type(ResourceType.WEBHOOK) webhook_db = WebhookDB(name=trigger_parameters['url']) permission_type = PermissionType.WEBHOOK_CREATE result = resolver.user_has_resource_db_permission( user_db=user_db, resource_db=webhook_db, permission_type=permission_type) if result is True: self._log('Found a matching trigger grant', extra=log_context) return True self._log('No matching trigger grants found', extra=log_context) return False
def post(self, hook, webhook_body_api, headers, requester_user): body = webhook_body_api.data permission_type = PermissionType.WEBHOOK_SEND rbac_utils = get_rbac_backend().get_utils_class() rbac_utils.assert_user_has_resource_db_permission( user_db=requester_user, resource_db=WebhookDB(name=hook), permission_type=permission_type, ) headers = self._get_headers_as_dict(headers) headers = self._filter_authentication_headers(headers) # If webhook contains a trace-tag use that else create create a unique trace-tag. trace_context = self._create_trace_context( trace_tag=headers.pop(TRACE_TAG_HEADER, None), hook=hook ) if hook == "st2" or hook == "st2/": # When using st2 or system webhook, body needs to always be a dict if not isinstance(body, dict): type_string = get_json_type_for_python_value(body) msg = "Webhook body needs to be an object, got: %s" % (type_string) raise ValueError(msg) trigger = body.get("trigger", None) payload = body.get("payload", None) if not trigger: msg = "Trigger not specified." return abort(http_client.BAD_REQUEST, msg) self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger, payload=payload, trace_context=trace_context, throw_on_validation_error=True, ) else: if not self._is_valid_hook(hook): self._log_request("Invalid hook.", headers, body) msg = "Webhook %s not registered with st2" % hook return abort(http_client.NOT_FOUND, msg) triggers = self._hooks.get_triggers_for_hook(hook) payload = {} payload["headers"] = headers payload["body"] = body # Dispatch trigger instance for each of the trigger found for trigger_dict in triggers: # TODO: Instead of dispatching the whole dict we should just # dispatch TriggerDB.ref or similar self._trigger_dispatcher_service.dispatch_with_context( trigger=trigger_dict, payload=payload, trace_context=trace_context, throw_on_validation_error=True, ) # NOTE: For url encoded request bodies, values will be bytes instead of unicode and this # doesn't work with orjson so we first need to "cast" all the values from bytes to unicode return Response(json=body, status=http_client.ACCEPTED)