Skip to content
Snippets Groups Projects
Commit 19cf0e64 authored by Florian Spreckelsen's avatar Florian Spreckelsen
Browse files

Merge branch 'f-parse-acl' into 'dev'

F parse acl

See merge request !49
parents ecf16ac0 d96fa19b
No related branches found
No related tags found
2 merge requests!53Release 0.7.2,!49F parse acl
Pipeline #20804 passed
...@@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ...@@ -17,6 +17,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed ### ### Fixed ###
* [caosdb-pylib#106](https://gitlab.indiscale.com/caosdb/src/caosdb-pylib/-/issues/106)
Parsing Error in class caosdb.common.models.ACL
### Security ### ### Security ###
### Documentation ### ### Documentation ###
......
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
# #
# Copyright (C) 2018 Research Group Biomedical Physics, # Copyright (C) 2018 Research Group Biomedical Physics,
# Max-Planck-Institute for Dynamics and Self-Organization Göttingen # Max-Planck-Institute for Dynamics and Self-Organization Göttingen
# Copyright (C) 2020 Indiscale GmbH <info@indiscale.com> # Copyright (C) 2020-2022 Indiscale GmbH <info@indiscale.com>
# Copyright (C) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com> # Copyright (C) 2020 Florian Spreckelsen <f.spreckelsen@indiscale.com>
# Copyright (C) 2020 Timm Fitschen <t.fitschen@indiscale.com> # Copyright (C) 2020-2022 Timm Fitschen <t.fitschen@indiscale.com>
# #
# This program is free software: you can redistribute it and/or modify # This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as # it under the terms of the GNU Affero General Public License as
...@@ -25,7 +25,14 @@ ...@@ -25,7 +25,14 @@
# ** end header # ** end header
# #
"""missing docstring.""" """
Collection of the central classes of the CaosDB client, namely the Entity class
and all of its subclasses and the Container class which is used to carry out
transactions.
All additional classes are either important for the entities or the
transactions.
"""
from __future__ import print_function, unicode_literals from __future__ import print_function, unicode_literals
import re import re
...@@ -269,14 +276,74 @@ class Entity(object): ...@@ -269,14 +276,74 @@ class Entity(object):
self.__pickup = new_pickup self.__pickup = new_pickup
def grant(self, realm=None, username=None, role=None, def grant(self, realm=None, username=None, role=None,
permission=None, priority=False): permission=None, priority=False, revoke_denial=True):
"""Grant a permission to a user or role for this entity.
You must specify either only the username and the realm, or only the
role.
By default a previously existing denial rule would be revoked, because
otherwise this grant wouldn't have any effect. However, for keeping
contradicting rules pass revoke_denial=False.
Parameters
----------
permission: str
The permission to be granted.
username : str, optional
The username. Exactly one is required, either the `username` or the
`role`.
realm: str, optional
The user's realm. Required when username is not None.
role: str, optional
The role (as in Role-Based Access Control). Exactly one is
required, either the `username` or the `role`.
priority: bool, default False
Whether this permission is granted with priority over non-priority
rules.
revoke_denial: bool, default True
Whether a contradicting denial (with same priority flag) in this
ACL will be revoked.
"""
# @review Florian Spreckelsen 2022-03-17
self.acl.grant(realm=realm, username=username, role=role, self.acl.grant(realm=realm, username=username, role=role,
permission=permission, priority=priority) permission=permission, priority=priority,
revoke_denial=revoke_denial)
def deny(self, realm=None, username=None, role=None, def deny(self, realm=None, username=None, role=None,
permission=None, priority=False): permission=None, priority=False, revoke_grant=True):
"""Deny a permission to a user or role for this entity.
You must specify either only the username and the realm, or only the
role.
By default a previously existing grant rule would be revoked, because
otherwise this denial would override the grant rules anyways. However,
for keeping contradicting rules pass revoke_grant=False.
Parameters
----------
permission: str
The permission to be denied.
username : str, optional
The username. Exactly one is required, either the `username` or the
`role`.
realm: str, optional
The user's realm. Required when username is not None.
role: str, optional
The role (as in Role-Based Access Control). Exactly one is
required, either the `username` or the `role`.
priority: bool, default False
Whether this permission is denied with priority over non-priority
rules.
revoke_grant: bool, default True
Whether a contradicting grant (with same priority flag) in this
ACL will be revoked.
"""
# @review Florian Spreckelsen 2022-03-17
self.acl.deny(realm=realm, username=username, role=role, self.acl.deny(realm=realm, username=username, role=role,
permission=permission, priority=priority) permission=permission, priority=priority,
revoke_grant=revoke_grant)
def revoke_denial(self, realm=None, username=None, def revoke_denial(self, realm=None, username=None,
role=None, permission=None, priority=False): role=None, permission=None, priority=False):
...@@ -3636,13 +3703,15 @@ class ACI(): ...@@ -3636,13 +3703,15 @@ class ACI():
self.permission = permission self.permission = permission
def __hash__(self): def __hash__(self):
return hash(str(self.realm) + ":" + str(self.username) + return hash(self.__repr__())
":" + str(self.role) + ":" + str(self.permission))
def __eq__(self, other): def __eq__(self, other):
return isinstance(other, ACI) and (self.role is None and self.username == other.username and self.realm == return isinstance(other, ACI) and (self.role is None and self.username == other.username and self.realm ==
other.realm) or self.role == other.role and self.permission == other.permission other.realm) or self.role == other.role and self.permission == other.permission
def __repr__(self):
return str(self.realm) + ":" + str(self.username) + ":" + str(self.role) + ":" + str(self.permission)
def add_to_element(self, e): def add_to_element(self, e):
if self.role is not None: if self.role is not None:
e.set("role", self.role) e.set("role", self.role)
...@@ -3667,10 +3736,35 @@ class ACL(): ...@@ -3667,10 +3736,35 @@ class ACL():
self.clear() self.clear()
def parse_xml(self, xml): def parse_xml(self, xml):
"""Clear this ACL and parse the xml.
Iterate over the rules in the xml and add each rule to this ACL.
Contradicting rules will both be kept.
Parameters
----------
xml : lxml.etree.Element
The xml element containing the ACL rules, i.e. <Grant> and <Deny>
rules.
"""
self.clear() self.clear()
self._parse_xml(xml) self._parse_xml(xml)
def _parse_xml(self, xml): def _parse_xml(self, xml):
"""Parse the xml.
Iterate over the rules in the xml and add each rule to this ACL.
Contradicting rules will both be kept.
Parameters
----------
xml : lxml.etree.Element
The xml element containing the ACL rules, i.e. <Grant> and <Deny>
rules.
"""
# @review Florian Spreckelsen 2022-03-17
for e in xml: for e in xml:
role = e.get("role") role = e.get("role")
username = e.get("username") username = e.get("username")
...@@ -3683,10 +3777,12 @@ class ACL(): ...@@ -3683,10 +3777,12 @@ class ACL():
if e.tag == "Grant": if e.tag == "Grant":
self.grant(username=username, realm=realm, role=role, self.grant(username=username, realm=realm, role=role,
permission=permission, priority=priority) permission=permission, priority=priority,
revoke_denial=False)
elif e.tag == "Deny": elif e.tag == "Deny":
self.deny(username=username, realm=realm, role=role, self.deny(username=username, realm=realm, role=role,
permission=permission, priority=priority) permission=permission, priority=priority,
revoke_grant=False)
def combine(self, other): def combine(self, other):
""" Combine and return new instance.""" """ Combine and return new instance."""
...@@ -3764,11 +3860,41 @@ class ACL(): ...@@ -3764,11 +3860,41 @@ class ACL():
if item in self._denials: if item in self._denials:
self._denials.remove(item) self._denials.remove(item)
def grant(self, username=None, realm=None, role=None, def grant(self, permission, username=None, realm=None, role=None,
permission=None, priority=False): priority=False, revoke_denial=True):
"""Grant a permission to a user or role.
You must specify either only the username and the realm, or only the
role.
By default a previously existing denial rule would be revoked, because
otherwise this grant wouldn't have any effect. However, for keeping
contradicting rules pass revoke_denial=False.
Parameters
----------
permission: str
The permission to be granted.
username : str, optional
The username. Exactly one is required, either the `username` or the
`role`.
realm: str, optional
The user's realm. Required when username is not None.
role: str, optional
The role (as in Role-Based Access Control). Exactly one is
required, either the `username` or the `role`.
priority: bool, default False
Whether this permission is granted with priority over non-priority
rules.
revoke_denial: bool, default True
Whether a contradicting denial (with same priority flag) in this
ACL will be revoked.
"""
# @review Florian Spreckelsen 2022-03-17
priority = self._get_boolean_priority(priority) priority = self._get_boolean_priority(priority)
item = ACI(role=role, username=username, item = ACI(role=role, username=username,
realm=realm, permission=permission) realm=realm, permission=permission)
if revoke_denial:
self._remove_item(item, priority) self._remove_item(item, priority)
if priority is True: if priority is True:
...@@ -3777,10 +3903,40 @@ class ACL(): ...@@ -3777,10 +3903,40 @@ class ACL():
self._grants.add(item) self._grants.add(item)
def deny(self, username=None, realm=None, role=None, def deny(self, username=None, realm=None, role=None,
permission=None, priority=False): permission=None, priority=False, revoke_grant=True):
"""Deny a permission to a user or role for this entity.
You must specify either only the username and the realm, or only the
role.
By default a previously existing grant rule would be revoked, because
otherwise this denial would override the grant rules anyways. However,
for keeping contradicting rules pass revoke_grant=False.
Parameters
----------
permission: str
The permission to be denied.
username : str, optional
The username. Exactly one is required, either the `username` or the
`role`.
realm: str, optional
The user's realm. Required when username is not None.
role: str, optional
The role (as in Role-Based Access Control). Exactly one is
required, either the `username` or the `role`.
priority: bool, default False
Whether this permission is denied with priority over non-priority
rules.
revoke_grant: bool, default True
Whether a contradicting grant (with same priority flag) in this
ACL will be revoked.
"""
# @review Florian Spreckelsen 2022-03-17
priority = self._get_boolean_priority(priority) priority = self._get_boolean_priority(priority)
item = ACI(role=role, username=username, item = ACI(role=role, username=username,
realm=realm, permission=permission) realm=realm, permission=permission)
if revoke_grant:
self._remove_item(item, priority) self._remove_item(item, priority)
if priority is True: if priority is True:
......
# -*- encoding: utf-8 -*-
#
# This file is a part of the CaosDB Project.
#
# Copyright (C) 2022 Indiscale GmbH <info@indiscale.com>
# Copyright (C) 2022 Timm Fitschen <f.fitschen@indiscale.com>
#
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU Affero General Public License as
# published by the Free Software Foundation, either version 3 of the
# License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU Affero General Public License for more details.
#
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see <https://www.gnu.org/licenses/>.
#
import caosdb as db
from lxml import etree
def test_parse_xml():
# @review Florian Spreckelsen 2022-03-17
xml_str = """
<EntityACL>
<Grant priority="False" role="role1">
<Permission name="RETRIEVE:ENTITY"/>
</Grant>
<Deny priority="False" role="role1">
<Permission name="RETRIEVE:ENTITY"/>
</Deny>
<Grant priority="True" role="role1">
<Permission name="RETRIEVE:ENTITY"/>
</Grant>
<Deny priority="True" role="role1">
<Permission name="RETRIEVE:ENTITY"/>
</Deny>
</EntityACL>"""
xml = etree.fromstring(xml_str)
left_acl = db.ACL(xml)
right_acl = db.ACL()
right_acl.grant(role="role1", permission="RETRIEVE:ENTITY",
revoke_denial=False)
right_acl.deny(role="role1", permission="RETRIEVE:ENTITY",
revoke_grant=False)
right_acl.grant(role="role1", permission="RETRIEVE:ENTITY",
priority=True, revoke_denial=False)
right_acl.deny(role="role1", permission="RETRIEVE:ENTITY",
priority=True, revoke_grant=False)
assert left_acl == right_acl
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment