sharing: fixes, doc update, alignment
This commit is contained in:
175
SHARING.md
175
SHARING.md
@@ -246,7 +246,10 @@ Can be selected by `HTTP_ACCEPT`
|
||||
|
||||
Shows what kind of ShareTypes are supported
|
||||
|
||||
* Example: TEXT
|
||||
* Output: text/plain|application/json
|
||||
|
||||
* Examples
|
||||
* form->text
|
||||
|
||||
```
|
||||
curl -u user:pass -H "accept: text/plain" -d "" http://localhost:5232/.sharing/v1/all/info
|
||||
@@ -258,7 +261,7 @@ FeatureEnabledCollectionByToken=True
|
||||
PermittedCreateCollectionByToken=True
|
||||
```
|
||||
|
||||
* Example: JSON
|
||||
* json->json, parsed with `jq`
|
||||
|
||||
```
|
||||
curl -u user:pass --silent -H "accept: application/json" -d "" http://localhost:5232/.sharing/v1/all/info | jq
|
||||
@@ -272,7 +275,6 @@ curl -u user:pass --silent -H "accept: application/json" -d "" http://localhost:
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
##### API Hook "(token|map)/create"
|
||||
|
||||
* Authorization
|
||||
@@ -284,8 +286,10 @@ Authenticated user is `Owner`
|
||||
Create a share by mapping a collection of an `Owner` to a token.
|
||||
|
||||
* Authorization
|
||||
|
||||
Authenticated user as `Owner` has at least read access to `PathMapped`
|
||||
* `PathMapped` is existing and a collection
|
||||
* Authenticated user as `Owner` has at least read access to `PathMapped`
|
||||
* Global permitted by `permit_create_token = True` or `rights` permission `t`
|
||||
* Global denied by `permit_create_token = False` or `rights` permission `T`
|
||||
|
||||
* Input
|
||||
|
||||
@@ -298,19 +302,27 @@ Authenticated user as `Owner` has at least read access to `PathMapped`
|
||||
| Hidden | bool | optional(owner/default:True) |
|
||||
| Properties | str | optional |
|
||||
|
||||
* Output
|
||||
* Output: text/plain|application/json
|
||||
|
||||
| Parameter | Type | Value |
|
||||
| - | - |
|
||||
| PathOrToken | str | (autogenerated token) |
|
||||
|
||||
* Example: TEXT
|
||||
* Examples:
|
||||
* form->text
|
||||
|
||||
```
|
||||
curl -u user:pass -d "PathMapped=/user/testcalendar1/" http://localhost:5232/.sharing/v1/token/create
|
||||
curl -u user:pass -d "PathMapped=/user/testcalendar1/" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/token/create
|
||||
ApiVersion=1
|
||||
Status='success'
|
||||
PathOrToken='v1/VQR7AmsVRi2ZlFj_JwGpFx-ES5Goyku-gP_YkLh1zUw='
|
||||
```
|
||||
|
||||
* json->json
|
||||
|
||||
```
|
||||
curl -u user:pass -H "Content-Type: application/json" -d '{ "PathMapped": "/user/testcalendar1/", "Enabled": true, "Hidden": false}' http://localhost:5232/.sharing/v1/token/create
|
||||
{"ApiVersion": 1, "Status": "success", "PathOrToken": "v1/aMsmGqOsRwSH-2-6tEa8EMr4RMYzMU7WvPmjnp5qDnw="}
|
||||
```
|
||||
|
||||
###### API Hook "map/create"
|
||||
@@ -318,10 +330,11 @@ PathOrToken='v1/VQR7AmsVRi2ZlFj_JwGpFx-ES5Goyku-gP_YkLh1zUw='
|
||||
Create a share by mapping a collection of an `Owner` to an `User`.
|
||||
|
||||
* Authorization
|
||||
|
||||
Authenticated user as `Owner` has at least read access to `PathMapped`
|
||||
|
||||
Provided `User` has at least read access to `PathOrToken`
|
||||
* `PathMapped` is existing and a collection
|
||||
* Authenticated user as `Owner` has at least read access to `PathMapped`
|
||||
* Provided `User` has at least read access to `PathOrToken`
|
||||
* Global permitted by `permit_create_map = True` or `rights` permission `m`
|
||||
* Global denied by `permit_create_map = False` or `rights` permission `M`
|
||||
|
||||
* Input
|
||||
|
||||
@@ -335,24 +348,30 @@ Provided `User` has at least read access to `PathOrToken`
|
||||
| Hidden | bool | optional(owner/default:True) |
|
||||
| Properties | optional |
|
||||
|
||||
* Output: result status
|
||||
* Output: text/plain|application/json
|
||||
|
||||
* Example: TEXT
|
||||
* Examples:
|
||||
* form->text
|
||||
|
||||
```
|
||||
curl -u owner:pass -d "PathOrToken=/user/cal1-from-owner/" -d "PathMapped=/owner/cal1/" -d "User=user" http://localhost:5232/.sharing/v1/map/create
|
||||
curl -u owner:pass -d "PathOrToken=/user/cal1-from-owner/" -d "PathMapped=/owner/testcalendar1/" -d "User=user" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/map/create
|
||||
ApiVersion=1
|
||||
Status=success
|
||||
Status='success'
|
||||
```
|
||||
|
||||
* json->json
|
||||
|
||||
```
|
||||
curl -u owner:pass -H "Content-Type: application/json" -d '{ "PathOrToken": "/user/cal1-from-owner/", "PathMapped": "/owner/testcalendar1/", "User" : "user", "Enabled": true, "Hidden": false}' http://localhost:5232/.sharing/v1/map/create
|
||||
{"ApiVersion": 1, "Status": "success"}
|
||||
```
|
||||
|
||||
##### API Hook "(map|token|all)/list"
|
||||
|
||||
List shares (optional with filter) either owned or assigned as user.
|
||||
|
||||
* Authorization
|
||||
|
||||
Authenticated user as `Owner` or `User`
|
||||
* Authenticated user as `Owner` or `User`
|
||||
|
||||
* Input
|
||||
|
||||
@@ -361,14 +380,58 @@ Authenticated user as `Owner` or `User`
|
||||
| PathOrToken | str | optional |
|
||||
| PathMapped | str | optional |
|
||||
|
||||
* Output: plain/csv/json
|
||||
* Output: text/plain|text/csv|application/json
|
||||
|
||||
* Example: CSV
|
||||
* Examples
|
||||
* form->csv ("map" only)
|
||||
|
||||
```
|
||||
curl -H "accept: text/csv" -u owner:pass -d "" http://localhost:5232/.sharing/v1/map/list
|
||||
ShareType,PathOrToken,PathMapped,Owner,User,Permissions,EnabledByOwner,EnabledByUser,HiddenByOwner,HiddenByUser,TimestampCreated,TimestampUpdated,Properties
|
||||
map,/user/cal1-from-owner/,/owner/cal1/,owner,user,r,False,False,True,True,1771962120,1771962120,{}
|
||||
curl -H "accept: text/csv" -u user:pass -d "" http://localhost:5232/.sharing/v1/map/list://localhost:5232/.sharing/v1/map/list
|
||||
ShareType;PathOrToken;PathMapped;Owner;User;Permissions;EnabledByOwner;EnabledByUser;HiddenByOwner;HiddenByUser;TimestampCreated;TimestampUpdated;Properties
|
||||
map;/user/cal1-from-owner/;/owner/testcalendar1/;owner;user;r;True;False;False;True;1772747277;1772747277;
|
||||
```
|
||||
|
||||
* json->json ("all"), parsed with `jq`
|
||||
|
||||
```
|
||||
curl -s -H "Content-Type: application/json" -u user:pass -d "{}" http://localhost:5232/.sharing/v1/all/list | jq
|
||||
{
|
||||
"ApiVersion": 1,
|
||||
"Lines": 2,
|
||||
"Status": "success",
|
||||
"Content": [
|
||||
{
|
||||
"ShareType": "map",
|
||||
"PathOrToken": "/user/cal1-from-owner/",
|
||||
"PathMapped": "/owner/testcalendar1/",
|
||||
"Owner": "owner",
|
||||
"User": "user",
|
||||
"Permissions": "r",
|
||||
"EnabledByOwner": true,
|
||||
"EnabledByUser": false,
|
||||
"HiddenByOwner": false,
|
||||
"HiddenByUser": true,
|
||||
"TimestampCreated": 1772747277,
|
||||
"TimestampUpdated": 1772747277,
|
||||
"Properties": ""
|
||||
},
|
||||
{
|
||||
"ShareType": "token",
|
||||
"PathOrToken": "v1/DUSl_J5rRlWx3fy8YRXpH22FFllplkOTpcSwfGtpvkc=",
|
||||
"PathMapped": "/user/testcalendar1/",
|
||||
"Owner": "user",
|
||||
"User": "user",
|
||||
"Permissions": "r",
|
||||
"EnabledByOwner": true,
|
||||
"EnabledByUser": false,
|
||||
"HiddenByOwner": false,
|
||||
"HiddenByUser": true,
|
||||
"TimestampCreated": 1772747371,
|
||||
"TimestampUpdated": 1772747371,
|
||||
"Properties": ""
|
||||
}
|
||||
7]
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
@@ -377,8 +440,8 @@ map,/user/cal1-from-owner/,/owner/cal1/,owner,user,r,False,False,True,True,17719
|
||||
Delete a share selected by `PathOrToken`.
|
||||
|
||||
* Authorization
|
||||
|
||||
Authenticated user is `Owner`
|
||||
* Authenticated user is `Owner`
|
||||
* Share is existing and owned
|
||||
|
||||
* Input
|
||||
|
||||
@@ -386,7 +449,23 @@ Authenticated user is `Owner`
|
||||
| - | - | - | - | - |
|
||||
| PathOrToken | str | selection | mandatory | not-permitted |
|
||||
|
||||
* Output: result status
|
||||
* Output: text/plain|application/json
|
||||
|
||||
* Examples:
|
||||
* form->text
|
||||
|
||||
```
|
||||
curl -u owner:pass -d "PathOrToken=/user/cal1-from-owner/" http://localhost:5232/.sharing/v1/map/delete
|
||||
ApiVersion=1
|
||||
Status='success'
|
||||
```
|
||||
|
||||
* json->json
|
||||
|
||||
```
|
||||
curl -u user:pass -H "Content-Type: application/json" -d '{ "PathOrToken": "v1/DUSl_J5rRlWx3fy8YRXpH22FFllplkOTpcSwfGtpvkc="}' http://localhost:5232/.sharing/v1/token/delete
|
||||
{"ApiVersion": 1, "Status": "success"}
|
||||
```
|
||||
|
||||
##### API Hook "(token|map)/update"
|
||||
|
||||
@@ -395,8 +474,7 @@ Update a share selected by `PathOrToken`.
|
||||
Execute delete+create in case `PathOrToken` needs to be changed.
|
||||
|
||||
* Authorization
|
||||
|
||||
Authenticated user is `Owner` or `User`
|
||||
* Authenticated user is `Owner` or `User`
|
||||
|
||||
* Input
|
||||
|
||||
@@ -410,15 +488,29 @@ Authenticated user is `Owner` or `User`
|
||||
| Hidden | bool | adjust | optional(owner) | optional(user) |
|
||||
| Properties | str | adjust | optional | optional |
|
||||
|
||||
* Output: result status
|
||||
* Output: text/plain|application/json
|
||||
|
||||
* form->text
|
||||
```
|
||||
curl -u user:pass -d "PathOrToken=/user/cal1-from-owner/" -d "Enabled=True" -d "Hidden=False" http://localhost:5232/.sharing/v1/map/update
|
||||
ApiVersion=1
|
||||
Status='success'
|
||||
```
|
||||
|
||||
* json->json
|
||||
|
||||
```
|
||||
curl -u user:pass -H "Content-Type: application/json" -d '{ "PathOrToken": "/user/cal1-from-owner/", "Enabled": true, "Hidden": false}' http://localhost:5232/.sharing/v1/map/update
|
||||
{"ApiVersion": 1, "Status": "success"}
|
||||
```
|
||||
|
||||
##### API Hooks "(map|token)/(enable|disable|hide|unhide)"
|
||||
|
||||
Toggle enable|disable|hide|unhide of `Owner` or `User` of a share selected by `PathOrToken`
|
||||
|
||||
* Authorization
|
||||
|
||||
Authenticated user is `Owner` or `User`
|
||||
* Authenticated user is `Owner` or `User`
|
||||
* `PathOrToken` is existing and either owned or assigned to user
|
||||
|
||||
* Input
|
||||
|
||||
@@ -426,22 +518,21 @@ Authenticated user is `Owner` or `User`
|
||||
| - | - | - | - | - |
|
||||
| PathOrToken | selection | mandatory | mandatory |
|
||||
|
||||
* Output: result status
|
||||
* Output: text/plain|application/json
|
||||
|
||||
* Example: TEXT (enable)
|
||||
|
||||
```
|
||||
curl -u owner:pass -d "PathOrToken=/user/cal1-from-owner/" -d "PathMapped=/owner/cal1/" -d "User=user" http://localhost:5232/.sharing/v1/map/enable
|
||||
ApiVersion=1
|
||||
Status=success
|
||||
```
|
||||
* form->text
|
||||
|
||||
* Example: JSON (unhide)
|
||||
|
||||
```
|
||||
curl -u owner:pass -d '{"PathOrToken": "/user/cal1-from-owner/", "PathMapped": "/owner/cal1/", "User": "user"} http://localhost:5232/.sharing/v1/map/unhide
|
||||
curl -u user:pass -d "PathOrToken=/user/cal1-from-owner/" http://localhost:5232/.sharing/v1/map/enable
|
||||
ApiVersion=1
|
||||
Status='success'
|
||||
```
|
||||
|
||||
* json->json
|
||||
|
||||
```
|
||||
curl -u user:pass -H "Content-Type: application/json" -d '{ "PathOrToken": "/user/cal1-from-owner/"}' http://localhost:5232/.sharing/v1/map/unhide
|
||||
{"ApiVersion": 1, "Status": "success"}
|
||||
```
|
||||
|
||||
## Properties Overlay
|
||||
|
||||
@@ -290,7 +290,7 @@ class BaseSharing:
|
||||
return None
|
||||
|
||||
# list sharings of type "map"
|
||||
def sharing_collection_map_list(self, user: str, active: bool = True) -> list[dict]:
|
||||
def sharing_collection_map_list(self, user: Union[str, None], active: bool = True) -> list[dict]:
|
||||
""" returning dict with shared collections (active==True: enabled and unhidden) or None if not found"""
|
||||
if not self.sharing_collection_by_map:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
@@ -488,6 +488,7 @@ class BaseSharing:
|
||||
# parse body according to content-type
|
||||
content_type = environ.get("CONTENT_TYPE", "")
|
||||
if 'application/json' in content_type:
|
||||
output_format = "json" # default
|
||||
try:
|
||||
request_data = json.loads(request_body)
|
||||
except json.JSONDecodeError:
|
||||
@@ -501,6 +502,7 @@ class BaseSharing:
|
||||
if logger.isEnabledFor(logging.DEBUG):
|
||||
logger.debug("TRACE/" + api_info + " (json): %r", f"{request_data}")
|
||||
elif 'application/x-www-form-urlencoded' in content_type:
|
||||
output_format = "txt" # default
|
||||
request_parsed = parse_qs(request_body)
|
||||
# convert arrays into single value
|
||||
request_data = {}
|
||||
@@ -539,8 +541,11 @@ class BaseSharing:
|
||||
output_format = "json"
|
||||
elif 'text/csv' in accept:
|
||||
output_format = "csv"
|
||||
else:
|
||||
elif 'text/plain' in accept:
|
||||
output_format = "txt"
|
||||
else:
|
||||
# default from input type
|
||||
pass
|
||||
|
||||
if output_format == "csv":
|
||||
if not action == "list":
|
||||
@@ -555,7 +560,6 @@ class BaseSharing:
|
||||
# parameters default
|
||||
PathOrToken: Union[str, None] = None
|
||||
PathMapped: Union[str, None] = None
|
||||
Owner: str = user
|
||||
User: Union[str, None] = None
|
||||
Permissions: Union[str, None] = None # no permissions by default
|
||||
Enabled: Union[bool, None] = None
|
||||
@@ -685,7 +689,7 @@ class BaseSharing:
|
||||
logger.error(api_info + ": missing PathMapped")
|
||||
return httputils.bad_request("Missing PathMapped")
|
||||
|
||||
## check whether collection exists
|
||||
# check whether collection exists
|
||||
with self._storage.acquire_lock("r", user, path=PathMapped):
|
||||
item = next(iter(self._storage.discover(PathMapped)), None)
|
||||
if not item:
|
||||
@@ -741,7 +745,6 @@ class BaseSharing:
|
||||
else:
|
||||
User = user
|
||||
|
||||
|
||||
# v1: create uuid token with 2x 32 bytes = 256 bit
|
||||
token = "v1/" + str(base64.urlsafe_b64encode(uuid.uuid4().bytes + uuid.uuid4().bytes), 'utf-8')
|
||||
|
||||
@@ -784,9 +787,9 @@ class BaseSharing:
|
||||
User = str(User)
|
||||
|
||||
# check access Permissions
|
||||
access = Access(self._rights, Owner, PathMapped, None) # PathMapped is mandatory
|
||||
access = Access(self._rights, user, PathMapped, None) # PathMapped is mandatory
|
||||
if not access.check("r") and "i" not in access.permissions:
|
||||
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, user)
|
||||
return httputils.NOT_ALLOWED
|
||||
|
||||
if self.permit_create_map is False:
|
||||
|
||||
@@ -306,6 +306,9 @@ class TestSharingApiSanity(BaseTest):
|
||||
form_array: Sequence[str]
|
||||
json_dict: dict
|
||||
|
||||
self.mkcalendar("/owner/collectionL1/", login="owner:ownerpw")
|
||||
self.mkcalendar("/owner/collectionL2/", login="owner:ownerpw")
|
||||
|
||||
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
|
||||
logging.info("\n*** test: %s", db_type)
|
||||
self.configure({"sharing": {"type": db_type}})
|
||||
@@ -605,6 +608,8 @@ class TestSharingApiSanity(BaseTest):
|
||||
path = path_base + "/event1.ics"
|
||||
self.put(path, event, login="owner:ownerpw")
|
||||
|
||||
self.mkcalendar(path_base2, login="owner:ownerpw")
|
||||
|
||||
for db_type in list(filter(lambda item: item != "none", sharing.INTERNAL_TYPES)):
|
||||
logging.info("\n*** test: %s", db_type)
|
||||
self.configure({"sharing": {"type": db_type}})
|
||||
|
||||
Reference in New Issue
Block a user