mirror of https://github.com/veops/cmdb.git
update acl
This commit is contained in:
parent
82a402883b
commit
cb0eb7eadd
12
Dockerfile
12
Dockerfile
|
@ -18,19 +18,19 @@ COPY --from=builder /data/apps/cmdb-ui/dist /etc/nginx/html/
|
||||||
|
|
||||||
|
|
||||||
# ================================= API ================================
|
# ================================= API ================================
|
||||||
FROM centos:7.6.1810 AS cmdb-api
|
FROM python:3.7-alpine AS cmdb-api
|
||||||
|
|
||||||
LABEL description="Python2.7.5,cmdb"
|
LABEL description="Python3.7,cmdb"
|
||||||
|
|
||||||
COPY . /data/apps/cmdb
|
COPY . /data/apps/cmdb
|
||||||
|
|
||||||
WORKDIR /data/apps/cmdb
|
WORKDIR /data/apps/cmdb
|
||||||
|
|
||||||
RUN yum install -y epel-release && yum clean all \
|
RUN apk add --no-cache gcc musl-dev libffi-dev
|
||||||
&& yum install -y python-pip \
|
|
||||||
&& pip install --no-cache-dir -r docs/requirements.txt \
|
RUN pip install --no-cache-dir -r docs/requirements.txt \
|
||||||
&& cp ./api/settings.py.example ./api/settings.py \
|
&& cp ./api/settings.py.example ./api/settings.py \
|
||||||
&& sed -i "s#{user}:{password}@127.0.0.1:3306/{db}#cmdb:123456@mysql:3306/cmdb#g" api/settings.py \
|
&& sed -i "s#{user}:{password}@127.0.0.1:3306/{db}#cmdb:123456@mysql:3306/cmdb#g" api/settings.py \
|
||||||
&& sed -i "s/127.0.0.1/redis/g" api/settings.py
|
&& sed -i "s/127.0.0.1/redis/g" api/settings.py
|
||||||
|
|
||||||
CMD ["bash", "-c", "flask run"]
|
CMD ["bash", "-c", "flask run"]
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
from api.extensions import cache
|
from api.extensions import cache
|
||||||
|
from api.models.acl import Permission
|
||||||
from api.models.acl import Role
|
from api.models.acl import Role
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,8 +47,8 @@ class RoleRelationCache(object):
|
||||||
def get_parent_ids(cls, rid):
|
def get_parent_ids(cls, rid):
|
||||||
parent_ids = cache.get(cls.PREFIX_PARENT.format(rid))
|
parent_ids = cache.get(cls.PREFIX_PARENT.format(rid))
|
||||||
if not parent_ids:
|
if not parent_ids:
|
||||||
from api.lib.perm.acl.role import RoleCRUD
|
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||||
parent_ids = RoleCRUD.get_parent_ids(rid)
|
parent_ids = RoleRelationCRUD.get_parent_ids(rid)
|
||||||
cache.set(cls.PREFIX_PARENT.format(rid), parent_ids, timeout=0)
|
cache.set(cls.PREFIX_PARENT.format(rid), parent_ids, timeout=0)
|
||||||
|
|
||||||
return parent_ids
|
return parent_ids
|
||||||
|
@ -56,8 +57,8 @@ class RoleRelationCache(object):
|
||||||
def get_child_ids(cls, rid):
|
def get_child_ids(cls, rid):
|
||||||
child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid))
|
child_ids = cache.get(cls.PREFIX_CHILDREN.format(rid))
|
||||||
if not child_ids:
|
if not child_ids:
|
||||||
from api.lib.perm.acl.role import RoleCRUD
|
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||||
child_ids = RoleCRUD.get_child_ids(rid)
|
child_ids = RoleRelationCRUD.get_child_ids(rid)
|
||||||
cache.set(cls.PREFIX_CHILDREN.format(rid), child_ids, timeout=0)
|
cache.set(cls.PREFIX_CHILDREN.format(rid), child_ids, timeout=0)
|
||||||
|
|
||||||
return child_ids
|
return child_ids
|
||||||
|
@ -89,3 +90,25 @@ class RoleRelationCache(object):
|
||||||
cache.delete(cls.PREFIX_PARENT.format(rid))
|
cache.delete(cls.PREFIX_PARENT.format(rid))
|
||||||
cache.delete(cls.PREFIX_CHILDREN.format(rid))
|
cache.delete(cls.PREFIX_CHILDREN.format(rid))
|
||||||
cache.delete(cls.PREFIX_RESOURCES.format(rid))
|
cache.delete(cls.PREFIX_RESOURCES.format(rid))
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionCache(object):
|
||||||
|
PREFIX_ID = "Permission::id::{0}"
|
||||||
|
PREFIX_NAME = "Permission::name::{0}"
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def get(cls, key):
|
||||||
|
perm = cache.get(cls.PREFIX_ID.format(key))
|
||||||
|
perm = perm or cache.get(cls.PREFIX_NAME.format(key))
|
||||||
|
if perm is None:
|
||||||
|
perm = Permission.get_by_id(key)
|
||||||
|
perm = perm or Permission.get_by(name=key, first=True, to_dict=False)
|
||||||
|
if perm is not None:
|
||||||
|
cache.set(cls.PREFIX_ID.format(key), perm)
|
||||||
|
|
||||||
|
return perm
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def clean(cls, key):
|
||||||
|
cache.delete(cls.PREFIX_ID.format(key))
|
||||||
|
cache.delete(cls.PREFIX_NAME.format(key))
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
from api.lib.perm.acl.cache import PermissionCache
|
||||||
|
from api.lib.perm.acl.cache import RoleCache
|
||||||
|
from api.models.acl import RolePermission
|
||||||
|
|
||||||
|
|
||||||
|
class PermissionCRUD(object):
|
||||||
|
@staticmethod
|
||||||
|
def get_all(resource_id=None, group_id=None):
|
||||||
|
result = dict()
|
||||||
|
if resource_id is not None:
|
||||||
|
perms = RolePermission.get_by(resource_id=resource_id, to_dict=False)
|
||||||
|
else:
|
||||||
|
perms = RolePermission.get_by(group_id=group_id, to_dict=False)
|
||||||
|
|
||||||
|
for perm in perms:
|
||||||
|
result.setdefault((perm.rid, RoleCache.get(perm.rid).name), []).append(
|
||||||
|
PermissionCache.get(perm.perm_id).to_dict())
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def grant(rid, perms, resource_id=None, group_id=None):
|
||||||
|
for perm in perms:
|
||||||
|
perm = PermissionCache.get(perm)
|
||||||
|
existed = RolePermission.get_by(rid=rid, perm_id=perm.id, group_id=group_id, resource_id=resource_id)
|
||||||
|
existed or RolePermission.create(rid=rid, perm_id=perm.id, group_id=group_id, resource_id=resource_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def revoke(rid, perms, resource_id=None, group_id=None):
|
||||||
|
for perm in perms:
|
||||||
|
perm = PermissionCache.get(perm)
|
||||||
|
existed = RolePermission.get_by(rid=rid,
|
||||||
|
perm_id=perm.id,
|
||||||
|
group_id=group_id,
|
||||||
|
resource_id=resource_id,
|
||||||
|
first=True,
|
||||||
|
to_dict=False)
|
||||||
|
existed and existed.soft_delete()
|
|
@ -156,3 +156,6 @@ class ResourceCRUD(object):
|
||||||
resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
|
resource = Resource.get_by_id(_id) or abort(404, "Resource <{0}> is not found".format(_id))
|
||||||
|
|
||||||
resource.soft_delete()
|
resource.soft_delete()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
# -*- coding:utf-8 -*-
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
import six
|
||||||
from flask import abort
|
from flask import abort
|
||||||
|
|
||||||
from api.extensions import db
|
from api.extensions import db
|
||||||
|
@ -16,75 +17,16 @@ from api.tasks.acl import role_rebuild
|
||||||
|
|
||||||
|
|
||||||
class RoleRelationCRUD(object):
|
class RoleRelationCRUD(object):
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class RoleCRUD(object):
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def search(q, app_id, page=1, page_size=None):
|
def get_parents(rids):
|
||||||
query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(
|
rids = [rids] if isinstance(rids, six.integer_types) else rids
|
||||||
Role.app_id == app_id).filter(Role.uid.is_(None))
|
res = db.session.query(RoleRelation).filter(
|
||||||
if q:
|
RoleRelation.child_id.in_(rids)).filter(RoleRelation.deleted.is_(False))
|
||||||
query = query.filter(Role.name.ilike('%{0}%'.format(q)))
|
id2parents = {}
|
||||||
|
|
||||||
numfound = query.count()
|
|
||||||
|
|
||||||
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def add_role(name, app_id, is_app_admin=False, uid=None):
|
|
||||||
Role.get_by(name=name, app_id=app_id) and abort(400, "Role <{0}> is already existed".format(name))
|
|
||||||
|
|
||||||
return Role.create(name=name,
|
|
||||||
app_id=app_id,
|
|
||||||
is_app_admin=is_app_admin,
|
|
||||||
uid=uid)
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def update_role(rid, **kwargs):
|
|
||||||
role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
|
|
||||||
|
|
||||||
RoleCache.clean(rid)
|
|
||||||
|
|
||||||
return role.update(**kwargs)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def delete_role(cls, rid):
|
|
||||||
role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
|
|
||||||
|
|
||||||
parent_ids = cls.get_parent_ids(rid)
|
|
||||||
child_ids = cls.get_child_ids(rid)
|
|
||||||
|
|
||||||
for i in RoleRelation.get_by(parent_id=rid, to_dict=False):
|
|
||||||
i.soft_delete()
|
|
||||||
for i in RoleRelation.get_by(child_id=rid, to_dict=False):
|
|
||||||
i.soft_delete()
|
|
||||||
|
|
||||||
for i in RolePermission.get_by(rid=rid, to_dict=False):
|
|
||||||
i.soft_delete()
|
|
||||||
|
|
||||||
role_rebuild.apply_async(args=(parent_ids + child_ids,), queue=ACL_QUEUE)
|
|
||||||
|
|
||||||
RoleCache.clean(rid)
|
|
||||||
RoleRelationCache.clean(rid)
|
|
||||||
|
|
||||||
role.soft_delete()
|
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_resources(rid):
|
|
||||||
res = RolePermission.get_by(rid=rid, to_dict=False)
|
|
||||||
id2perms = dict(id2perms={}, group2perms={})
|
|
||||||
for i in res:
|
for i in res:
|
||||||
if i.resource_id:
|
id2parents.setdefault(i.child_id, []).append(RoleCache.get(i.parent_id).to_dict())
|
||||||
id2perms['id2perms'].setdefault(i.resource_id, []).append(i.perm.name)
|
|
||||||
elif i.group_id:
|
|
||||||
id2perms['group2perms'].setdefault(i.group_id, []).append(i.perm.name)
|
|
||||||
|
|
||||||
return id2perms
|
return id2parents
|
||||||
|
|
||||||
@staticmethod
|
|
||||||
def get_group_ids(resource_id):
|
|
||||||
return [i.group_id for i in ResourceGroupItems.get_by(resource_id, to_dict=False)]
|
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def get_parent_ids(rid):
|
def get_parent_ids(rid):
|
||||||
|
@ -126,12 +68,99 @@ class RoleCRUD(object):
|
||||||
|
|
||||||
return all_child_ids
|
return all_child_ids
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add(parent_id, child_id):
|
||||||
|
RoleRelation.get_by(parent_id=parent_id, child_id=child_id) and abort(400, "It's already existed")
|
||||||
|
|
||||||
|
return RoleRelation.create(parent_id=parent_id, child_id=child_id)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete(_id):
|
||||||
|
existed = RoleRelation.get_by_id(_id) or abort(400, "RoleRelation <{0}> does not exist".format(_id))
|
||||||
|
|
||||||
|
existed.soft_delete()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def delete2(parent_id, child_id):
|
||||||
|
existed = RoleRelation.get_by(parent_id=parent_id, child_id=child_id)
|
||||||
|
existed or abort(400, "RoleRelation < {0} -> {1} > does not exist".format(parent_id, child_id))
|
||||||
|
|
||||||
|
existed.soft_delete()
|
||||||
|
|
||||||
|
|
||||||
|
class RoleCRUD(object):
|
||||||
|
@staticmethod
|
||||||
|
def search(q, app_id, page=1, page_size=None):
|
||||||
|
query = db.session.query(Role).filter(Role.deleted.is_(False)).filter(
|
||||||
|
Role.app_id == app_id).filter(Role.uid.is_(None))
|
||||||
|
if q:
|
||||||
|
query = query.filter(Role.name.ilike('%{0}%'.format(q)))
|
||||||
|
|
||||||
|
numfound = query.count()
|
||||||
|
|
||||||
|
return numfound, query.offset((page - 1) * page_size).limit(page_size)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def add_role(name, app_id, is_app_admin=False, uid=None):
|
||||||
|
Role.get_by(name=name, app_id=app_id) and abort(400, "Role <{0}> is already existed".format(name))
|
||||||
|
|
||||||
|
return Role.create(name=name,
|
||||||
|
app_id=app_id,
|
||||||
|
is_app_admin=is_app_admin,
|
||||||
|
uid=uid)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def update_role(rid, **kwargs):
|
||||||
|
role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
|
||||||
|
|
||||||
|
RoleCache.clean(rid)
|
||||||
|
|
||||||
|
return role.update(**kwargs)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def delete_role(cls, rid):
|
||||||
|
role = Role.get_by_id(rid) or abort(404, "Role <{0}> does not exist".format(rid))
|
||||||
|
|
||||||
|
parent_ids = RoleRelationCRUD.get_parent_ids(rid)
|
||||||
|
child_ids = RoleRelationCRUD.get_child_ids(rid)
|
||||||
|
|
||||||
|
for i in RoleRelation.get_by(parent_id=rid, to_dict=False):
|
||||||
|
i.soft_delete()
|
||||||
|
for i in RoleRelation.get_by(child_id=rid, to_dict=False):
|
||||||
|
i.soft_delete()
|
||||||
|
|
||||||
|
for i in RolePermission.get_by(rid=rid, to_dict=False):
|
||||||
|
i.soft_delete()
|
||||||
|
|
||||||
|
role_rebuild.apply_async(args=(parent_ids + child_ids,), queue=ACL_QUEUE)
|
||||||
|
|
||||||
|
RoleCache.clean(rid)
|
||||||
|
RoleRelationCache.clean(rid)
|
||||||
|
|
||||||
|
role.soft_delete()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_resources(rid):
|
||||||
|
res = RolePermission.get_by(rid=rid, to_dict=False)
|
||||||
|
id2perms = dict(id2perms={}, group2perms={})
|
||||||
|
for i in res:
|
||||||
|
if i.resource_id:
|
||||||
|
id2perms['id2perms'].setdefault(i.resource_id, []).append(i.perm.name)
|
||||||
|
elif i.group_id:
|
||||||
|
id2perms['group2perms'].setdefault(i.group_id, []).append(i.perm.name)
|
||||||
|
|
||||||
|
return id2perms
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def get_group_ids(resource_id):
|
||||||
|
return [i.group_id for i in ResourceGroupItems.get_by(resource_id, to_dict=False)]
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def has_permission(cls, rid, resource_name, perm):
|
def has_permission(cls, rid, resource_name, perm):
|
||||||
resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
|
resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
|
||||||
resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name))
|
resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name))
|
||||||
|
|
||||||
parent_ids = cls.recursive_parent_ids(rid)
|
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid)
|
||||||
|
|
||||||
group_ids = cls.get_group_ids(resource.id)
|
group_ids = cls.get_group_ids(resource.id)
|
||||||
|
|
||||||
|
@ -154,7 +183,7 @@ class RoleCRUD(object):
|
||||||
resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
|
resource = Resource.get_by(name=resource_name, first=True, to_dict=False)
|
||||||
resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name))
|
resource = resource or abort(403, "Resource <{0}> is not in ACL".format(resource_name))
|
||||||
|
|
||||||
parent_ids = cls.recursive_parent_ids(rid)
|
parent_ids = RoleRelationCRUD.recursive_parent_ids(rid)
|
||||||
group_ids = cls.get_group_ids(resource.id)
|
group_ids = cls.get_group_ids(resource.id)
|
||||||
|
|
||||||
perms = []
|
perms = []
|
||||||
|
|
|
@ -32,6 +32,6 @@ register_resources(os.path.join(HERE, "cmdb"), rest)
|
||||||
|
|
||||||
|
|
||||||
# acl
|
# acl
|
||||||
blueprint_acl_v1 = Blueprint('cmdb_acl_v1', __name__, url_prefix='/api/v1/acl')
|
blueprint_acl_v1 = Blueprint('acl_api_v1', __name__, url_prefix='/api/v1/acl')
|
||||||
rest = Api(blueprint_acl_v1)
|
rest = Api(blueprint_acl_v1)
|
||||||
register_resources(os.path.join(HERE, "acl"), rest)
|
register_resources(os.path.join(HERE, "acl"), rest)
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
# -*- coding:utf-8 -*-
|
||||||
|
|
||||||
|
|
||||||
|
from flask import request
|
||||||
|
|
||||||
|
from api.lib.decorator import args_required
|
||||||
|
from api.lib.perm.acl.permission import PermissionCRUD
|
||||||
|
from api.lib.utils import handle_arg_list
|
||||||
|
from api.resource import APIView
|
||||||
|
|
||||||
|
|
||||||
|
class ResourcePermissionView(APIView):
|
||||||
|
url_prefix = ("/resources/<int:resource_id>/permissions", "/resource_groups/<int:group_id>/permissions")
|
||||||
|
|
||||||
|
def get(self, resource_id=None, group_id=None):
|
||||||
|
return self.jsonify(PermissionCRUD.get_all(resource_id, group_id))
|
||||||
|
|
||||||
|
|
||||||
|
class RolePermissionGrantView(APIView):
|
||||||
|
url_prefix = ('/roles/<int:rid>/resources/<int:resource_id>/grant',
|
||||||
|
'/roles/<int:rid>/resource_groups/<int:group_id>/grant')
|
||||||
|
|
||||||
|
@args_required('perms')
|
||||||
|
def post(self, rid, resource_id=None, group_id=None):
|
||||||
|
perms = handle_arg_list(request.values.get("perms"))
|
||||||
|
PermissionCRUD.grant(rid, perms, resource_id=resource_id, group_id=group_id)
|
||||||
|
|
||||||
|
return self.jsonify(rid=rid, resource_id=resource_id, group_id=group_id, perms=perms)
|
||||||
|
|
||||||
|
|
||||||
|
class RolePermissionRevokeView(APIView):
|
||||||
|
url_prefix = ('/roles/<int:rid>/resources/<int:resource_id>/revoke',
|
||||||
|
'/roles/<int:rid>/resource_groups/<int:group_id>/revoke')
|
||||||
|
|
||||||
|
@args_required('perms')
|
||||||
|
def post(self, rid, resource_id=None, group_id=None):
|
||||||
|
perms = handle_arg_list(request.values.get("perms"))
|
||||||
|
PermissionCRUD.revoke(rid, perms, resource_id=resource_id, group_id=group_id)
|
||||||
|
|
||||||
|
return self.jsonify(rid=rid, resource_id=resource_id, group_id=group_id, perms=perms)
|
|
@ -4,6 +4,7 @@ from flask import request
|
||||||
|
|
||||||
from api.lib.decorator import args_required
|
from api.lib.decorator import args_required
|
||||||
from api.lib.perm.acl.role import RoleCRUD
|
from api.lib.perm.acl.role import RoleCRUD
|
||||||
|
from api.lib.perm.acl.role import RoleRelationCRUD
|
||||||
from api.lib.utils import get_page
|
from api.lib.utils import get_page
|
||||||
from api.lib.utils import get_page_size
|
from api.lib.utils import get_page_size
|
||||||
from api.resource import APIView
|
from api.resource import APIView
|
||||||
|
@ -21,9 +22,12 @@ class RoleView(APIView):
|
||||||
|
|
||||||
numfound, roles = RoleCRUD.search(q, app_id, page, page_size)
|
numfound, roles = RoleCRUD.search(q, app_id, page, page_size)
|
||||||
|
|
||||||
|
id2parents = RoleRelationCRUD.get_parents([i.id for i in roles])
|
||||||
|
|
||||||
return self.jsonify(numfound=numfound,
|
return self.jsonify(numfound=numfound,
|
||||||
page=page,
|
page=page,
|
||||||
page_size=page_size,
|
page_size=page_size,
|
||||||
|
id2parents=id2parents,
|
||||||
roles=[i.to_dict() for i in roles])
|
roles=[i.to_dict() for i in roles])
|
||||||
|
|
||||||
@args_required('name')
|
@args_required('name')
|
||||||
|
@ -46,3 +50,22 @@ class RoleView(APIView):
|
||||||
RoleCRUD.delete_role(rid)
|
RoleCRUD.delete_role(rid)
|
||||||
|
|
||||||
return self.jsonify(rid=rid)
|
return self.jsonify(rid=rid)
|
||||||
|
|
||||||
|
|
||||||
|
class RoleRelationView(APIView):
|
||||||
|
url_prefix = "/roles/<int:child_id>/parents"
|
||||||
|
|
||||||
|
@args_required('parent_id')
|
||||||
|
def post(self, child_id):
|
||||||
|
parent_id = request.values.get('parent_id')
|
||||||
|
res = RoleRelationCRUD.add(parent_id, child_id)
|
||||||
|
|
||||||
|
return self.jsonify(res.to_dict())
|
||||||
|
|
||||||
|
@args_required('parent_id')
|
||||||
|
def delete(self, child_id):
|
||||||
|
parent_id = request.values.get('parent_id')
|
||||||
|
|
||||||
|
RoleRelationCRUD.delete2(parent_id, child_id)
|
||||||
|
|
||||||
|
return self.jsonify(parent_id=parent_id, child_id=child_id)
|
||||||
|
|
|
@ -72,4 +72,4 @@ volumes:
|
||||||
db-data:
|
db-data:
|
||||||
|
|
||||||
networks:
|
networks:
|
||||||
new:
|
new:
|
Loading…
Reference in New Issue