blob: 420de7f1a83f8105b28889c3316ca67267ec6ad4 [file] [log] [blame]
DavidPurcellb25f93d2017-01-27 12:46:27 -05001# Copyright 2017 AT&T Corporation.
DavidPurcell029d8c32017-01-06 15:27:41 -05002# 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
Felipe Monteirob0595652017-01-23 16:51:58 -050016import copy
DavidPurcell029d8c32017-01-06 15:27:41 -050017import os
18
DavidPurcell029d8c32017-01-06 15:27:41 -050019from oslo_log import log as logging
DavidPurcell029d8c32017-01-06 15:27:41 -050020from oslo_policy import policy
21from tempest import config
22
Felipe Monteirob0595652017-01-23 16:51:58 -050023from patrole_tempest_plugin import rbac_exceptions
DavidPurcell029d8c32017-01-06 15:27:41 -050024
25CONF = config.CONF
26LOG = logging.getLogger(__name__)
27
DavidPurcell029d8c32017-01-06 15:27:41 -050028
29class RbacPolicyConverter(object):
30 """A class for parsing policy rules into lists of allowed roles.
31
32 RBAC testing requires that each rule in a policy file be broken up into
33 the roles that constitute it. This class automates that process.
Felipe Monteirob0595652017-01-23 16:51:58 -050034
35 The list of roles per rule can be reverse-engineered by checking, for
36 each role, whether a given rule is allowed using oslo policy.
DavidPurcell029d8c32017-01-06 15:27:41 -050037 """
38
Felipe Monteirob0595652017-01-23 16:51:58 -050039 def __init__(self, tenant_id, service, path=None):
40 """Initialization of Policy Converter.
DavidPurcell029d8c32017-01-06 15:27:41 -050041
Felipe Monteirob0595652017-01-23 16:51:58 -050042 Parse policy files to create dictionary mapping policy actions to
43 roles.
44
45 :param tenant_id: type uuid
DavidPurcell029d8c32017-01-06 15:27:41 -050046 :param service: type string
47 :param path: type string
48 """
DavidPurcell029d8c32017-01-06 15:27:41 -050049 if path is None:
Felipe Monteirob0595652017-01-23 16:51:58 -050050 self.path = '/etc/{0}/policy.json'.format(service)
51 else:
52 self.path = path
DavidPurcell029d8c32017-01-06 15:27:41 -050053
Felipe Monteirob0595652017-01-23 16:51:58 -050054 if not os.path.isfile(self.path):
55 raise rbac_exceptions.RbacResourceSetupFailed(
56 'Policy file for service: {0}, {1} not found.'
57 .format(service, self.path))
DavidPurcell029d8c32017-01-06 15:27:41 -050058
Felipe Monteirob0595652017-01-23 16:51:58 -050059 self.tenant_id = tenant_id
DavidPurcell029d8c32017-01-06 15:27:41 -050060
Felipe Monteirob0595652017-01-23 16:51:58 -050061 def allowed(self, rule_name, role):
62 policy_file = open(self.path, 'r')
63 access_token = self._get_access_token(role)
DavidPurcell029d8c32017-01-06 15:27:41 -050064
Felipe Monteirob0595652017-01-23 16:51:58 -050065 is_allowed = self._allowed(
66 policy_file=policy_file,
67 access=access_token,
68 apply_rule=rule_name,
69 is_admin=False)
DavidPurcell029d8c32017-01-06 15:27:41 -050070
Felipe Monteirob0595652017-01-23 16:51:58 -050071 policy_file = open(self.path, 'r')
72 access_token = self._get_access_token(role)
73 allowed_as_admin_context = self._allowed(
74 policy_file=policy_file,
75 access=access_token,
76 apply_rule=rule_name,
77 is_admin=True)
DavidPurcell029d8c32017-01-06 15:27:41 -050078
Felipe Monteirob0595652017-01-23 16:51:58 -050079 if allowed_as_admin_context and is_allowed:
80 return True
81 if allowed_as_admin_context and not is_allowed:
82 return False
83 if not allowed_as_admin_context and is_allowed:
84 return True
85 if not allowed_as_admin_context and not is_allowed:
86 return False
DavidPurcell029d8c32017-01-06 15:27:41 -050087
Felipe Monteirob0595652017-01-23 16:51:58 -050088 def _get_access_token(self, role):
89 access_token = {
90 "token": {
91 "roles": [
92 {
93 "name": role
94 }
95 ],
96 "project": {
97 "id": self.tenant_id
98 }
99 }
100 }
101 return access_token
DavidPurcell029d8c32017-01-06 15:27:41 -0500102
Felipe Monteirob0595652017-01-23 16:51:58 -0500103 def _allowed(self, policy_file, access, apply_rule, is_admin=False):
104 """Checks if a given rule in a policy is allowed with given access.
DavidPurcell029d8c32017-01-06 15:27:41 -0500105
Felipe Monteirob0595652017-01-23 16:51:58 -0500106 Adapted from oslo_policy.shell.
DavidPurcell029d8c32017-01-06 15:27:41 -0500107
Felipe Monteirob0595652017-01-23 16:51:58 -0500108 :param policy file: type string: path to policy file
109 :param access: type dict: dictionary from ``_get_access_token``
110 :param apply_rule: type string: rule to be checked
111 :param is_admin: type bool: whether admin context is used
DavidPurcell029d8c32017-01-06 15:27:41 -0500112 """
Felipe Monteirob0595652017-01-23 16:51:58 -0500113 access_data = copy.copy(access['token'])
114 access_data['roles'] = [role['name'] for role in access_data['roles']]
115 access_data['project_id'] = access_data['project']['id']
116 access_data['is_admin'] = is_admin
117 policy_data = policy_file.read()
118 rules = policy.Rules.load(policy_data, "default")
DavidPurcell029d8c32017-01-06 15:27:41 -0500119
Felipe Monteirob0595652017-01-23 16:51:58 -0500120 class Object(object):
121 pass
122 o = Object()
123 o.rules = rules
DavidPurcell029d8c32017-01-06 15:27:41 -0500124
Felipe Monteirob0595652017-01-23 16:51:58 -0500125 target = {"project_id": access_data['project_id']}
126
127 key = apply_rule
128 rule = rules[apply_rule]
129 result = self._try_rule(key, rule, target, access_data, o)
130 return result
131
132 def _try_rule(self, key, rule, target, access_data, o):
133 try:
134 return rule(target, access_data, o)
135 except Exception as e:
136 LOG.debug("Exception: {0} for rule: {1}".format(e, rule))
137 return False