summaryrefslogtreecommitdiff
path: root/trove/extensions
diff options
context:
space:
mode:
authorPetr Malik <pmalik@tesora.com>2016-06-27 16:01:42 -0400
committerPetr Malik <pmalik@tesora.com>2016-12-06 21:51:21 +0000
commit21250cf20c0efbe6d57c4a712c51b80667e53b44 (patch)
treed18e6ee84986b798e7654e254a2a6894dc8d54f4 /trove/extensions
parent77fd7014c0007c83652dd4fb1f9d3316a97b1ed3 (diff)
downloadtrove-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.py38
-rw-r--r--trove/extensions/mysql/service.py28
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()