proper quote text/plain output
This commit is contained in:
89
SHARING.md
89
SHARING.md
@@ -145,7 +145,7 @@ Shows what kind of ShareTypes are supported
|
||||
```
|
||||
curl -u user:pass -H "accept: text/plain" -d "" http://localhost:5232/.sharing/v1/all/info
|
||||
ApiVersion=1
|
||||
Status=success
|
||||
Status='success'
|
||||
FeatureEnabledCollectionByMap=True
|
||||
PermittedCreateCollectionByMap=True
|
||||
FeatureEnabledCollectionByToken=True
|
||||
@@ -183,29 +183,28 @@ Authenticated user as `Owner` has at least read access to `PathMapped`
|
||||
|
||||
* Input
|
||||
|
||||
| Parameter | Owner |
|
||||
| - | - |
|
||||
| Owner | implicit(by authentication) |
|
||||
| PathMapped | mandatory |
|
||||
| User | optional(default:owner) |
|
||||
| Permissions | optional(default:r) |
|
||||
| Enabled | optional(owner) |
|
||||
| Hidden | optional(owner) |
|
||||
| Properties | optional |
|
||||
| Parameter | Type | Requirement |
|
||||
| - | - | - |
|
||||
| PathMapped | str | mandatory |
|
||||
| User | str | optional(default:owner) |
|
||||
| Permissions | str | optional(default:r) |
|
||||
| Enabled | bool | optional(owner/default:False) |
|
||||
| Hidden | bool | optional(owner/default:True) |
|
||||
| Properties | str | optional |
|
||||
|
||||
* Output
|
||||
|
||||
| Parameter | Value |
|
||||
| Parameter | Type | Value |
|
||||
| - | - |
|
||||
| PathOrToken | (autogenerated token) |
|
||||
| PathOrToken | str | (autogenerated token) |
|
||||
|
||||
* Example: TEXT
|
||||
|
||||
```
|
||||
curl -u user:pass -d "PathMapped=/user/testcalendar1/" http://localhost:5232/.sharing/v1/token/create
|
||||
ApiVersion=1
|
||||
Status=success
|
||||
PathOrToken=v1/VQR7AmsVRi2ZlFj_JwGpFx-ES5Goyku-gP_YkLh1zUw=
|
||||
Status='success'
|
||||
PathOrToken='v1/VQR7AmsVRi2ZlFj_JwGpFx-ES5Goyku-gP_YkLh1zUw='
|
||||
```
|
||||
|
||||
###### API Hook "map/create"
|
||||
@@ -220,15 +219,14 @@ Provided `User` has at least read access to `PathOrToken`
|
||||
|
||||
* Input
|
||||
|
||||
| Parameter | Value |
|
||||
| Parameter | Type | Requirement |
|
||||
| - | - |
|
||||
| Owner | implicit(by authentication) |
|
||||
| PathOrToken | mandatory |
|
||||
| PathMapped | mandatory |
|
||||
| User | mandatory |
|
||||
| Permissions | optional(default:r) |
|
||||
| Enabled | optional(owner) |
|
||||
| Hidden | optional(owner) |
|
||||
| PathOrToken | str | mandatory |
|
||||
| PathMapped | str | mandatory |
|
||||
| User | str | mandatory |
|
||||
| Permissions | str | optional(default:r) |
|
||||
| Enabled | bool | optional(owner/default:False) |
|
||||
| Hidden | bool | optional(owner/default:True) |
|
||||
| Properties | optional |
|
||||
|
||||
* Output: result status
|
||||
@@ -252,12 +250,10 @@ Authenticated user as `Owner` or `User`
|
||||
|
||||
* Input
|
||||
|
||||
| Parameter | Filter |
|
||||
| - | - |
|
||||
| Owner | implicit(by authentication) |
|
||||
| User | implicit(by authentication) |
|
||||
| PathOrToken | optional |
|
||||
| PathMapped | optional |
|
||||
| Parameter | Type | Used for |
|
||||
| - | - | - |
|
||||
| PathOrToken | str | optional |
|
||||
| PathMapped | str | optional |
|
||||
|
||||
* Output: plain/csv/json
|
||||
|
||||
@@ -265,8 +261,8 @@ Authenticated user as `Owner` or `User`
|
||||
|
||||
```
|
||||
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
|
||||
map,/user/cal1-from-owner/,/owner/cal1/,owner,user,r,False,False,True,True,1771962120,1771962120
|
||||
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,{}
|
||||
```
|
||||
|
||||
|
||||
@@ -280,9 +276,9 @@ Authenticated user is `Owner`
|
||||
|
||||
* Input
|
||||
|
||||
| Parameter | Type | Owner | User |
|
||||
| - | - | - | - |
|
||||
| PathOrToken | selector | mandatory | not-permitted |
|
||||
| Parameter | Type | Used for | as Owner | as User |
|
||||
| - | - | - | - | - |
|
||||
| PathOrToken | str | selection | mandatory | not-permitted |
|
||||
|
||||
* Output: result status
|
||||
|
||||
@@ -298,16 +294,15 @@ Authenticated user is `Owner` or `User`
|
||||
|
||||
* Input
|
||||
|
||||
| Parameter | Type | Owner | User |
|
||||
| - | - | - | - |
|
||||
| PathOrToken | selector | mandatory | mandatory |
|
||||
| Owner | by authentication | not-permitted | not-permitted |
|
||||
| PathMapped | adjustable | optional | not-permitted |
|
||||
| User | adjustable | optional | not-permitted |
|
||||
| Permissions | adjustable | optional | not-permitted |
|
||||
| Enabled | adjustable | optional(owner) | optional(user) |
|
||||
| Hidden | adjustable | optional(owner) | optional(user) |
|
||||
| Properties | adjustable | optional | optional |
|
||||
| Parameter | Type | Used for | Owner | User |
|
||||
| - | - | - | - | - |
|
||||
| PathOrToken | str | selection | mandatory | mandatory |
|
||||
| PathMapped | str | adjust | optional | not-permitted |
|
||||
| User | str | adjust | optional | not-permitted |
|
||||
| Permissions | str | adjust | optional | not-permitted |
|
||||
| Enabled | bool | adjust | optional(owner) | optional(user) |
|
||||
| Hidden | bool | adjust | optional(owner) | optional(user) |
|
||||
| Properties | str | adjust | optional | optional |
|
||||
|
||||
* Output: result status
|
||||
|
||||
@@ -321,9 +316,9 @@ Authenticated user is `Owner` or `User`
|
||||
|
||||
* Input
|
||||
|
||||
| Parameter | Type | Owner | User |
|
||||
| - | - | - | - |
|
||||
| PathOrToken | selector | mandatory | mandatory |
|
||||
| Parameter | Type | Used for | Owner | User |
|
||||
| - | - | - | - | - |
|
||||
| PathOrToken | selection | mandatory | mandatory |
|
||||
|
||||
* Output: result status
|
||||
|
||||
@@ -340,7 +335,7 @@ Status=success
|
||||
```
|
||||
curl -u owner:pass -d '{"PathOrToken": "/user/cal1-from-owner/", "PathMapped": "/owner/cal1/", "User": "user"} http://localhost:5232/.sharing/v1/map/unhide
|
||||
ApiVersion=1
|
||||
Status=success
|
||||
Status='success'
|
||||
```
|
||||
|
||||
## Properties Overlay
|
||||
|
||||
@@ -72,6 +72,20 @@ API_HOOKS_V1: Sequence[str] = ('list', 'create', 'delete', 'update', 'hide', 'un
|
||||
|
||||
API_SHARE_TOGGLES_V1: Sequence[str] = ('hide', 'unhide', 'enable', 'disable')
|
||||
|
||||
API_TYPES_V1: dict[str, type] = {
|
||||
"ApiVersion": int,
|
||||
"Status": str,
|
||||
"Lines": int,
|
||||
"ShareType": str,
|
||||
"PathOrToken": str,
|
||||
"PathMapped:": str,
|
||||
"Owner": str,
|
||||
"User": str,
|
||||
"Permissions": str,
|
||||
"Enabled": bool,
|
||||
"Hidden": bool,
|
||||
"Properties": str}
|
||||
|
||||
TOKEN_PATTERN_V1: str = "(v1/[a-zA-Z0-9_=\\-]{44})"
|
||||
|
||||
PATH_PATTERN: str = "([a-zA-Z0-9/.\\-]+)" # TODO: extend or find better source
|
||||
@@ -1056,7 +1070,10 @@ class BaseSharing:
|
||||
if output_format == "txt":
|
||||
for key in answer:
|
||||
if key != 'Content':
|
||||
answer_array.append(key + '=' + str(answer[key]))
|
||||
if API_TYPES_V1[key] is bool or API_TYPES_V1[key] is int:
|
||||
answer_array.append(key + '=' + str(answer[key]))
|
||||
else:
|
||||
answer_array.append(key + "='" + str(answer[key]) + "'")
|
||||
if 'Content' in answer and answer['Content'] is not None:
|
||||
csv = io.StringIO()
|
||||
writer = DictWriter(csv, fieldnames=DB_FIELDS_V1, delimiter=';')
|
||||
|
||||
@@ -319,13 +319,13 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** list (form->csv)")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form(sharing_type, "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=not-found" in answer
|
||||
assert "Status='not-found'" in answer
|
||||
assert "Lines=0" in answer
|
||||
|
||||
logging.info("\n*** list (json->text)")
|
||||
json_dict = {}
|
||||
_, headers, answer = self._sharing_api_json(sharing_type, "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
|
||||
assert "Status=not-found" in answer
|
||||
assert "Status='not-found'" in answer
|
||||
assert "Lines=0" in answer
|
||||
|
||||
logging.info("\n*** list (json->json)")
|
||||
@@ -338,10 +338,10 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** create a token -> 200")
|
||||
form_array = ["PathMapped=/owner/collectionL1/"]
|
||||
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "PathOrToken=" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "PathOrToken='" in answer
|
||||
# extract token
|
||||
match = re.search('PathOrToken=(.+)', answer)
|
||||
match = re.search("PathOrToken='(.+)'", answer)
|
||||
if match:
|
||||
token = match.group(1)
|
||||
logging.info("received token %r", token)
|
||||
@@ -360,20 +360,20 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** list/all (form->csv)")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form("all", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=2" in answer
|
||||
|
||||
logging.info("\n*** delete token -> 200")
|
||||
form_array = ["PathOrToken=" + token]
|
||||
_, headers, answer = self._sharing_api_form("token", "delete", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** delete share -> 200")
|
||||
form_array = []
|
||||
form_array.append("PathOrToken=/user/collectionL2-shared-by-owner/")
|
||||
form_array.append("PathMapped=/owner/collectionL2/")
|
||||
_, headers, answer = self._sharing_api_form("map", "delete", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
def test_sharing_api_token_basic(self) -> None:
|
||||
"""share-by-token API tests."""
|
||||
@@ -409,10 +409,10 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** create token#1 (form->text)")
|
||||
form_array = ["PathMapped=/owner/collection1/"]
|
||||
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "PathOrToken=" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "PathOrToken='" in answer
|
||||
# extract token
|
||||
match = re.search('PathOrToken=(.+)', answer)
|
||||
match = re.search("PathOrToken='(.+)'", answer)
|
||||
if match:
|
||||
token1 = match.group(1)
|
||||
logging.info("received token %r", token1)
|
||||
@@ -422,10 +422,10 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** create token#2 (json->text)")
|
||||
json_dict = {'PathMapped': "/owner/collection2/"}
|
||||
_, headers, answer = self._sharing_api_json("token", "create", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Token=" in answer
|
||||
# extract token
|
||||
match = re.search('Token=(.+)', answer)
|
||||
match = re.search("Token='(.+)'", answer)
|
||||
if match:
|
||||
token2 = match.group(1)
|
||||
logging.info("received token %r", token2)
|
||||
@@ -435,14 +435,14 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** lookup token#1 (form->text)")
|
||||
form_array = ["PathOrToken=" + token1]
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
assert "/owner/collection1/" in answer
|
||||
|
||||
logging.info("\n*** lookup token#2 (json->text")
|
||||
json_dict = {'PathOrToken': token2}
|
||||
_, headers, answer = self._sharing_api_json("token", "list", check=200, login="owner:ownerpw", json_dict=json_dict, accept="text/plain")
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
assert "/owner/collection2/" in answer
|
||||
|
||||
@@ -457,7 +457,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** lookup tokens (form->text)")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=2" in answer
|
||||
assert "/owner/collection1/" in answer
|
||||
assert "/owner/collection2/" in answer
|
||||
@@ -465,7 +465,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** lookup tokens (form->csv)")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array, accept="text/csv")
|
||||
assert "Status=success" not in answer
|
||||
assert "Status='success'" not in answer
|
||||
assert "Lines=2" not in answer
|
||||
assert ";".join(sharing.DB_FIELDS_V1) in answer
|
||||
assert "/owner/collection1/" in answer
|
||||
@@ -474,24 +474,24 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** delete token#1 (form->text)")
|
||||
form_array = ["PathOrToken=" + token1]
|
||||
_, headers, answer = self._sharing_api_form("token", "delete", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** lookup token#1 (form->text) -> should not be there anymore")
|
||||
form_array = ["PathOrToken=" + token1]
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=not-found" in answer
|
||||
assert "Status='not-found'" in answer
|
||||
assert "Lines=0" in answer
|
||||
|
||||
logging.info("\n*** lookup tokens (form->text) -> still one should be there")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
|
||||
logging.info("\n*** disable token#2 as owner (form->text)")
|
||||
form_array = ["PathOrToken=" + token2]
|
||||
_, headers, answer = self._sharing_api_form("token", "disable", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** lookup token#2 (json->json) -> check for not enabled")
|
||||
json_dict = {'PathOrToken': token2}
|
||||
@@ -512,7 +512,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
form_array = []
|
||||
form_array.append("PathOrToken=" + token2)
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
assert "True;True;True;True" in answer
|
||||
|
||||
@@ -520,13 +520,13 @@ class TestSharingApiSanity(BaseTest):
|
||||
form_array = []
|
||||
form_array.append("PathOrToken=" + token2)
|
||||
_, headers, answer = self._sharing_api_form("token", "hide", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** lookup token#2 (form->text) -> check for hidden")
|
||||
form_array = []
|
||||
form_array.append("PathOrToken=" + token2)
|
||||
_, headers, answer = self._sharing_api_form("token", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
assert "True;True;True;True" in answer
|
||||
|
||||
@@ -606,10 +606,10 @@ class TestSharingApiSanity(BaseTest):
|
||||
form_array = []
|
||||
form_array.append("PathMapped=" + path_base)
|
||||
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "PathOrToken=" in answer
|
||||
# extract token
|
||||
match = re.search('PathOrToken=(.+)', answer)
|
||||
match = re.search("PathOrToken='(.+)'", answer)
|
||||
if match:
|
||||
token = match.group(1)
|
||||
logging.info("received token %r", token)
|
||||
@@ -620,10 +620,10 @@ class TestSharingApiSanity(BaseTest):
|
||||
form_array = []
|
||||
form_array.append("PathMapped=" + path_base2)
|
||||
_, headers, answer = self._sharing_api_form("token", "create", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "PathOrToken=" in answer
|
||||
# extract token
|
||||
match = re.search('PathOrToken=(.+)', answer)
|
||||
match = re.search("PathOrToken='(.+)'", answer)
|
||||
if match:
|
||||
token2 = match.group(1)
|
||||
logging.info("received token %r", token2)
|
||||
@@ -633,7 +633,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** enable token (form->text)")
|
||||
form_array = ["PathOrToken=" + token]
|
||||
_, headers, answer = self._sharing_api_form("token", "enable", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** fetch collection using invalid token (without credentials)")
|
||||
_, headers, answer = self.request("GET", path_token + "v1/invalidtoken", check=401)
|
||||
@@ -645,7 +645,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** disable token (form->text)")
|
||||
form_array = ["PathOrToken=" + token]
|
||||
_, headers, answer = self._sharing_api_form("token", "disable", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** fetch collection using disabled token (without credentials)")
|
||||
_, headers, answer = self.request("GET", path_token + token, check=401)
|
||||
@@ -653,7 +653,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** enable token (form->text)")
|
||||
form_array = ["PathOrToken=" + token]
|
||||
_, headers, answer = self._sharing_api_form("token", "enable", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
logging.info("\n*** fetch collection using token (without credentials)")
|
||||
_, headers, answer = self.request("GET", path_token + token, check=200)
|
||||
@@ -2207,7 +2207,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** list/all (form->csv)")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form("map", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
|
||||
# read collection
|
||||
@@ -2227,7 +2227,7 @@ class TestSharingApiSanity(BaseTest):
|
||||
logging.info("\n*** list/all (form->csv)")
|
||||
form_array = []
|
||||
_, headers, answer = self._sharing_api_form("map", "list", check=200, login="owner:ownerpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
assert "Lines=1" in answer
|
||||
|
||||
# read collection
|
||||
@@ -2987,7 +2987,7 @@ permissions: RrWw""")
|
||||
form_array.append("Properties='C:calendar-description'='ICAL-USER-NEW'")
|
||||
form_array.append("Properties='ICAL:calendar-color'='#CCCCCC'")
|
||||
_, headers, answer = self._sharing_api_form("map", "update", check=200, login="user:userpw", form_array=form_array)
|
||||
assert "Status=success" in answer
|
||||
assert "Status='success'" in answer
|
||||
|
||||
# verify overlay as user
|
||||
logging.info("\n*** PROPFIND collection user (overlay) -> ok")
|
||||
|
||||
Reference in New Issue
Block a user