# Copyright (c) 2026 Thomas Goirand <zigo@debian.org>
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#    http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
# implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from oslo_config import cfg
from oslo_policy import opts
import testtools

from vmms.policy import enforcer


class TestPolicyRules(testtools.TestCase):

    ACTIONS = [
        'vmms:add',
        'vmms:remove',
        'vmms:update',
        'vmms:list',
        'vmms:show',
        'vmms:output',
    ]

    WRITE_ACTIONS = [
        'vmms:add',
        'vmms:remove',
        'vmms:update',
    ]

    READ_ACTIONS = [
        'vmms:list',
        'vmms:show',
        'vmms:output',
    ]

    def setUp(self):
        super().setUp()

        self.conf = cfg.ConfigOpts()

        # oslo.policy expects its options to be registered on the
        # ConfigOpts instance before Enforcer is created.
        opts.set_defaults(self.conf)

        self.conf([])

        # The VMMS enforcer is global, so reset it between tests.
        enforcer._ENFORCER = None
        self.addCleanup(self._reset_enforcer)

    def _reset_enforcer(self):
        enforcer._ENFORCER = None

    def _enforcer(self):
        return enforcer.get_enforcer(self.conf)

    def _enforce(self, action, roles):
        target = {
            'project_id': 'project-id',
            'user_id': 'user-id',
        }

        creds = {
            'user_id': 'user-id',
            'project_id': 'project-id',
            'roles': roles,
        }

        return self._enforcer().enforce(
            action,
            target,
            creds,
        )

    def test_admin_can_perform_all_actions(self):
        for action in self.ACTIONS:
            self.assertTrue(
                self._enforce(action, ['admin']),
                'admin should be allowed to perform %s' % action,
            )

    def test_operator_can_perform_all_actions(self):
        for action in self.ACTIONS:
            self.assertTrue(
                self._enforce(action, ['vmms_operator']),
                'vmms_operator should be allowed to perform %s' % action,
            )

    def test_viewer_can_read(self):
        for action in self.READ_ACTIONS:
            self.assertTrue(
                self._enforce(action, ['vmms_viewer']),
                'vmms_viewer should be allowed to perform %s' % action,
            )

    def test_viewer_cannot_write(self):
        for action in self.WRITE_ACTIONS:
            self.assertFalse(
                self._enforce(action, ['vmms_viewer']),
                'vmms_viewer should NOT be allowed to perform %s' % action,
            )

    def test_unrelated_role_cannot_perform_any_action(self):
        for action in self.ACTIONS:
            self.assertFalse(
                self._enforce(action, ['member']),
                'member should NOT be allowed to perform %s' % action,
            )

    def test_no_roles_cannot_perform_any_action(self):
        for action in self.ACTIONS:
            self.assertFalse(
                self._enforce(action, []),
                'user without roles should NOT be allowed to perform %s'
                % action,
            )

    def test_viewer_and_operator_can_perform_all_actions(self):
        roles = ['vmms_viewer', 'vmms_operator']

        for action in self.ACTIONS:
            self.assertTrue(
                self._enforce(action, roles),
                'combined viewer/operator roles should allow %s' % action,
            )

    def test_admin_with_unrelated_roles_can_perform_all_actions(self):
        roles = ['admin', 'member']

        for action in self.ACTIONS:
            self.assertTrue(
                self._enforce(action, roles),
                'admin should allow %s despite unrelated roles' % action,
            )
