sharing: add global and conditional permissions support
This commit is contained in:
@@ -2102,6 +2102,40 @@ Share collection by map
|
|||||||
|
|
||||||
Default: `false`
|
Default: `false`
|
||||||
|
|
||||||
|
##### permit_create_token
|
||||||
|
|
||||||
|
_(>= 3.7.0)_
|
||||||
|
|
||||||
|
Permit create of token-based sharing
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
* If `False` it can be explicitly granted by `permissions: t`
|
||||||
|
* If `True` it can be explicitly forbidden by `permissions: T`
|
||||||
|
|
||||||
|
##### permit_create_map
|
||||||
|
|
||||||
|
_(>= 3.7.0)_
|
||||||
|
|
||||||
|
Permit create of map-based sharing
|
||||||
|
|
||||||
|
Default: `false`
|
||||||
|
|
||||||
|
* If `False` it can be explicitly granted by `permissions: m`
|
||||||
|
* If `True` it can be explicitly forbidden by `permissions: M`
|
||||||
|
|
||||||
|
##### default_permissions_create_token
|
||||||
|
|
||||||
|
Default permissions for create token-based sharing
|
||||||
|
|
||||||
|
Default: `r`
|
||||||
|
|
||||||
|
##### default_permissions_create_map
|
||||||
|
|
||||||
|
Default permissions for map-based sharing
|
||||||
|
|
||||||
|
Default: `r`
|
||||||
|
|
||||||
## Supported Clients
|
## Supported Clients
|
||||||
|
|
||||||
Radicale has been tested with:
|
Radicale has been tested with:
|
||||||
@@ -2326,6 +2360,10 @@ The following `permissions` are recognized:
|
|||||||
* **d:** deny deleting a collection in case `permit_delete_collection=True` _(>= 3.3.0)_
|
* **d:** deny deleting a collection in case `permit_delete_collection=True` _(>= 3.3.0)_
|
||||||
* **O:** allow overwriting a collection in case `permit_overwrite_collection=False` _(>= 3.3.0)_
|
* **O:** allow overwriting a collection in case `permit_overwrite_collection=False` _(>= 3.3.0)_
|
||||||
* **o:** deny overwriting a collection in case `permit_overwrite_collection=True` _(>= 3.3.0)_
|
* **o:** deny overwriting a collection in case `permit_overwrite_collection=True` _(>= 3.3.0)_
|
||||||
|
* **T:** permit create of token-based sharing of collection in case `permit_create_token=False` _(>= 3.7.0)_
|
||||||
|
* **t:** deny create of token-based sharing of collection in case `permit_create_token=True` _(>= 3.7.0)_
|
||||||
|
* **M:** permit create of map-based sharing of collection in case `permit_create_map= False` _(>= 3.7.0)_
|
||||||
|
* **m:** deny create of map-based sharing of collection in case `permit_create_map=True` _(>= 3.7.0)_
|
||||||
|
|
||||||
### Storage
|
### Storage
|
||||||
|
|
||||||
|
|||||||
16
config
16
config
@@ -317,6 +317,22 @@
|
|||||||
# Share collection by map
|
# Share collection by map
|
||||||
#collection_by_map = false
|
#collection_by_map = false
|
||||||
|
|
||||||
|
# Permit create of token-based sharing
|
||||||
|
# If False it can be explicitly granted by permissions: t
|
||||||
|
# If True it can be explicitly forbidden by permissions: T
|
||||||
|
#permit_create_token = false
|
||||||
|
|
||||||
|
# Permit create of map-based sharing
|
||||||
|
# If False it can be explicitly granted by permissions: m
|
||||||
|
# If True it can be explicitly forbidden by permissions: M
|
||||||
|
#permit_create_map = false
|
||||||
|
|
||||||
|
# Default permissions for token-based sharing
|
||||||
|
#default_permissions_create_token = r
|
||||||
|
|
||||||
|
# Default permissions for map-based sharing
|
||||||
|
#default_permissions_create_map = r
|
||||||
|
|
||||||
|
|
||||||
[web]
|
[web]
|
||||||
|
|
||||||
|
|||||||
@@ -482,7 +482,23 @@ DEFAULT_CONFIG_SCHEMA: types.CONFIG_SCHEMA = OrderedDict([
|
|||||||
("collection_by_map", {
|
("collection_by_map", {
|
||||||
"value": "false",
|
"value": "false",
|
||||||
"help": "enable sharing of collection by map",
|
"help": "enable sharing of collection by map",
|
||||||
"type": bool})])),
|
"type": bool}),
|
||||||
|
("permit_create_token", {
|
||||||
|
"value": "false",
|
||||||
|
"help": "permit create of token-based sharing",
|
||||||
|
"type": bool}),
|
||||||
|
("permit_create_map", {
|
||||||
|
"value": "false",
|
||||||
|
"help": "permit create of map-based sharing",
|
||||||
|
"type": bool}),
|
||||||
|
("default_permissions_create_token", {
|
||||||
|
"value": "r",
|
||||||
|
"help": "default permissions for token-based sharing",
|
||||||
|
"type": rights_permission}),
|
||||||
|
("default_permissions_create_map", {
|
||||||
|
"value": "r",
|
||||||
|
"help": "default permissions for map-based sharing",
|
||||||
|
"type": rights_permission})])),
|
||||||
("hook", OrderedDict([
|
("hook", OrderedDict([
|
||||||
("type", {
|
("type", {
|
||||||
"value": "none",
|
"value": "none",
|
||||||
|
|||||||
@@ -31,6 +31,10 @@ Permissions:
|
|||||||
- d: deny deleting a collection in case permit_delete_collection=True (>= 3.3.0)
|
- d: deny deleting a collection in case permit_delete_collection=True (>= 3.3.0)
|
||||||
- O: allow overwriting a collection in case permit_overwrite_collection=False (>= 3.3.0)
|
- O: allow overwriting a collection in case permit_overwrite_collection=False (>= 3.3.0)
|
||||||
- o: deny overwriting a collection in case permit_overwrite_collection=True (>= 3.3.0)
|
- o: deny overwriting a collection in case permit_overwrite_collection=True (>= 3.3.0)
|
||||||
|
- T: permit create of token-based sharing of collection in case permit_create_token=False (>= 3.7.0)
|
||||||
|
- t: deny create of token-based sharing of collection in case permit_create_token=True (>= 3.7.0)
|
||||||
|
- M: permit create of map-based sharing of collection in case permit_create_map= False (>= 3.7.0)
|
||||||
|
- m: deny create of map-based sharing of collection in case permit_create_map=True (>= 3.7.0)
|
||||||
|
|
||||||
Take a look at the class ``BaseRights`` if you want to implement your own.
|
Take a look at the class ``BaseRights`` if you want to implement your own.
|
||||||
|
|
||||||
|
|||||||
@@ -102,8 +102,16 @@ class BaseSharing:
|
|||||||
# Sharing
|
# Sharing
|
||||||
self.sharing_collection_by_map = configuration.get("sharing", "collection_by_map")
|
self.sharing_collection_by_map = configuration.get("sharing", "collection_by_map")
|
||||||
self.sharing_collection_by_token = configuration.get("sharing", "collection_by_token")
|
self.sharing_collection_by_token = configuration.get("sharing", "collection_by_token")
|
||||||
|
self.permit_create_token = configuration.get("sharing", "permit_create_token")
|
||||||
|
self.permit_create_map = configuration.get("sharing", "permit_create_map")
|
||||||
|
self.default_permissions_create_token = configuration.get("sharing", "default_permissions_create_token")
|
||||||
|
self.default_permissions_create_map = configuration.get("sharing", "default_permissions_create_map")
|
||||||
logger.info("sharing.collection_by_map : %s", self.sharing_collection_by_map)
|
logger.info("sharing.collection_by_map : %s", self.sharing_collection_by_map)
|
||||||
logger.info("sharing.collection_by_token: %s", self.sharing_collection_by_token)
|
logger.info("sharing.collection_by_token: %s", self.sharing_collection_by_token)
|
||||||
|
logger.info("sharing.permit_create_token: %s", self.permit_create_token)
|
||||||
|
logger.info("sharing.permit_create_map : %s", self.permit_create_map)
|
||||||
|
logger.info("sharing.default_permissions_create_token: %r", self.default_permissions_create_token)
|
||||||
|
logger.info("sharing.default_permissions_create_map : %r", self.default_permissions_create_map)
|
||||||
|
|
||||||
if ((self.sharing_collection_by_map is False) and (self.sharing_collection_by_token is False)):
|
if ((self.sharing_collection_by_map is False) and (self.sharing_collection_by_token is False)):
|
||||||
logger.info("sharing disabled as no feature is enabled")
|
logger.info("sharing disabled as no feature is enabled")
|
||||||
@@ -634,7 +642,13 @@ class BaseSharing:
|
|||||||
if logger.isEnabledFor(logging.DEBUG):
|
if logger.isEnabledFor(logging.DEBUG):
|
||||||
logger.debug("TRACE/" + api_info + ": start")
|
logger.debug("TRACE/" + api_info + ": start")
|
||||||
if 'Permissions' not in request_data:
|
if 'Permissions' not in request_data:
|
||||||
Permissions = "r"
|
if ShareType == "token":
|
||||||
|
Permissions = self.default_permissions_create_token
|
||||||
|
elif ShareType == "map":
|
||||||
|
Permissions = self.default_permissions_create_map
|
||||||
|
else:
|
||||||
|
# default
|
||||||
|
Permissions = "r"
|
||||||
|
|
||||||
if 'Enabled' in request_data:
|
if 'Enabled' in request_data:
|
||||||
EnabledByOwner = config._convert_to_bool(request_data['Enabled'])
|
EnabledByOwner = config._convert_to_bool(request_data['Enabled'])
|
||||||
@@ -652,10 +666,19 @@ class BaseSharing:
|
|||||||
if ShareType == "token":
|
if ShareType == "token":
|
||||||
# check access Permissions
|
# check access Permissions
|
||||||
access = Access(self._rights, user, str(PathMapped)) # PathMapped is mandatory
|
access = Access(self._rights, user, str(PathMapped)) # PathMapped is mandatory
|
||||||
if not access.check("r") and "i" not in access.permissions:
|
if not access.check("r"):
|
||||||
logger.info("Add sharing-by-token: access to %r not allowed for user %r", PathMapped, user)
|
logger.info("Add sharing-by-token: access to %r not allowed for user %r", PathMapped, user)
|
||||||
return httputils.NOT_ALLOWED
|
return httputils.NOT_ALLOWED
|
||||||
|
|
||||||
|
if self.permit_create_token is False:
|
||||||
|
if "t" not in access.permissions:
|
||||||
|
logger.info("Add sharing-by-token: access to %r not allowed for user %r (permit=False but explict grant misses 't')", PathMapped, user)
|
||||||
|
return httputils.NOT_ALLOWED
|
||||||
|
else:
|
||||||
|
if "T" in access.permissions:
|
||||||
|
logger.info("Add sharing-by-token: access to %r not allowed for user %r (permit=True but denied by 'T')", PathMapped, user)
|
||||||
|
return httputils.NOT_ALLOWED
|
||||||
|
|
||||||
# v1: create uuid token with 2x 32 bytes = 256 bit
|
# v1: create uuid token with 2x 32 bytes = 256 bit
|
||||||
token = "v1/" + str(base64.urlsafe_b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes), 'utf-8')
|
token = "v1/" + str(base64.urlsafe_b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes), 'utf-8')
|
||||||
|
|
||||||
@@ -690,9 +713,18 @@ class BaseSharing:
|
|||||||
logger.info("Add sharing-by-map: access to path(mapped) %r not allowed for owner %r", PathMapped, Owner)
|
logger.info("Add sharing-by-map: access to path(mapped) %r not allowed for owner %r", PathMapped, Owner)
|
||||||
return httputils.NOT_ALLOWED
|
return httputils.NOT_ALLOWED
|
||||||
|
|
||||||
|
if self.permit_create_map is False:
|
||||||
|
if "m" not in access.permissions:
|
||||||
|
logger.info("Add sharing-by-map: access to %r not allowed for user %r (permit=False but explicit grant misses 'm')", PathMapped, user)
|
||||||
|
return httputils.NOT_ALLOWED
|
||||||
|
else:
|
||||||
|
if "M" in access.permissions:
|
||||||
|
logger.info("Add sharing-by-map: access to %r not allowed for user %r (permit=True but denied by 'M')", PathMapped, user)
|
||||||
|
return httputils.NOT_ALLOWED
|
||||||
|
|
||||||
access = Access(self._rights, str(User), PathOrToken)
|
access = Access(self._rights, str(User), PathOrToken)
|
||||||
if not access.check("r") and "i" not in access.permissions:
|
if not access.check("r"):
|
||||||
logger.info("Add sharing-by-map: access to path %r not allowed for user %r", PathOrToken, user)
|
logger.info("Add sharing-by-map: access to path %r not allowed for user %r", PathOrToken, User)
|
||||||
return httputils.NOT_ALLOWED
|
return httputils.NOT_ALLOWED
|
||||||
|
|
||||||
# check whether share is already existing as real collection
|
# check whether share is already existing as real collection
|
||||||
|
|||||||
Reference in New Issue
Block a user