diff options
author | Petr Malik <pmalik@tesora.com> | 2016-06-27 16:01:42 -0400 |
---|---|---|
committer | Petr Malik <pmalik@tesora.com> | 2016-12-06 21:51:21 +0000 |
commit | 21250cf20c0efbe6d57c4a712c51b80667e53b44 (patch) | |
tree | d18e6ee84986b798e7654e254a2a6894dc8d54f4 /trove/extensions | |
parent | 77fd7014c0007c83652dd4fb1f9d3316a97b1ed3 (diff) | |
download | trove-21250cf20c0efbe6d57c4a712c51b80667e53b44.tar.gz |
Add support for Oslo Policies to Trove
The Oslo Policy library provides support for RBAC policy
enforcement across all OpenStack services.
Update the devstack plugin to copy the default policy file
over to /etc/trove in the gate environments.
Note: Not adding a rule for 'reset-password' instance
action as that API was discontinued years ago
and is now just waiting for removal (Bug: 1645866).
DocImpact
Co-Authored-By: Ali Adil <aadil@tesora.com>
Change-Id: Ic443a4c663301840406cad537159eab7b0b5ed1c
Implements: blueprint trove-policy
Diffstat (limited to 'trove/extensions')
-rw-r--r-- | trove/extensions/common/service.py | 38 | ||||
-rw-r--r-- | trove/extensions/mysql/service.py | 28 |
2 files changed, 61 insertions, 5 deletions
diff --git a/trove/extensions/common/service.py b/trove/extensions/common/service.py index 8120b48c..78669266 100644 --- a/trove/extensions/common/service.py +++ b/trove/extensions/common/service.py @@ -21,14 +21,17 @@ from oslo_log import log as logging from oslo_utils import importutils import six +from trove.cluster import models as cluster_models from trove.cluster.models import DBCluster from trove.common import cfg from trove.common import exception from trove.common.i18n import _LI +from trove.common import policy from trove.common import wsgi from trove.datastore import models as datastore_models from trove.extensions.common import models from trove.extensions.common import views +from trove.instance import models as instance_models from trove.instance.models import DBInstance @@ -37,8 +40,30 @@ import_class = importutils.import_class CONF = cfg.CONF +class ExtensionController(wsgi.Controller): + + @classmethod + def authorize_target_action(cls, context, target_rule_name, + target_id, is_cluster=False): + target = None + if is_cluster: + target = cluster_models.Cluster.load(context, target_id) + else: + target = instance_models.Instance.load(context, target_id) + + if not target: + if is_cluster: + raise exception.ClusterNotFound(cluster=target_id) + raise exception.InstanceNotFound(instance=target_id) + + target_type = 'cluster' if is_cluster else 'instance' + policy.authorize_on_target( + context, '%s:extension:%s' % (target_type, target_rule_name), + {'tenant': target.tenant_id}) + + @six.add_metaclass(abc.ABCMeta) -class BaseDatastoreRootController(wsgi.Controller): +class BaseDatastoreRootController(ExtensionController): """Base class that defines the contract for root controllers.""" @abc.abstractmethod @@ -174,13 +199,16 @@ class ClusterRootController(DefaultRootController): return single_instance_id, instance_ids -class RootController(wsgi.Controller): +class RootController(ExtensionController): """Controller for instance functionality.""" def index(self, req, tenant_id, instance_id): """Returns True if root is enabled; False otherwise.""" datastore_manager, is_cluster = self._get_datastore(tenant_id, instance_id) + context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'root:index', instance_id, + is_cluster=is_cluster) root_controller = self.load_root_controller(datastore_manager) return root_controller.root_index(req, tenant_id, instance_id, is_cluster) @@ -189,6 +217,9 @@ class RootController(wsgi.Controller): """Enable the root user for the db instance.""" datastore_manager, is_cluster = self._get_datastore(tenant_id, instance_id) + context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'root:create', instance_id, + is_cluster=is_cluster) root_controller = self.load_root_controller(datastore_manager) if root_controller is not None: return root_controller.root_create(req, body, tenant_id, @@ -199,6 +230,9 @@ class RootController(wsgi.Controller): def delete(self, req, tenant_id, instance_id): datastore_manager, is_cluster = self._get_datastore(tenant_id, instance_id) + context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'root:delete', instance_id, + is_cluster=is_cluster) root_controller = self.load_root_controller(datastore_manager) if root_controller is not None: return root_controller.root_delete(req, tenant_id, diff --git a/trove/extensions/mysql/service.py b/trove/extensions/mysql/service.py index 8ea01954..fb444ede 100644 --- a/trove/extensions/mysql/service.py +++ b/trove/extensions/mysql/service.py @@ -30,6 +30,7 @@ from trove.common import pagination from trove.common.utils import correct_id_with_req from trove.common import wsgi from trove.extensions.common.service import DefaultRootController +from trove.extensions.common.service import ExtensionController from trove.extensions.mysql.common import populate_users from trove.extensions.mysql.common import populate_validated_databases from trove.extensions.mysql.common import unquote_user_host @@ -42,7 +43,7 @@ import_class = importutils.import_class CONF = cfg.CONF -class UserController(wsgi.Controller): +class UserController(ExtensionController): """Controller for instance functionality.""" schemas = apischema.user @@ -60,6 +61,7 @@ class UserController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'user:index', instance_id) users, next_marker = models.Users.load(context, instance_id) view = views.UsersView(users) paged = pagination.SimplePaginatedDataView(req.url, 'users', view, @@ -75,6 +77,7 @@ class UserController(wsgi.Controller): "req": strutils.mask_password(req), "body": strutils.mask_password(body)}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'user:create', instance_id) context.notification = notification.DBaaSUserCreate(context, request=req) users = body['users'] @@ -94,6 +97,7 @@ class UserController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'user:delete', instance_id) id = correct_id_with_req(id, req) username, host = unquote_user_host(id) user = None @@ -122,6 +126,7 @@ class UserController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'user:show', instance_id) id = correct_id_with_req(id, req) username, host = unquote_user_host(id) user = None @@ -141,6 +146,7 @@ class UserController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": strutils.mask_password(req)}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'user:update', instance_id) id = correct_id_with_req(id, req) username, hostname = unquote_user_host(id) user = None @@ -171,6 +177,7 @@ class UserController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": strutils.mask_password(req)}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action(context, 'user:update_all', instance_id) context.notification = notification.DBaaSUserChangePassword( context, request=req) users = body['users'] @@ -203,7 +210,7 @@ class UserController(wsgi.Controller): return wsgi.Result(None, 202) -class UserAccessController(wsgi.Controller): +class UserAccessController(ExtensionController): """Controller for adding and removing database access for a user.""" schemas = apischema.user @@ -232,6 +239,8 @@ class UserAccessController(wsgi.Controller): {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'user_access:index', instance_id) # Make sure this user exists. user_id = correct_id_with_req(user_id, req) user = self._get_user(context, instance_id, user_id) @@ -249,6 +258,8 @@ class UserAccessController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'user_access:update', instance_id) context.notification = notification.DBaaSUserGrant( context, request=req) user_id = correct_id_with_req(user_id, req) @@ -270,6 +281,8 @@ class UserAccessController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'user_access:delete', instance_id) context.notification = notification.DBaaSUserRevoke( context, request=req) user_id = correct_id_with_req(user_id, req) @@ -288,7 +301,7 @@ class UserAccessController(wsgi.Controller): return wsgi.Result(None, 202) -class SchemaController(wsgi.Controller): +class SchemaController(ExtensionController): """Controller for instance functionality.""" schemas = apischema.dbschema @@ -299,6 +312,8 @@ class SchemaController(wsgi.Controller): {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'database:index', instance_id) schemas, next_marker = models.Schemas.load(context, instance_id) view = views.SchemasView(schemas) paged = pagination.SimplePaginatedDataView(req.url, 'databases', view, @@ -315,6 +330,8 @@ class SchemaController(wsgi.Controller): "body": body}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'database:create', instance_id) schemas = body['databases'] context.notification = notification.DBaaSDatabaseCreate(context, request=req) @@ -334,6 +351,8 @@ class SchemaController(wsgi.Controller): "req : '%(req)s'\n\n") % {"id": instance_id, "req": req}) context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'database:delete', instance_id) context.notification = notification.DBaaSDatabaseDelete( context, request=req) with StartNotification(context, instance_id=instance_id, dbname=id): @@ -349,6 +368,9 @@ class SchemaController(wsgi.Controller): return wsgi.Result(None, 202) def show(self, req, tenant_id, instance_id, id): + context = req.environ[wsgi.CONTEXT_KEY] + self.authorize_target_action( + context, 'database:show', instance_id) raise webob.exc.HTTPNotImplemented() |