Merge "Add new default roles in os-instance-actions policies"
This commit is contained in:
commit
177dadc193
|
@ -84,7 +84,7 @@ class InstanceActionsController(wsgi.Controller):
|
|||
"""Returns the list of actions recorded for a given instance."""
|
||||
context = req.environ["nova.context"]
|
||||
instance = self._get_instance(req, context, server_id)
|
||||
context.can(ia_policies.BASE_POLICY_NAME,
|
||||
context.can(ia_policies.BASE_POLICY_NAME % 'list',
|
||||
target={'project_id': instance.project_id})
|
||||
actions_raw = self.action_api.actions_get(context, instance)
|
||||
actions = [self._format_action(action, ACTION_KEYS)
|
||||
|
@ -101,7 +101,7 @@ class InstanceActionsController(wsgi.Controller):
|
|||
"""Returns the list of actions recorded for a given instance."""
|
||||
context = req.environ["nova.context"]
|
||||
instance = self._get_instance(req, context, server_id)
|
||||
context.can(ia_policies.BASE_POLICY_NAME,
|
||||
context.can(ia_policies.BASE_POLICY_NAME % 'list',
|
||||
target={'project_id': instance.project_id})
|
||||
search_opts = {}
|
||||
search_opts.update(req.GET)
|
||||
|
@ -140,7 +140,7 @@ class InstanceActionsController(wsgi.Controller):
|
|||
"""Return data about the given instance action."""
|
||||
context = req.environ['nova.context']
|
||||
instance = self._get_instance(req, context, server_id)
|
||||
context.can(ia_policies.BASE_POLICY_NAME,
|
||||
context.can(ia_policies.BASE_POLICY_NAME % 'show',
|
||||
target={'project_id': instance.project_id})
|
||||
action = self.action_api.action_get_by_request_id(context, instance,
|
||||
id)
|
||||
|
@ -161,7 +161,7 @@ class InstanceActionsController(wsgi.Controller):
|
|||
show_events = False
|
||||
show_traceback = False
|
||||
show_host = False
|
||||
if context.can(ia_policies.POLICY_ROOT % 'events',
|
||||
if context.can(ia_policies.BASE_POLICY_NAME % 'events',
|
||||
target={'project_id': instance.project_id},
|
||||
fatal=False):
|
||||
# For all microversions, the user can see all event details
|
||||
|
|
|
@ -18,23 +18,31 @@ from oslo_policy import policy
|
|||
from nova.policies import base
|
||||
|
||||
|
||||
BASE_POLICY_NAME = 'os_compute_api:os-instance-actions'
|
||||
POLICY_ROOT = 'os_compute_api:os-instance-actions:%s'
|
||||
ROOT_POLICY = 'os_compute_api:os-instance-actions'
|
||||
BASE_POLICY_NAME = 'os_compute_api:os-instance-actions:%s'
|
||||
|
||||
DEPRECATED_INSTANCE_ACTION_POLICY = policy.DeprecatedRule(
|
||||
ROOT_POLICY,
|
||||
base.RULE_ADMIN_OR_OWNER,
|
||||
)
|
||||
|
||||
DEPRECATED_REASON = """
|
||||
Nova API policies are introducing new default roles with scope_type
|
||||
capabilities. Old policies are deprecated and silently going to be ignored
|
||||
in nova 23.0.0 release.
|
||||
"""
|
||||
|
||||
instance_actions_policies = [
|
||||
policy.DocumentedRuleDefault(
|
||||
name=POLICY_ROOT % 'events',
|
||||
check_str=base.RULE_ADMIN_API,
|
||||
name=BASE_POLICY_NAME % 'events',
|
||||
check_str=base.SYSTEM_READER,
|
||||
description="""Add events details in action details for a server.
|
||||
|
||||
This check is performed only after the check
|
||||
os_compute_api:os-instance-actions passes. Beginning with
|
||||
Microversion 2.51, events details are always included; traceback
|
||||
information is provided per event if policy enforcement passes.
|
||||
Beginning with Microversion 2.62, each event includes a hashed
|
||||
host identifier and, if policy enforcement passes, the name of
|
||||
the host.""",
|
||||
os_compute_api:os-instance-actions:show passes. Beginning with Microversion
|
||||
2.51, events details are always included; traceback information is provided
|
||||
per event if policy enforcement passes. Beginning with Microversion 2.62,
|
||||
each event includes a hashed host identifier and, if policy enforcement
|
||||
passes, the name of the host.""",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
|
@ -43,20 +51,33 @@ the host.""",
|
|||
],
|
||||
scope_types=['system']),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME,
|
||||
check_str=base.RULE_ADMIN_OR_OWNER,
|
||||
description="""List actions and show action details for a server.""",
|
||||
name=BASE_POLICY_NAME % 'list',
|
||||
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
|
||||
description="""List actions for a server.""",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/servers/{server_id}/os-instance-actions'
|
||||
},
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project'],
|
||||
deprecated_rule=DEPRECATED_INSTANCE_ACTION_POLICY,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since='21.0.0'),
|
||||
policy.DocumentedRuleDefault(
|
||||
name=BASE_POLICY_NAME % 'show',
|
||||
check_str=base.PROJECT_READER_OR_SYSTEM_READER,
|
||||
description="""Show action details for a server.""",
|
||||
operations=[
|
||||
{
|
||||
'method': 'GET',
|
||||
'path': '/servers/{server_id}/os-instance-actions/{request_id}'
|
||||
}
|
||||
],
|
||||
scope_types=['system', 'project']),
|
||||
scope_types=['system', 'project'],
|
||||
deprecated_rule=DEPRECATED_INSTANCE_ACTION_POLICY,
|
||||
deprecated_reason=DEPRECATED_REASON,
|
||||
deprecated_since='21.0.0'),
|
||||
]
|
||||
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
|
||||
from nova.tests.functional.api_sample_tests import test_servers
|
||||
from nova.tests.functional import api_samples_test_base
|
||||
from nova.tests.unit import policy_fixture
|
||||
|
||||
|
||||
class ServerActionsSampleJsonTest(test_servers.ServersSampleBase):
|
||||
|
@ -34,6 +35,7 @@ class ServerActionsSampleJsonTest(test_servers.ServersSampleBase):
|
|||
actions = api_samples_test_base.objectify(response_data)
|
||||
self.action_stop = actions['instanceActions'][0]
|
||||
self._wait_for_state_change({'id': self.uuid}, 'SHUTOFF')
|
||||
self.policy = self.useFixture(policy_fixture.RealPolicyFixture())
|
||||
|
||||
def _get_subs(self):
|
||||
return {
|
||||
|
|
|
@ -121,7 +121,7 @@ class InstanceActionsTestV21(test.NoDBTestCase):
|
|||
|
||||
def _set_policy_rules(self):
|
||||
rules = {'compute:get': '',
|
||||
'os_compute_api:os-instance-actions': '',
|
||||
'os_compute_api:os-instance-actions:show': '',
|
||||
'os_compute_api:os-instance-actions:events': 'is_admin:True'}
|
||||
policy.set_rules(oslo_policy.Rules.from_dict(rules))
|
||||
|
||||
|
|
|
@ -117,13 +117,16 @@ class TestPolicyCheck(test.NoDBTestCase):
|
|||
r.name for r in ia_policies.list_rules()]
|
||||
|
||||
passing_rules = self.cmd._filter_rules(
|
||||
context, 'os-instance-actions', target)
|
||||
context, 'os-instance-actions:list', target)
|
||||
passing_rules += self.cmd._filter_rules(
|
||||
context, 'os-instance-actions:show', target)
|
||||
passing_rules += self.cmd._filter_rules(
|
||||
context, 'os-instance-actions:events', target)
|
||||
self.assertEqual(set(expected_rules), set(passing_rules))
|
||||
|
||||
def test_filter_rules_non_admin(self):
|
||||
context = nova_context.RequestContext()
|
||||
rule_conditions = [base_policies.RULE_ANY,
|
||||
base_policies.RULE_ADMIN_OR_OWNER]
|
||||
rule_conditions = [base_policies.PROJECT_READER_OR_SYSTEM_READER]
|
||||
expected_rules = [r.name for r in ia_policies.list_rules() if
|
||||
r.check_str in rule_conditions]
|
||||
self._check_filter_rules(context, expected_rules=expected_rules)
|
||||
|
@ -150,8 +153,7 @@ class TestPolicyCheck(test.NoDBTestCase):
|
|||
db_context = nova_context.RequestContext(user_id='fake-user',
|
||||
project_id='fake-project')
|
||||
instance = fake_instance.fake_instance_obj(db_context)
|
||||
rule_conditions = [base_policies.RULE_ANY,
|
||||
base_policies.RULE_ADMIN_OR_OWNER]
|
||||
rule_conditions = [base_policies.PROJECT_READER_OR_SYSTEM_READER]
|
||||
expected_rules = [r.name for r in ia_policies.list_rules() if
|
||||
r.check_str in rule_conditions]
|
||||
self._check_filter_rules(db_context, instance, expected_rules)
|
||||
|
|
|
@ -49,7 +49,9 @@ policy_data = """
|
|||
"os_compute_api:os-flavor-manage:delete": "",
|
||||
"os_compute_api:os-floating-ip-pools": "",
|
||||
"os_compute_api:os-floating-ips": "",
|
||||
"os_compute_api:os-instance-actions": "",
|
||||
"os_compute_api:os-instance-actions:list": "",
|
||||
"os_compute_api:os-instance-actions:show": "",
|
||||
"os_compute_api:os-instance-actions:events": "",
|
||||
"os_compute_api:os-instance-usage-audit-log": "",
|
||||
|
||||
"os_compute_api:os-lock-server:lock": "",
|
||||
|
|
|
@ -20,12 +20,16 @@ from oslo_utils import timeutils
|
|||
|
||||
from nova.api.openstack.compute import instance_actions as instance_actions_v21
|
||||
from nova.compute import vm_states
|
||||
from nova import exception
|
||||
from nova.policies import base as base_policy
|
||||
from nova.policies import instance_actions as ia_policies
|
||||
from nova import policy
|
||||
from nova.tests.unit.api.openstack.compute import test_instance_actions
|
||||
from nova.tests.unit.api.openstack import fakes
|
||||
from nova.tests.unit import fake_instance
|
||||
from nova.tests.unit import fake_server_actions
|
||||
from nova.tests.unit.policies import base
|
||||
from nova.tests.unit import policy_fixture
|
||||
|
||||
FAKE_UUID = fake_server_actions.FAKE_UUID
|
||||
FAKE_REQUEST_ID = fake_server_actions.FAKE_REQUEST_ID1
|
||||
|
@ -57,68 +61,61 @@ class InstanceActionsPolicyTest(base.BasePolicyTest):
|
|||
task_state=None, launched_at=timeutils.utcnow())
|
||||
self.mock_get.return_value = self.instance
|
||||
|
||||
# Check that admin or owner is able to list/show
|
||||
# instance actions.
|
||||
self.admin_or_owner_authorized_contexts = [
|
||||
# Check that system reader are able to show the instance
|
||||
# actions events.
|
||||
self.system_reader_authorized_contexts = [
|
||||
self.system_admin_context, self.system_member_context,
|
||||
self.system_reader_context, self.legacy_admin_context,
|
||||
self.project_admin_context]
|
||||
# Check that non-system-reader are not able to show the instance
|
||||
# actions events.
|
||||
self.system_reader_unauthorized_contexts = [
|
||||
self.system_foo_context, self.other_project_member_context,
|
||||
self.project_foo_context, self.project_member_context,
|
||||
self.project_reader_context]
|
||||
|
||||
self.project_or_system_reader_authorized_contexts = [
|
||||
self.legacy_admin_context, self.system_admin_context,
|
||||
self.project_admin_context, self.project_foo_context,
|
||||
self.project_reader_context, self.project_member_context
|
||||
self.project_admin_context, self.system_member_context,
|
||||
self.system_reader_context, self.project_reader_context,
|
||||
self.project_member_context, self.project_foo_context
|
||||
]
|
||||
|
||||
self.admin_or_owner_unauthorized_contexts = [
|
||||
self.system_member_context, self.system_reader_context,
|
||||
self.project_or_system_reader_unauthorized_contexts = [
|
||||
self.system_foo_context,
|
||||
self.other_project_member_context
|
||||
]
|
||||
|
||||
# Check that admin is able to show the instance actions
|
||||
# events.
|
||||
self.admin_authorized_contexts = [
|
||||
self.legacy_admin_context,
|
||||
self.system_admin_context,
|
||||
self.project_admin_context
|
||||
]
|
||||
# Check that non-admin is not able to show the instance
|
||||
# actions events.
|
||||
self.admin_unauthorized_contexts = [
|
||||
self.system_member_context,
|
||||
self.system_reader_context,
|
||||
self.system_foo_context,
|
||||
self.project_member_context,
|
||||
self.other_project_member_context,
|
||||
self.project_foo_context,
|
||||
self.project_reader_context
|
||||
]
|
||||
|
||||
def _set_policy_rules(self, overwrite=True):
|
||||
rules = {'os_compute_api:os-instance-actions': '@'}
|
||||
rules = {ia_policies.BASE_POLICY_NAME % 'show': '@'}
|
||||
policy.set_rules(oslo_policy.Rules.from_dict(rules),
|
||||
overwrite=overwrite)
|
||||
|
||||
def test_index_instance_action_policy(self):
|
||||
rule_name = "os_compute_api:os-instance-actions"
|
||||
self.common_policy_check(self.admin_or_owner_authorized_contexts,
|
||||
self.admin_or_owner_unauthorized_contexts,
|
||||
rule_name, self.controller.index,
|
||||
self.req, self.instance['uuid'])
|
||||
rule_name = ia_policies.BASE_POLICY_NAME % "list"
|
||||
self.common_policy_check(
|
||||
self.project_or_system_reader_authorized_contexts,
|
||||
self.project_or_system_reader_unauthorized_contexts,
|
||||
rule_name, self.controller.index,
|
||||
self.req, self.instance['uuid'])
|
||||
|
||||
@mock.patch('nova.compute.api.InstanceActionAPI.action_get_by_request_id')
|
||||
def test_show_instance_action_policy(self, mock_action_get):
|
||||
fake_action = self.fake_actions[FAKE_UUID][FAKE_REQUEST_ID]
|
||||
mock_action_get.return_value = fake_action
|
||||
rule_name = "os_compute_api:os-instance-actions"
|
||||
self.common_policy_check(self.admin_or_owner_authorized_contexts,
|
||||
self.admin_or_owner_unauthorized_contexts,
|
||||
rule_name, self.controller.show,
|
||||
self.req, self.instance['uuid'],
|
||||
fake_action['request_id'])
|
||||
rule_name = ia_policies.BASE_POLICY_NAME % "show"
|
||||
self.common_policy_check(
|
||||
self.project_or_system_reader_authorized_contexts,
|
||||
self.project_or_system_reader_unauthorized_contexts,
|
||||
rule_name, self.controller.show,
|
||||
self.req, self.instance['uuid'], fake_action['request_id'])
|
||||
|
||||
@mock.patch('nova.objects.InstanceActionEventList.get_by_action')
|
||||
@mock.patch('nova.objects.InstanceAction.get_by_request_id')
|
||||
def test_show_instance_action_policy_with_events(
|
||||
self, mock_get_action, mock_get_events):
|
||||
"""Test to ensure skip checking policy rule
|
||||
os_compute_api:os-instance-actions.
|
||||
'os_compute_api:os-instance-actions:show'.
|
||||
"""
|
||||
fake_action = self.fake_actions[FAKE_UUID][FAKE_REQUEST_ID]
|
||||
mock_get_action.return_value = fake_action
|
||||
|
@ -129,10 +126,12 @@ class InstanceActionsPolicyTest(base.BasePolicyTest):
|
|||
copy.deepcopy(fake_action))
|
||||
|
||||
self._set_policy_rules(overwrite=False)
|
||||
rule_name = "os_compute_api:os-instance-actions:events"
|
||||
rule_name = ia_policies.BASE_POLICY_NAME % "events"
|
||||
authorize_res, unauthorize_res = self.common_policy_check(
|
||||
self.admin_authorized_contexts, self.admin_unauthorized_contexts,
|
||||
rule_name, self.controller.show, self.req, self.instance['uuid'],
|
||||
self.system_reader_authorized_contexts,
|
||||
self.system_reader_unauthorized_contexts,
|
||||
rule_name, self.controller.show,
|
||||
self.req, self.instance['uuid'],
|
||||
fake_action['request_id'], fatal=False)
|
||||
|
||||
for action in authorize_res:
|
||||
|
@ -146,6 +145,62 @@ class InstanceActionsPolicyTest(base.BasePolicyTest):
|
|||
self.assertNotIn('events', action['instanceAction'])
|
||||
|
||||
|
||||
class InstanceActionsDeprecatedPolicyTest(base.BasePolicyTest):
|
||||
"""Test os-instance-actions APIs Deprecated policies.
|
||||
This lass checks if deprecated policy rules are overridden
|
||||
by user on policy.json file then they still work because
|
||||
oslo.policy add deprecated rules in logical OR condition
|
||||
and enforce them for policy checks if overridden.
|
||||
"""
|
||||
|
||||
def setUp(self):
|
||||
super(InstanceActionsDeprecatedPolicyTest, self).setUp()
|
||||
self.controller = instance_actions_v21.InstanceActionsController()
|
||||
self.admin_or_owner_req = fakes.HTTPRequest.blank('')
|
||||
self.admin_or_owner_req.environ[
|
||||
'nova.context'] = self.project_admin_context
|
||||
self.reader_req = fakes.HTTPRequest.blank('')
|
||||
self.reader_req.environ['nova.context'] = self.project_reader_context
|
||||
self.deprecated_policy = ia_policies.ROOT_POLICY
|
||||
# Overridde rule with different checks than defaults so that we can
|
||||
# verify the rule overridden case.
|
||||
override_rules = {self.deprecated_policy:
|
||||
base_policy.RULE_ADMIN_OR_OWNER}
|
||||
# NOTE(brinzhang): Only override the deprecated rule in policy file
|
||||
# so that we can verify if overridden checks are considered by
|
||||
# oslo.policy.
|
||||
# Oslo.policy will consider the overridden rules if:
|
||||
# 1. overridden deprecated rule's checks are different than defaults
|
||||
# 2. new rules are not present in policy file
|
||||
self.policy = self.useFixture(policy_fixture.OverridePolicyFixture(
|
||||
rules_in_file=override_rules))
|
||||
|
||||
@mock.patch('nova.compute.api.InstanceActionAPI.actions_get')
|
||||
@mock.patch('nova.api.openstack.common.get_instance')
|
||||
def test_deprecated_policy_overridden_rule_is_checked(
|
||||
self, mock_instance_get, mock_actions_get):
|
||||
# Test to verify if deprecatd overridden policy is working.
|
||||
|
||||
instance = fake_instance.fake_instance_obj(
|
||||
self.admin_or_owner_req.environ['nova.context'])
|
||||
|
||||
# Check for success as admin_or_owner role. Deprecated rule
|
||||
# has been overridden with admin checks in policy.json
|
||||
# If admin role pass it means overridden rule is enforced by
|
||||
# olso.policy because new default is system reader and the old
|
||||
# default is admin.
|
||||
self.controller.index(self.admin_or_owner_req, instance['uuid'])
|
||||
|
||||
# check for failure with reader context.
|
||||
exc = self.assertRaises(exception.PolicyNotAuthorized,
|
||||
self.controller.index,
|
||||
self.reader_req,
|
||||
instance['uuid'])
|
||||
self.assertEqual(
|
||||
"Policy doesn't allow os_compute_api:os-instance-actions:list "
|
||||
"to be performed.", exc.format_message())
|
||||
|
||||
|
||||
class InstanceActionsScopeTypePolicyTest(InstanceActionsPolicyTest):
|
||||
"""Test os-instance-actions APIs policies with system scope enabled.
|
||||
|
||||
|
@ -161,16 +216,65 @@ class InstanceActionsScopeTypePolicyTest(InstanceActionsPolicyTest):
|
|||
super(InstanceActionsScopeTypePolicyTest, self).setUp()
|
||||
self.flags(enforce_scope=True, group="oslo_policy")
|
||||
|
||||
# Check that system admin is able to get the
|
||||
# Check that system reader is able to get the
|
||||
# instance action events
|
||||
self.admin_authorized_contexts = [
|
||||
self.system_admin_context]
|
||||
# Check that non-system or non-admin is not able to
|
||||
self.system_reader_authorized_contexts = [
|
||||
self.system_admin_context, self.system_member_context,
|
||||
self.system_reader_context]
|
||||
# Check that non-system-reader is not able to
|
||||
# get the instance action events
|
||||
self.admin_unauthorized_contexts = [
|
||||
self.legacy_admin_context, self.system_member_context,
|
||||
self.system_reader_context, self.system_foo_context,
|
||||
self.system_reader_unauthorized_contexts = [
|
||||
self.system_foo_context, self.legacy_admin_context,
|
||||
self.project_admin_context, self.project_member_context,
|
||||
self.other_project_member_context,
|
||||
self.project_foo_context, self.project_reader_context
|
||||
]
|
||||
|
||||
|
||||
class InstanceActionsNoLegacyPolicyTest(InstanceActionsPolicyTest):
|
||||
"""Test os-instance-actions APIs policies with system scope enabled,
|
||||
and no more deprecated rules that allow the legacy admin API to
|
||||
access system_admin_or_owner APIs.
|
||||
"""
|
||||
without_deprecated_rules = True
|
||||
rules_without_deprecation = {
|
||||
ia_policies.BASE_POLICY_NAME % 'list':
|
||||
base_policy.PROJECT_READER_OR_SYSTEM_READER,
|
||||
ia_policies.BASE_POLICY_NAME % 'show':
|
||||
base_policy.PROJECT_READER_OR_SYSTEM_READER,
|
||||
ia_policies.BASE_POLICY_NAME % 'events':
|
||||
base_policy.SYSTEM_READER}
|
||||
|
||||
def setUp(self):
|
||||
super(InstanceActionsNoLegacyPolicyTest, self).setUp()
|
||||
self.flags(enforce_scope=True, group="oslo_policy")
|
||||
|
||||
# Check that system reader are able to get the
|
||||
# instance action events.
|
||||
self.system_reader_authorized_contexts = [
|
||||
self.system_admin_context, self.system_reader_context,
|
||||
self.system_member_context]
|
||||
# Check that non-system-reader are not able to
|
||||
# get the instance action events
|
||||
self.system_reader_unauthorized_contexts = [
|
||||
self.project_admin_context,
|
||||
self.system_foo_context, self.legacy_admin_context,
|
||||
self.other_project_member_context,
|
||||
self.project_foo_context, self.project_member_context,
|
||||
self.project_reader_context]
|
||||
|
||||
# Check that system or projct reader is able to
|
||||
# show the instance actions events.
|
||||
self.project_or_system_reader_authorized_contexts = [
|
||||
self.system_admin_context,
|
||||
self.project_admin_context, self.system_member_context,
|
||||
self.system_reader_context, self.project_reader_context,
|
||||
self.project_member_context,
|
||||
]
|
||||
|
||||
# Check that non-system or non-project reader is not able to
|
||||
# show the instance actions events.
|
||||
self.project_or_system_reader_unauthorized_contexts = [
|
||||
self.legacy_admin_context, self.project_foo_context,
|
||||
self.system_foo_context, self.other_project_member_context
|
||||
]
|
||||
|
|
|
@ -426,7 +426,6 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
|||
"os_compute_api:os-flavor-extra-specs:show",
|
||||
"os_compute_api:os-floating-ip-pools",
|
||||
"os_compute_api:os-floating-ips",
|
||||
"os_compute_api:os-instance-actions",
|
||||
"os_compute_api:limits",
|
||||
"os_compute_api:os-multinic",
|
||||
"os_compute_api:os-networks:view",
|
||||
|
@ -464,6 +463,8 @@ class RealRolePolicyTestCase(test.NoDBTestCase):
|
|||
"os_compute_api:os-volumes-attachments:show",
|
||||
"os_compute_api:os-attach-interfaces:list",
|
||||
"os_compute_api:os-attach-interfaces:show",
|
||||
"os_compute_api:os-instance-actions:list",
|
||||
"os_compute_api:os-instance-actions:show",
|
||||
)
|
||||
|
||||
self.allow_nobody_rules = (
|
||||
|
|
Loading…
Reference in New Issue