From ec8e6cc56eb5e7fc578b1a4a7fa3fe22571ff69f Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Wed, 10 Sep 2025 14:12:50 +0200 Subject: [PATCH 1/6] feat: add Api docs --- .../keycloak.md | 2 +- docs/dev/server/Apis/_category_.json | 4 + docs/dev/server/Apis/grpc_apis/index.md | 52 ++ docs/dev/server/Apis/http/authorization.md | 131 +++++ docs/dev/server/Apis/http/graph/groups.md | 270 +++++++++ docs/dev/server/Apis/http/graph/index.md | 66 +++ .../dev/server/Apis/http/graph/permissions.md | 179 ++++++ docs/dev/server/Apis/http/graph/role.md | 33 ++ docs/dev/server/Apis/http/graph/spaces.md | 492 ++++++++++++++++ docs/dev/server/Apis/http/graph/users.md | 265 +++++++++ docs/dev/server/Apis/http/index.md | 8 + docs/dev/server/Apis/http/tus_upload.md | 248 ++++++++ docs/dev/server/Apis/http/webdav/index.md | 547 ++++++++++++++++++ docs/dev/server/Apis/index.md | 44 ++ static/img/cs3org.png | Bin 0 -> 10081 bytes static/img/grpc-logo.png | Bin 0 -> 161836 bytes static/img/http-logo.png | Bin 0 -> 46701 bytes static/img/oc-apis.drawio.svg | 286 +++++++++ 18 files changed, 2626 insertions(+), 1 deletion(-) create mode 100644 docs/dev/server/Apis/_category_.json create mode 100644 docs/dev/server/Apis/grpc_apis/index.md create mode 100644 docs/dev/server/Apis/http/authorization.md create mode 100644 docs/dev/server/Apis/http/graph/groups.md create mode 100644 docs/dev/server/Apis/http/graph/index.md create mode 100644 docs/dev/server/Apis/http/graph/permissions.md create mode 100644 docs/dev/server/Apis/http/graph/role.md create mode 100644 docs/dev/server/Apis/http/graph/spaces.md create mode 100644 docs/dev/server/Apis/http/graph/users.md create mode 100644 docs/dev/server/Apis/http/index.md create mode 100644 docs/dev/server/Apis/http/tus_upload.md create mode 100644 docs/dev/server/Apis/http/webdav/index.md create mode 100644 docs/dev/server/Apis/index.md create mode 100644 static/img/cs3org.png create mode 100644 static/img/grpc-logo.png create mode 100644 static/img/http-logo.png create mode 100644 static/img/oc-apis.drawio.svg diff --git a/docs/admin/configuration/authentication-and-user-management/keycloak.md b/docs/admin/configuration/authentication-and-user-management/keycloak.md index ec2aebdf..c77bd168 100644 --- a/docs/admin/configuration/authentication-and-user-management/keycloak.md +++ b/docs/admin/configuration/authentication-and-user-management/keycloak.md @@ -307,7 +307,7 @@ IDP_DOMAIN=your-idp-domain.example.com IDP_ACCOUNT_URL=https://your-idp-domain.example.com/realms/openCloud/account ``` -The Docker Compose file `idm/external-idp.yml` contains the complete configuration for each opencloud component. The file `10_opencloud_ldap_schema.ldif` contains the OpenCloud LDAP schema and is loaded during the startup of the OpenLdap container. In this mode, your IdP setup is not part of the openCloud Deployment. +The Docker Compose file `idm/external-idp.yml` contains the complete configuration for each opencloud component. The file `10_opencloud_ldap_schema.ldif` contains the OpenCloud LDAP schema and is loaded during the startup of the OpenLdap container. In this mode, your IdP setup is not part of the OpenCloud Deployment. :::warning diff --git a/docs/dev/server/Apis/_category_.json b/docs/dev/server/Apis/_category_.json new file mode 100644 index 00000000..c345e05a --- /dev/null +++ b/docs/dev/server/Apis/_category_.json @@ -0,0 +1,4 @@ +{ + "label": "Apis", + "position": 1 +} \ No newline at end of file diff --git a/docs/dev/server/Apis/grpc_apis/index.md b/docs/dev/server/Apis/grpc_apis/index.md new file mode 100644 index 00000000..8de33a56 --- /dev/null +++ b/docs/dev/server/Apis/grpc_apis/index.md @@ -0,0 +1,52 @@ +--- +title: gRPC +sidebar_position: 2 +--- + + +## **R**emote   **P**rocedure   **C**alls + +[gRPC](https://grpc.io) is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services. + +## Advantages of gRPC + +### 🚀 Performance + +gRPC uses http/2 by default and is faster than REST. When using protocol buffers for encoding, the information comes on and off the wire much faster than JSON. Latency is an important factor in distributed systems. JSON encoding creates a noticeable factor of latency. For distributed systems and high data loads, gRPC can actually make an important difference. Other than that, gRPC supports multiple calls via the same channel and the connections are bidirectional. A single connection can transmit requests and responses at the same time. gRPC keeps connections open to reuse the same connection again which prevents latency and saves bandwidth. + +### 🛡️ Robustness + +gRPC empowers better relationships between clients and servers. The rules of communication are strictly enforced. That is not the case in REST calls, where the client and the server can send and receive anything they like and hopefully the other end understands what to do with it. In gRPC, to make changes to the communication, both client and server need to change accordingly. This prevents mistakes specially in microservice architectures. +### 🔍 Debuggability + +gRPC requests are re-using the same context and can be tracked or traced across multiple service boundaries. +This helps to identify slow calls and see what is causing delays. It is possible to cancel requests which cancels +them on all involved services. + +### 📦 Microservices + +gRPC has been evolving and has become the best option for communication between microservices because of its unmatched +performance and its polyglot nature. One of the biggest strengths of microservices is the freedom of programming +languages and technologies. By using gRPC we can leverage all the advantages of strictly enforced communication +standards combined with freedom of choice between different programming languages - whichever would fit best. + +:::info gRPC Advantages + +- http/2 +- protocol buffers +- reusable connections +- multi language support + +::: + +## CS3 APIs + +![CS3 Organization](/img/cs3org.png) + +The [CS3 APIs](https://github.com/cs3org/cs3apis) connect storages and application providers. + +The CS3 APIs follow Google and Uber API design guidelines, specially on error handling and naming convention. You can read more about these +guidelines at https://cloud.google.com/apis/design/ and https://github.com/uber/prototool/blob/dev/style/README.md. + +The CS3 APIs use [Protocol Buffers version 3 (proto3)](https://github.com/protocolbuffers/protobuf) as their +Interface Definition Language (IDL) to define the API interface and the structure of the payload messages. diff --git a/docs/dev/server/Apis/http/authorization.md b/docs/dev/server/Apis/http/authorization.md new file mode 100644 index 00000000..223cd474 --- /dev/null +++ b/docs/dev/server/Apis/http/authorization.md @@ -0,0 +1,131 @@ +--- +title: Authorization +sidebar_position: 40 +--- + +In its default configuration, OpenCloud supports three authentication methods as outlined on the [OIDC official site](https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3): +1. Authorization Code Flow +2. Implicit Flow +3. Hybrid Flow + +For detailed information on OpenCloud's support for OpenID Connect (OIDC), please consult the [Official Documentation](../../../../admin/configuration/authentication-and-user-management). + +While selecting an OpenCloud client for authentication, take note of specific limitations such as the `Redirect URI`: + +| Source | Redirect URI | +|---------|------------------------------------------| +| Android | oc://android.opencloud.eu | +| iOS | oc://ios.opencloud.eu | +| Desktop | http://127.0.0.1
http://localhost | + +In this example, the desktop app's `client_id` are being used. + +```bash +client_id=OpenCloudDesktop +``` + +## Authorization Code Flow + +1. Requesting authorization + + To initiate the OIDC Code Flow, you can use tools like curl and a web browser. + The user should be directed to a URL to authenticate and give their consent (bypassing consent is against the standard): + + ```plaintext + https://cloud.opencloud.test/signin/v1/identifier/_/authorize?client_id=client_id&scope=openid+profile+email+offline_access&response_type=code&redirect_uri=http://path-to-redirect-uri + ``` + + After a successful authentication, the browser will redirect to a URL that looks like this: + + ```plaintext + http://path-to-redirect-uri?code=mfWsjEL0mc8gx0ftF9LFkGb__uFykaBw&scope=openid%20profile%20email%20offline_access&session_state=32b08dd...&state= + ``` + + For the next step extract the code from the URL. + + In the above example, + the code is `mfWsjEL0mc8gx0ftF9LFkGb__uFykaBw` + +2. Requesting an access token + + The next step in the OIDC Code Flow involves an HTTP POST request + to the token endpoint of the **OpenCloud Identity Server**. + + ```bash + curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ + -d "grant_type=authorization_code" \ + -d "code=3a3PTcO-WWXfN3l1mDN4u7G5PzWFxatU" \ + -d "redirect_uri=http:path-to-redirect-uri" \ + -d "client_id=client_id" + ``` + + Response looks like this: + ```json + { + "access_token": "eyJhbGciOid...", + "token_type": "Bearer", + "id_token": "eyJhbGciOi...", + "refresh_token": "eyJhbGciOiJ...", + "expires_in": 300 + } + ``` + +3. Refreshing an access token + + If the access token has expired, you can get a new one using the refresh token. + ```bash + curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ + -d "grant_type=refresh_token" \ + -d "refresh_token=eyJhbGciOiJ..." \ + -d "redirect_uri=http://path-to-redirect-uri" \ + -d "client_id=client_id" + ``` + + Response looks like this: + ```json + { + "access_token": "eyJhbGciOi...", + "token_type": "Bearer", + "expires_in": 300 + } + ``` + +## Implicit Code Flow + +When using the implicit flow, tokens are provided in a URI fragment of the redirect URL. +Valid values for the `response_type` request parameter are: + +- token +- id_token token + +:::warning Important Warning +If you are using the implicit flow, `nonce` parameter is required in the initial `/authorize` request. +`nonce=pL3UkpAQPZ8bTMGYOmxHY/dQABin8yrqipZ7iN0PY18=` + +bash command to generate cryptographically random value +```bash +openssl rand -base64 32 +``` +::: +The user should be directed to a URL to authenticate and give their consent (bypassing consent is against the standard): +```bash +https://cloud.opencloud.test/signin/v1/identifier/_/authorize?client_id=client_id&scope=openid+profile+email+offline_access&response_type=id_token+token&redirect_uri=http://path-to-redirect-uri&nonce=pL3UkpAQPZ8bTMGYOmxHY/dQABin8yrqipZ7iN0PY18= + ``` + +After a successful authentication, the browser will redirect to a URL that looks like this: +```bash +http://path-to-redirect-uri#access_token=eyJhbGciOiJQUzI...&expires_in=300&id_token=eyJhbGciOiJ...&scope=email%20openid%20profile&session_state=c8a1019f5e054d...&state=&token_type=Bearer +``` + +For the next step, extract the access_token from the URL. +```bash +access_token = 'eyJhbGciOiJQ...' + ``` + +## Hybrid Flow +The Hybrid Flow in OpenID Connect melds features from both the Implicit and Authorization Code flows. It allows clients to directly retrieve certain tokens from the Authorization Endpoint, yet also offers the option to acquire additional tokens from the Token Endpoint. + +The Authorization Server redirects back to the client with appropriate parameters in the response, based on the value of the response_type request parameter: +- code token +- code id_token +- code id_token token diff --git a/docs/dev/server/Apis/http/graph/groups.md b/docs/dev/server/Apis/http/graph/groups.md new file mode 100644 index 00000000..0fef118e --- /dev/null +++ b/docs/dev/server/Apis/http/graph/groups.md @@ -0,0 +1,270 @@ +--- +title: Groups +sidebar_position: 40 +--- + +## Groups API + +The Groups API is implementing a subset of the functionality of the +[MS Graph Group resource](https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0) +The JSON representation of a Group as handled by the Groups API looks like this: + +``` +{ + "displayName": "group", + "id": "f0d97060-da16-4b0d-9fa4-d1ec43afc5f1" +} +``` + +Our implementation currently supports two Attributes for a Group: + +| Attribute | Description | +|-------------|-----------------------------------------------------------------------------------------------------------------------------| +| displayName | The groups name | +| id | An unique, stable readonly identifier for the group that stays the same for the whole lifetime of the Group, usually a UUID | + + +### Reading groups + +#### `GET /groups` + +Returns a list of all groups + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/groups' -u user:password + +``` + +Response: + +``` +{ + "value": [ + { + "displayName": "group", + "id": "38580a2e-7018-42ed-aff6-b2af0b4e9790" + }, + { + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" + } + ] +} +``` + + +#### `GET /groups?$expand=members` + +Returns a list of all groups including its members + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/groups?$expand=members' -u user:password + +``` + +Response: + +``` +{ + "value": [ + { + "displayName": "group", + "id": "38580a2e-7018-42ed-aff6-b2af0b4e9790", + "members": [ + { + "displayName": "user1", + "id": "2e7b7e23-6c42-4d34-81b0-2bed34e51983", + "mail": "user1@example.org", + "onPremisesSamAccountName": "user1" + }, + { + "displayName": "user2", + "id": "b45c9e35-0d95-4165-96bc-68bff4a316ed", + "mail": "user2@example.org", + "onPremisesSamAccountName": "user2" + } + ] + }, + { + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f", + "members": [ + { + "displayName": "user3", + "id": "026fbfef-79ef-4f5d-887b-9eaf42777239", + "mail": "user3@example.org", + "onPremisesSamAccountName": "user3" + } + ] + } + ] +} +``` + +#### `GET /groups/{groupid}` + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f' -u user:password +``` + +Response: + +``` +{ + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" +} +``` + +#### `GET /groups/{groupid}?$expand=members` + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f?$expand=members' -u user:password +``` + +Response: + +``` +{ + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f", + "members": [ + { + "displayName": "user3", + "id": "026fbfef-79ef-4f5d-887b-9eaf42777239", + "mail": "user3@example.org", + "onPremisesSamAccountName": "user3" + } + ] +} +``` +### Getting Group Members + +#### `GET /groups/{groupid}/members` + +Returns a list of User objects that are members of a group. + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members' -u user:password + +``` + +Response: + +``` +[ + { + "displayName": "Test User", + "id": "c54b0588-7157-4521-bb52-c1c8ca84ea71", + "mail": "example@example.org", + "onPremisesSamAccountName": "example" + }, + { + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" + } +] +``` + +### Creating / Updating Groups + +#### `POST /groups` + +Use this to create a new group. +h +##### Request Body + +Note the missing `"id"` Attribute. It will be generated by the server: + +``` +{ + "displayName": "Example Users" +} +``` + +##### Response + +When successful, the response will return the new group including the newly allocated `"id"`: + +``` +{ + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" +} +``` + +#### `DELETE /groups/{id}` + +Example: + +``` +curl -k --request DELETE 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f' -u user:password +``` + +When successful the API returns no response body and the HTTP status code 204 (No Content) + +#### `PATCH /groups/\{id\}` + +Updating attributes of a single group is supposed to be done with a patch request. This is however currently not fully +implemented for our write-enabled backends. The PATCH request can however be used to add multiple members to a group at once. +See below. + +### Adding a single member to a group + +#### `POST /groups/{id}/members/$ref` + +The request body contains a single attribute "`@odata.id`" referencing the new member of the group by URI. Example: + +``` +curl -k --header "Content-Type: application/json" \ + --request POST --data \ + '{ "@odata.id": "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51" }' \ + 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members/$ref' -u user:password + +``` + +When successful the API returns no response body and the HTTP status code 204 (No Content) + +### Adding multiple members in a single request + +#### `PATCH /groups/\{id\}` + +The request body contains the attribute `members@odata.bind` holding a list of URI references for the new members. +Example: + +``` +{ + "members@odata.bind": [ + "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51", + "https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71" + ] +} +``` + +When successful the API returns no response body and the HTTP status code 204 (No Content) + +### Removing a member + +#### `DELETE /groups/{groupid}/members/{id}/$ref` + +Example + +``` +curl -k --request DELETE \ + 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members/4c510ada-c86b-4815-8820-42cdf82c3d51/$ref' \ + -u user:password +``` + +When successful the API returns no response body and the HTTP status code 204 (No Content) diff --git a/docs/dev/server/Apis/http/graph/index.md b/docs/dev/server/Apis/http/graph/index.md new file mode 100644 index 00000000..944e5094 --- /dev/null +++ b/docs/dev/server/Apis/http/graph/index.md @@ -0,0 +1,66 @@ +--- +title: LibreGraph +sidebar_position: 1 +--- + +The LibreGraph API is a REST Api which is inspired by the [Microsoft Graph API](https://developer.microsoft.com/en-us/graph). It tries to stay compliant with the Microsoft Graph API and aims to be the Next Generation Api in OpenCloud where we want to support most of the features of the platform. +The [API specification](https://github.com/opencloud-eu/libre-graph-api) is available in the OpenApi 3 standard and there are generated client and server [SDKs](https://github.com/opencloud-eu/libre-graph-api#clients) available. You can browse the API with the [Swagger UI](https://docs.opencloud.eu/swagger/libre-graph-api/). + +## Calling the LibreGraph API + +```sh +{HTTP method} https://cloud.opencloud.test/graph/{version}/{resource}?{query-parameters} +``` + +The request component consists of: + +| Component | Description | +|----------------------|-------------------------------------------------------------------------| +| `{HTTP method}` | The HTTP method which is used in the request. | +| `{version}` | The version of the LibreGraph API used by the client. | +| `{resource}` | The LibreGraph Resource which the client is referencing in the request. | +| `{query-parameters}` | Optional parameters for the request to customize the response. | + +### HTTP methods + +| Method | Description | +|--------|-------------------------------| +| GET | Read data from a resource. | +| POST | Create a new resource. | +| PATCH | Update an existing resource. | +| PUT | Replace an existing resource. | +| DELETE | Delete an existing resource. | + +The methods `GET` and `DELETE` need no request body. The methods `POST`, `PATCH` and `PUT` require a request body, normally in JSON format to provide the needed values. + +### Version + +OpenCloud currently provides the version `v1.0`. + +### Resource + +A resource could be an entity or a complex type and is usually defined by properties. Entities are always recognizable by an `Id` property. The URL contains the resource which you are interacting with e.g. `/me/drives` or `/groups/{group-id}`. + +Each resource could possibly require different permissions. Usually you need permissions on a higher level for creating or updating an existing resource than for reading. + +### Query parameters + +Query parameters can be OData system query options, or other strings that a method accepts to customize its response. + +You can use optional OData system query options to include more or fewer properties than the default response, filter the response for items that match a custom query, or provide additional parameters for a method. + +For example, adding the following filter parameter restricts the drives returned to only those with the driveType property of `project`. + +```shell +GET https://cloud.opencloud.test/graph/v1.0/drives?$filter=driveType eq 'project' +``` +For more information about OData query options please check the [API specification](https://github.com/opencloud-eu/libre-graph-api) and the provided examples. + +### Authorization + +For development purposes the examples in the developer documentation use Basic Auth. It is disabled by default and should only be enabled by setting `PROXY_ENABLE_BASIC_AUTH` in [the proxy](../../../services/proxy/configuration/#environment-variables) for development or test instances. + +To authenticate with a Bearer token or OpenID Connect access token replace the `-u user:password` Basic Auth option of curl with a `-H 'Authorization: Bearer '` header. A `` can be obtained by copying it from a request in the browser, although it will time out within minutes. To automatically refresh the OpenID Connect access token an ssh-agent like solution like [oidc-agent](https://github.com/indigo-dc/oidc-agent) should be used. The graph endpoints that support a preconfigured token can be found in the [API specification](https://github.com/opencloud-eu/libre-graph-api) + +## Resources + diff --git a/docs/dev/server/Apis/http/graph/permissions.md b/docs/dev/server/Apis/http/graph/permissions.md new file mode 100644 index 00000000..f7763165 --- /dev/null +++ b/docs/dev/server/Apis/http/graph/permissions.md @@ -0,0 +1,179 @@ +--- +title: Permissions +sidebar_position: 50 +--- + +## Permissions API + +The Permissions API is implementing a subset of the functionality of the +[MS Graph Permission resource](https://learn.microsoft.com/en-us/graph/api/resources/permission?view=graph-rest-1.0). + +### Example Permissions + +The JSON representation of a Drive, as handled by the Spaces API, looks like this: +````json +{ + "@libre.graph.permissions.roles.allowedValues": [ + { + "id": "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5", + "description": "Allows reading the shared file or folder", + "displayName": "Viewer", + "@libre.graph.weight": 1 + }, + { + "id": "fb6c3e19-e378-47e5-b277-9732f9de6e21", + "description": "Allows reading and writing the shared file or folder", + "displayName": "Editor", + "@libre.graph.weight": 2 + }, + { + "id": "312c0871-5ef7-4b3a-85b6-0e4074c64049", + "description": "Allows managing a space", + "displayName": "Manager", + "@libre.graph.weight": 3 + }, + { + "id": "4916f47e-66d5-49bb-9ac9-748ad00334b", + "description": "Allows creating new files", + "displayName": "File Drop", + "@libre.graph.weight": 4 + } + ], + "@libre.graph.permissions.actions.allowedValues": [ + "libre.graph/driveItem/basic/read", + "libre.graph/driveItem/permissions/read", + "libre.graph/driveItem/upload/create", + "libre.graph/driveItem/standard/allTasks", + "libre.graph/driveItem/upload/create" + ], + "value": [ + { + "id": "67445fde-a647-4dd4-b015-fc5dafd2821d", + "link": { + "type": "view", + "webUrl": "https://cloud.example.org/s/fhGBMIkKFEHWysj" + } + }, + { + "id": "34646ab6-be32-43c9-89e6-987e0c237e9b", + "roles": [ + "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" + ], + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Albert Einstein" + } + } + ] + }, + { + "id": "81d5bad3-3eff-410a-a2ea-eda2d14d4474", + "roles": [ + "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" + ], + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Albert Einstein" + } + } + ] + }, + { + "id": "b470677e-a7f5-4304-8ef5-f5056a21fff1", + "roles": [ + "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" + ], + "grantedToV2": [ + { + "user": { + "id": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + "displayName": "Marie Skłodowska Curie" + } + } + ] + }, + { + "id": "453b02be-4ec2-4e7d-b576-09fc153de812", + "roles": [ + "fb6c3e19-e378-47e5-b277-9732f9de6e21" + ], + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Albert Einstein" + } + } + ], + "expirationDateTime": "2018-07-15T14:00:00.000Z" + }, + { + "id": "86765c0d-3905-444a-9b07-76201f8cf7df", + "roles": [ + "312c0871-5ef7-4b3a-85b6-0e4074c64049" + ], + "grantedToV2": [ + { + "group": { + "id": "167cbee2-0518-455a-bfb2-031fe0621e5d", + "displayName": "Philosophy Haters" + } + } + ] + }, + { + "id": "c42b5cbd-2d65-42cf-b0b6-fb6d2b762256", + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Albert Einstein" + } + } + ], + "@libre.graph.permissions.actions": [ + "libre.graph/driveItem/basic/read", + "libre.graph/driveItem/path/update" + ] + } + ] +} +```` + +## Creating Share Invitation / Link + +### Create a link share `POST /drives/{drive-id}/items/{item-id}/createLink` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/CreateLink + +### Create a user/group share `POST /drives/{drive-id}/items/{item-id}/invite` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/Invite + +## Reading Permissions + +### List the effective sharing permissions on a driveitem `GET /drives/{drive-id}/items/{item-id}/permissions` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/ListPermissions + +### List Get sharing permission for a file or folder `GET /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/GetPermission + +## Updating Permissions + +### Updating sharing permission `POST /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/UpdatePermission + +### Set password of permission `POST /drives/{drive-id}/items/{item-id}/permissions/{perm-id}/setPassword` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/SetPermissionPassword + +### Deleting permission `DELETE /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/DeletePermission \ No newline at end of file diff --git a/docs/dev/server/Apis/http/graph/role.md b/docs/dev/server/Apis/http/graph/role.md new file mode 100644 index 00000000..64c4b28f --- /dev/null +++ b/docs/dev/server/Apis/http/graph/role.md @@ -0,0 +1,33 @@ +--- +title: Role +sidebar_position: 60 +--- + +## Role API + +The Roles API is implementing a subset of the functionality of the +[MS Graph Role Management](https://learn.microsoft.com/en-us/graph/api/resources/rolemanagement?view=graph-rest-1.0). + +## Role Management + +### List roleDefinitions `GET /v1beta1/roleManagement/permissions/roleDefinitions` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/roleManagement/ListPermissionRoleDefinitions + +### Get unifiedRoleDefinition `GET /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/roleManagement/GetPermissionRoleDefinition + +## Role Assignment + +### Get appRoleAssignments of a user `GET /v1.0/users/{user-id}/appRoleAssignments` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.ListAppRoleAssignments + +### Grant an appRoleAssignment to a user `POST /v1.0/users/{user-id}/appRoleAssignments` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.CreateAppRoleAssignments + +### Delete the appRoleAssignment from a user `DELETE /v1.0/users/{user-id}/appRoleAssignments/{appRoleAssignment-id}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.DeleteAppRoleAssignments \ No newline at end of file diff --git a/docs/dev/server/Apis/http/graph/spaces.md b/docs/dev/server/Apis/http/graph/spaces.md new file mode 100644 index 00000000..1eebb967 --- /dev/null +++ b/docs/dev/server/Apis/http/graph/spaces.md @@ -0,0 +1,492 @@ +--- +title: Spaces +sidebar_position: 20 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +## Spaces API + +The Spaces API is implementing a subset of the functionality of the +[MS Graph Drives resource](https://learn.microsoft.com/en-us/graph/api/resources/drive?view=graph-rest-1.0). + +### Example Space + +The JSON representation of a Drive, as handled by the Spaces API, looks like this: +````json +{ + "driveAlias": "project/mars", + "driveType": "project", + "id": "storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925", + "lastModifiedDateTime": "2023-01-24T21:19:26.417055+01:00", + "name": "Mars", + "owner": { + "user": { + "displayName": "", + "id": "89ad5ad2-5fdb-4877-b8c9-601a9670b925" + } + }, + "quota": { + "remaining": 999853685, + "state": "normal", + "total": 1000000000, + "used": 146315 + }, + "root": { + "eTag": "\"910af0061161c42d8d1224df6c4a2527\"", + "id": "storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925", + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" + } + } + ], + "roles": [ + "manager" + ] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925" + }, + "special": [ + { + "eTag": "\"f97829324f63ce778095334cfeb0097b\"", + "file": { + "mimeType": "image/jpeg" + }, + "id": "storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925!40171bea-3263-47a8-80ef-0ca20c37f45a", + "lastModifiedDateTime": "2022-02-15T17:11:50.000000496+01:00", + "name": "Mars_iStock-MR1805_20161221.jpeg", + "size": 146250, + "specialFolder": { + "name": "image" + }, + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925%2189ad5ad2-5fdb-4877-b8c9-601a9670b925/.space/Mars_iStock-MR1805_20161221.jpeg" + }, + { + "eTag": "\"ff38b31d8f109a4fbb98ab34499a3379\"", + "file": { + "mimeType": "text/markdown" + }, + "id": "storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925!e2167612-7578-46e2-8ed7-971481037bc1", + "lastModifiedDateTime": "2023-01-24T21:10:23.661841+01:00", + "name": "readme.md", + "size": 65, + "specialFolder": { + "name": "readme" + }, + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925%2189ad5ad2-5fdb-4877-b8c9-601a9670b925/.space/readme.md" + } + ], + "webUrl": "https://localhost:9200/f/storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925" +} +```` + +## Creating Spaces + +### Create a single space `POST /drives` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives/CreateDrive + +### Create a space item (Enable sync) `POST /drives/\{drive-id\}/root/children` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/CreateDriveItem + +## Reading Spaces + +```shell +GET https://cloud.opencloud.test/graph/{version}/{me/}drives/?{query-parameters} +``` + +| Component | Description | +|----------------------|------------------------------------------------------------------------------------------------------------------------| +| \{version\} | The version of the LibreGraph API used by the client. | +| \{/me\} | The `me` component of the part is optional. If used, you only see spaces where the acting user is a regular member of. | +| \{query-parameters\} | Optional parameters for the request to customize the response. | + +### List all spaces `GET /drives` + +Returns a list of all available spaces, even ones where the acting user is not a regular member of. You need elevated permissions to do list all spaces. If you don't have the elevated permissions, the result is the same like `GET /me/drives`. + + +:::info[Multiple Administration Personas] + +The openCloud spaces concept draws a strict line between users which can work with the content of a space and others who have the permission to manage the space. A user which is able to manage quota and space metadata does not necessarily need to be able to access the content of a space. + +**Space Admin**\ +There is a global user role "Space Admin" which grants users some global permissions to manage space quota and some space metadata. This Role enables the user also to disable, restore and delete spaces. He cannot manage space members. + +**Space Manager**\ +The "Space Manager" is a user which is a regular member of a space because he has been invited. In addition to being part of a space the user can also manage the memberships of the space. + +::: + +### List My Spaces `GET /me/drives` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/me.drives/ListMyDrives + +## Modifying Spaces + +Modify the properties of a space. You need elevated permissions to execute this request. + +### Set the space quota to 5GB `PATCH /drives/\{drive-id\}` + +To limit the quota of a space you need to set the `quota.total` value. The API response will give back all actual quota properties. + +````json +{ + "quota": { + "remaining": 5368709120, + "state": "normal", + "total": 5368709120, + "used": 0 + } +} +```` + +| Attribute | Description | +|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| remaining | The remaining disk space in `bytes`. If the quota is not limited, this will show the total available disk space. | +| state | The state of the space in regards to quota usage. This can be used for visual indicators. It can be `normal`(\<75%), `nearing`(between 75% and 89%), `critical`(between 90% and 99%) and `exceeded`(100%). | +| total | The space id. The value needs to be a space ID. | +| used | The used disk space in bytes. | + + + +```shell +curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff' \ +-H 'Content-Type: application/json' \ +--data-raw '{ + "quota": { + "total": 5368709120 + } +}' +``` + + +```json title="Response" {17} +{ + "description": "Marketing team resources", + "driveAlias": "project/marketing", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-18T17:13:48.385204589+01:00", + "name": "Marketing", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "remaining": 5368709120, + "state": "normal", + "total": 5368709120, + "used": 0 + }, + "root": { + "eTag": "\"f91e56554fd9305db81a93778c0fae96\"", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" + } + } + ], + "roles": [ + "manager" + ] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" +} +``` + + + +### Change the space name, subtitle and alias `PATCH /drives/\{drive-id\}` + +You can change multiple space properties in one request as long as you submit a valid JSON body. Please be aware that some properties need different permissions. + + + +```shell +curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff' \ +-H 'Content-Type: application/json' \ +--data-raw '{ + "name": "Mars", + "description": "Mission to mars", + "driveAlias": "project/mission-to-mars" +}' +``` + + + +```json title="Response" {2,3,7} +{ + "description": "Mission to mars", + "driveAlias": "project/mission-to-mars", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", + "name": "Mars", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "remaining": 15, + "state": "normal", + "total": 15, + "used": 0 + }, + "root": { + "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" + } + } + ], + "roles": [ + "manager" + ] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" +} +``` + + + +## Disabling / Deleting Spaces + +### Disable a space `DELETE /drives/\{drive-id\}` + +This operation will make the space content unavailable for all space members. No data will be deleted. + + + +```shell +curl -L -k -X DELETE 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff/' +``` + + + + +This response has no body value. + +A disabled space will appear in listings with a `root.deleted.state=trashed` property. The space description and the space image will not be readable anymore. + +```json title="Response" {18,19,20} +{ + "description": "Marketing team resources", + "driveAlias": "project/marketing", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", + "name": "Marketing", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "total": 15 + }, + "root": { + "deleted": { + "state": "trashed" + }, + "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" + } + } + ], + "roles": [ + "manager" + ] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" +} +``` + + + + +### Restore a space `PATCH /drives/\{drive-id\}` + +This operation will make the space content available again to all members. No content will be changed. + +To restore a space, the Header `Restore: T` needs to be set. + + + +```shell +curl -L -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff/' \ +-H 'Restore: T' \ +-H 'Content-Type: text/plain' \ +--data-raw '{}' +``` + +:::info[Body value] + +This request needs an empty body (--data-raw '{}') to fulfil the standard libregraph specification even when the body is not needed. + +::: + + + + +```json +{ + "description": "Marketing team resources", + "driveAlias": "project/marketing", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", + "name": "Marketing", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "remaining": 15, + "state": "normal", + "total": 15, + "used": 0 + }, + "root": { + "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" + } + } + ], + "roles": [ + "manager" + ] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" +} +``` + + + + +### Permanently delete a space `DELETE /drives/\{drive-id\}` + +This operation will delete a space and all its data permanently. This is restricted to spaces which are already disabled. + +To delete a space, the Header `Purge: T` needs to be set. + + + + +```shell title="Request" {2} +curl -L -X DELETE 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff' \ +-H 'Purge: T' +``` + +:::warning[Data will be deleted] + +This request will delete a space and all its content permanently. This operation cannot be reverted. + +::: + + + + +This response has no body value. + + + + +The space to be deleted was not disabled before. + +```json +{ + "error": { + "code": "invalidRequest", + "innererror": { + "date": "2023-01-24T19:57:19Z", + "request-id": "f62af40f-bc18-475e-acd7-e9008d6bd326" + }, + "message": "error: bad request: can't purge enabled space" + } +} +``` + + + +## Sharing Space + +### Add member to space `POST /drives/\{drive-id\}/root/invite` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/Invite + +### Sharing space as a link `POST /drives/\{drive-id\}/root/createLink` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/CreateLinkSpaceRoot + +## Reading Space Permissions + +### Listing permissions of a space `GET /drives/\{drive-id\}/root/permissions` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/ListPermissionsSpaceRoot + +## Modifying / Deleting Space Permissions + +### Update permissions of a drive `PATCH /drives/\{drive-id\}/root/permissions/\{perm-id\}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/UpdatePermissionSpaceRoot + +### Set password of a link share `POST /drives/\{drive-id\}/root/permissions/\{perm-id\}/setPassword` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/SetPermissionPasswordSpaceRoot + +### Removing acess to a space `DELETE /drives/\{drive-id\}/root/permissions/\{perm-id\}` + +https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/DeletePermissionSpaceRoot diff --git a/docs/dev/server/Apis/http/graph/users.md b/docs/dev/server/Apis/http/graph/users.md new file mode 100644 index 00000000..7d7a7c3a --- /dev/null +++ b/docs/dev/server/Apis/http/graph/users.md @@ -0,0 +1,265 @@ +--- +title: Users +sidebar_position: 30 +--- + +## Users API + +The Users API is implementing a subset of the functionality of the +[MS Graph User resource](https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0) +The JSON representation of a User handled by the Users API looks like this: + +``` +{ + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" +} +``` + +Our implementation currently supports only a limited set of Attributes of Users: + +| Attribute | Description | +|--------------------------|---------------------------------------------------------------------------------------------------------------------------| +| displayName | The full name of the user, usually a combination of given name and last name | +| mail | The user's email address | +| onPremisesSamAccountName | The loginname/account name of the user | +| id | An unique, stable readonly identifier for the user that stays the same for the whole lifetime of the User, usually a UUID | +| passwordProfile | Contains the password of the users. This is only present when updating or creating users. It is never returned by the API | + + +### Reading users + +#### `GET /me` + +Returns the user object of the currently signed-in user + +Example: +``` +curl -k 'https://localhost:9200/graph/v1.0/me' -u user:password +``` + +Response: +``` +{ + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" +} +``` + +#### `GET /users` + +Returns a list of all users + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/users' -u user:password + +``` + +Response: + +``` +{ + "value": [ + { + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" + }, + { + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss" + } + ] +} +``` + +#### `GET /users?$expand=memberOf` + +Returns a list of all users + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/users?$expand=memberOf' -u user:password + +``` + +Response: + +``` +{ + "value": [ + { + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + }, + { + "displayName": "sailing-lovers", + "id": "6040aa17-9c64-4fef-9bd0-77234d71bad0" + }, + { + "displayName": "violin-haters", + "id": "dd58e5ec-842e-498b-8800-61f2ec6f911f" + }, + { + "displayName": "physics-lovers", + "id": "262982c1-2362-4afa-bfdf-8cbfef64a06e" + } + ], + }, + { + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + } + ], + } + ] +} +``` + +#### `GET /users/{userid or accountname}` + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/users/058bff95-6708-4fe5-91e4-9ea3d377588b' -u user:password +``` + +Response: + +``` +{ + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss" +} +``` + +#### `GET /users/{userid or accountname}?$expand=memberOf` + +Example: + +``` +curl -k 'https://localhost:9200/graph/v1.0/users/058bff95-6708-4fe5-91e4-9ea3d377588b?$expand=memberOf' -u user:password +``` + +Response: + +``` +{ + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + } + ], +} +``` + +### Creating / Updating Users + +#### `POST /users` + +Use this to create a new user. + +##### Request Body + +Note the missing `"id"` Attribute. It will be generated by the server: + +``` +{ + "displayName": "Example User", + "mail": "example@example.org", + "onPremisesSamAccountName": "example", + "passwordProfile": { + "password": "ThePassword" + } +} +``` + +##### Response + +When successful, the response will return the new user, without the password, but including the newly allocated `"id"`: + +``` +{ + "displayName":"Example User", + "id":"c067b139-c91c-4e47-8be6-669156a0587b", + "mail":"example@example.org", + "onPremisesSamAccountName":"example" +} +``` + +#### `DELETE /users/{id}` + +Example: + +``` +curl -k --request DELETE 'https://localhost:9200/graph/v1.0/users/c067b139-c91c-4e47-8be6-669156a0587b' -u user:password +``` + +When successful the API returns no response body and the HTTP status code 204 (No Content) + + +#### `PATCH /users/{id}` + +Updating attributes of a single user can be done with a patch request. The Request Body contains the new values of the attributes +to be updated. E.g. to update the `displayName` Attribute: + +``` + curl -k --header "Content-Type: application/json" \ + --request PATCH --data '{"displayName": "Test User" }' \ + 'https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71' -u user:password +``` + +Similar to creating a user via `POST`, the `PATCH` request will return the user object containing the new attribute values. + +### Change password + +#### `POST /me/changePassword` + +Users can change their own password by sending a POST request to `/me/changePassword` + +##### Request Body + +``` +{ + "currentPassword": "current", + "newPassword": "new" +} + +``` + +When successful the API returns no response body and the HTTP status code 204 (No Content) + +``` + curl -i -k --header "Content-Type: application/json" \ + --request POST --data '{"currentPassword": "current", "newPassword": "new" }' \ + 'https://localhost:9200/graph/v1.0/me/changePassword' -u user:current +``` diff --git a/docs/dev/server/Apis/http/index.md b/docs/dev/server/Apis/http/index.md new file mode 100644 index 00000000..a24d8ac1 --- /dev/null +++ b/docs/dev/server/Apis/http/index.md @@ -0,0 +1,8 @@ +--- +title: HTTP +sidebar_position: 1 +--- + +The [Hypertext Transfer Protocol (HTTP)](https://www.rfc-editor.org/rfc/rfc2616) is a stateless application-level protocol for distributed, collaborative, hypertext information systems. HTTP is the foundation of data communication for the World Wide Web, where hypertext documents include hyperlinks to other resources that the user can easily access, for example by a mouse click or by tapping the screen in a web browser. + +Development of HTTP was initiated by Tim Berners-Lee at CERN in 1989 and summarized in a simple document describing the behavior of a client and a server using the first HTTP protocol version that was named 0.9. That first version of HTTP protocol soon evolved into a more elaborated version that was the first draft toward a far future version 1.0 diff --git a/docs/dev/server/Apis/http/tus_upload.md b/docs/dev/server/Apis/http/tus_upload.md new file mode 100644 index 00000000..22b19fce --- /dev/null +++ b/docs/dev/server/Apis/http/tus_upload.md @@ -0,0 +1,248 @@ +--- +title: Resumable Upload +sidebar_position: 21 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +OpenCloud supports the tus resumable-upload protocol, which is a robust, modular, and open protocol designed to resume large file uploads reliably over HTTP. +In situations where file uploads might be interrupted due to network issues, browser crashes, or other unforeseen interruptions, +tus ensures that uploads can be resumed from the point of failure without losing data. +This documentation shows some basic examples, refer [tus official site](https://tus.io/protocols/resumable-upload) for more details. + +## Supported tus Features + +The backend announces certain tus features to clients. WebDAV responses come with tus HTTP headers for the official tus features, and additional, OpenCloud specific features are announced via the capabilities endpoint (e.g. `https://localhost:9200/ocs/v1.php/cloud/capabilities?format=json`). + +The following snippet shows the relevant part of the server capabilities of OpenCloud that concerns the tus upload: +```json +{ + "ocs": { + "data": { + "capabilities": { + "files": { + "tus_support": { + "version": "1.0.0", + "resumable": "1.0.0", + "extension": "creation,creation-with-upload", + "max_chunk_size": 10000000, + "http_method_override": "" + } + } + } + } + } +} + +``` + +| Parameter | Environment Variable | Default Value | Description | +|----------------|--------------------------------|---------------|---------------------------------------------------------------------| +| max_chunk_size | FRONTEND_UPLOAD_MAX_CHUNK_SIZE | 10000000 | Announces the max chunk sizes in bytes for uploads via the clients. | + +## Upload in Chunks + +### Create an Upload URL + +The client must send a POST request against a known upload creation URL to request a new upload resource. +The filename has to be provided in base64-encoded format. + +Example: +```shell +# base64 encoded filename 'tustest.txt' is 'dHVzdGVzdC50eHQ=' +echo -n 'tustest.txt' | base64 +``` + + + +```shell +curl -ks -XPOST https://cloud.opencloud.test/remote.php/dav/spaces/8d72036d-14a5-490f-889e-414064156402$196ac304-7b88-44ce-a4db-c4becef0d2e0 \ +-H "Authorization: Bearer eyJhbGciOiJQUzI..."\ +-H "Tus-Resumable: 1.0.0" \ +-H "Upload-Length: 10" \ +-H "Upload-Metadata: filename dHVzdGVzdC50eHQ=" +``` + + + +``` +< HTTP/1.1 201 Created +< Access-Control-Allow-Headers: Tus-Resumable, Upload-Length, Upload-Metadata, If-Match +< Access-Control-Allow-Origin: * +< Access-Control-Expose-Headers: Tus-Resumable, Upload-Offset, Location +< Content-Length: 0 +< Content-Security-Policy: default-src 'none'; +< Date: Mon, 16 Oct 2023 08:49:39 GMT +< Location: https://cloud.opencloud.test/data/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZXZhIiwiZXhwIjoxNjk3NTMyNTc5LCJpYXQiOjE2OTc0NDYxNzksInRhcmdldCI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTE1OC9kYXRhL3R1cy8zYTU3ZWZlMS04MzE0LTQ4MGEtOWY5Ny04N2Q1YzBjYTJhMTgifQ.FbrlY7mdOfsbFgMrP8OtcHlCEq72a2ZVnPD2iBo9MfM +< Tus-Extension: creation,creation-with-upload,checksum,expiration +< Tus-Resumable: 1.0.0 +< Vary: Origin +< X-Content-Type-Options: nosniff +< X-Download-Options: noopen +< X-Frame-Options: SAMEORIGIN +< X-Permitted-Cross-Domain-Policies: none +< X-Request-Id: xxxxxxxxxxxxxxxxxxxxxx +< X-Robots-Tag: none +< X-Xss-Protection: 1; mode=block +< +* Connection #0 to host localhost left intact +``` + + + +The server will return a temporary upload URL in the location header of the response: +``` +< Location: https://cloud.opencloud.test/data/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZXZhIiwiZXhwIjoxNjk3NTMyNTc5LCJpYXQiOjE2OTc0NDYxNzksInRhcmdldCI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTE1OC9kYXRhL3R1cy8zYTU3ZWZlMS04MzE0LTQ4MGEtOWY5Ny04N2Q1YzBjYTJhMTgifQ.FbrlY7mdOfsbFgMrP8OtcHlCEq72a2ZVnPD2iBo9MfM +``` + +### Upload the First Chunk + +Once a temporary upload URL has been created, a client can send a PATCH request to upload a file. The file content should be sent in the body of the request: + + +```shell +curl -ks -XPATCH https://temporary-upload-url \ +-H "Authorization: Bearer eyJhbGciOiJQUzI..." \ +-H "Tus-Resumable: 1.0.0" \ +-H "Upload-Offset: 0" \ +-H "Content-Type: application/offset+octet-stream" -d "01234" +``` + + + +``` +< HTTP/1.1 204 No Content +< Date: Tue, 17 Oct 2023 04:10:52 GMT +< Oc-Fileid: 8d72036d-14a5-490f-889e-414064156402$73bb5450-816b-4cae-90aa-1f96adc95bd4!84e319e4-de1d-4dd8-bbd0-e51d933cdbcd +< Tus-Resumable: 1.0.0 +< Upload-Expires: 1697602157 +< Upload-Offset: 5 +< Vary: Origin +< X-Content-Type-Options: nosniff +< X-Request-Id: xxxxxxxxxxxxxxxxxxxxxx +< +* Connection #0 to host localhost left intact +``` + + + +### Upload Further Chunks + +After the first chunk is uploaded, the second chunk can be uploaded by pointing `Upload-Offset` to exact position that was returned in the first response. +Upload process will not be marked as complete until the total uploaded content size matches the `Upload-Length` specified during the creation of the temporary URL. + + + +```shell +curl -ks -XPATCH https://temporary-upload-url \ +-H "Authorization: Bearer eyJhbGciOiJQUzI..." \ +-H "Tus-Resumable: 1.0.0" \ +-H "Upload-Offset: 5" \ +-H "Content-Type: application/offset+octet-stream" -d "56789" +``` + + + +``` +< HTTP/1.1 204 No Content +< Date: Tue, 17 Oct 2023 04:11:00 GMT +< Oc-Fileid: 8d72036d-14a5-490f-889e-414064156402$73bb5450-816b-4cae-90aa-1f96adc95bd4!84e319e4-de1d-4dd8-bbd0-e51d933cdbcd +< Tus-Resumable: 1.0.0 +< Upload-Expires: 1697602157 +< Upload-Offset: 10 +< Vary: Origin +< X-Content-Type-Options: nosniff +< X-Request-Id: xxxxxxxxxxxxxxxxxxxxxx +< +* Connection #0 to host localhost left intact +``` + + + +:::warning Important Warning +`Upload-Offset` header indicates the byte position in the target file where the server should start writing the upload content. +It ensures data integrity and order during the upload process. +::: + +## Creation with Upload + + + +```shell +curl -ks -XPOST https://cloud.opencloud.test/remote.php/dav/spaces/\{space-id\} \ +-H "Authorization: Bearer eyJhbGciOiJQUzI..." \ +-H "Tus-Resumable: 1.0.0" \ +-H "Upload-Length: 14" \ +-H "Content-Type: application/offset+octet-stream" \ +-H "Upload-Metadata: filename dGVzdC50eHQ=" \ +-H "Tus-Extension: creation-with-upload" \ +-d "upload content" +``` + + + +```shell +< HTTP/1.1 201 Created +< Access-Control-Allow-Headers: Tus-Resumable, Upload-Length, Upload-Metadata, If-Match +< Access-Control-Allow-Origin: * +< Access-Control-Expose-Headers: Tus-Resumable, Upload-Offset, Location +< Content-Length: 0 +< Content-Security-Policy: default-src 'none'; +< Content-Type: text/plain +< Date: Mon, 16 Oct 2023 04:18:25 GMT +< Etag: "372c96743f68bc40e789124d30567371" +< Last-Modified: Mon, 16 Oct 2023 04:18:25 +0000 +< Location: https://cloud.opencloud.test/data/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZXZhIiwiZXhwIjoxNjk3NTE2MzA1LCJpYXQiOjE2OTc0Mjk5MDUsInRhcmdldCI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTE1OC9kYXRhL3R1cy82NjlhODBlZi1hN2VjLTQwYTAtOGNmOS05MTgwNTVhYzlkZjAifQ.yq-ofJYnJ9FLML7Z_jki1FJQ7Ulbt9O_cmLe6V411A4 +< Oc-Etag: "372c96743f68bc40e789124d30567371" +< Oc-Fileid: 44d3e1e0-6c01-4b94-9145-9d0068239fcd$446bdad4-4b27-41f1-afce-0881f202a214!d7c292a6-c395-4e92-bf07-2c1663aec8dd +< Oc-Perm: RDNVWZP +< Tus-Extension: creation,creation-with-upload,checksum,expiration +< Tus-Resumable: 1.0.0 +< Upload-Expires: 1697516305 +< Upload-Offset: 14 +< Vary: Origin +< X-Content-Type-Options: nosniff +< X-Download-Options: noopen +< X-Frame-Options: SAMEORIGIN +* TLSv1.2 (IN), TLS header, Supplemental data (23): +{ [5 bytes data] +< X-Permitted-Cross-Domain-Policies: none +< X-Request-Id: xxxxxxxxxxxxxxxxxxxxxx +< X-Robots-Tag: none +< X-Xss-Protection: 1; mode=block +< +* Connection #0 to host localhost left intact +``` + + + +:::warning Important Warning +The `Upload-Length` header of the request has to contain the exact size of the upload content in byte. +::: + +## Supported Upload-Metadata + +Upload-metadata key-value pairs aren't specified in the general tus docs. The following ones are supported in the OpenCloud ecosystem: + +| Parameter (key) | Example (value, MUST be Base64 encoded) | Description | +|----------------------------------|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| `name` OR `filename` (mandatory) | example.pdf | Filename | +| `mtime` (recommended) | 1701708712 | Modification time (Unix time format) | +| `checksum` (recommended) | SHA1 a330de5886e5a92d78fb3f8d59fe469857759e72 | Checksum, computed from the client | +| `type` OR `filetype` | application/pdf | MIME Type, sent by the web UI | +| `relativePath` | undefined | File path relative to the folder that is being uploaded, including the filename. Sent by the web UI | +| `spaceId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | +| `spaceName` | Personal | Sent by the web UI | +| `driveAlias` | personal/admin | Sent by the web UI | +| `driveType` | personal | Sent by the web UI | +| `currentFolder` | / | Sent by the web UI | +| `currentFolderId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993!a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | +| `uppyId` | uppy-example/pdf-1e-application/pdf-238300 | Sent by the web UI | +| `relativeFolder` | | File path relative to the folder that is being uploaded, without filename. Sent by the web UI. | +| `tusEndpoint` | https://cloud.opencloud.test/remote.php/dav/spaces/8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | +| `uploadId` | 71d5f878-a96c-4d7b-9627-658d782c93d7 | Sent by the web UI | +| `topLevelFolderId` | undefined | Sent by the web UI | +| `routeName` | files-spaces-generic | Sent by the web UI | +| `routeDriveAliasAndItem` | cGVyc29uYWwvYWRtaW4= | Sent by the web UI | +| `routeShareId` | | Share ID when uploading into a received folder share. Sent by the web UI | diff --git a/docs/dev/server/Apis/http/webdav/index.md b/docs/dev/server/Apis/http/webdav/index.md new file mode 100644 index 00000000..1fbe7b60 --- /dev/null +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -0,0 +1,547 @@ +--- +title: WebDAV +sidebar_position: 2 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + + +**Web** **D**istributed **A**uthoring and **V**ersioning (WebDAV) consists of a set of methods, headers, and content-types extending HTTP/1.1 for the management of resources and -properties, creation and management of resource collections, URL namespace manipulation, and resource locking (collision avoidance). WebDAV is one of the central APIs that OpenCloud uses for handling file resources, metadata and locks. + + +:::info RFC +**WebDAV RFCs** + +RFC 2518 was published in February 1999. [RFC 4918](https://datatracker.ietf.org/doc/html/rfc4918), published in June 2008 obsoletes RFC 2518 with minor revisions mostly due to interoperability experience. + +::: +## Calling the WebDAV API + +### Request URI + +```sh +{HTTP method} https://cloud.opencloud.test/{webdav-base}/{resourceID}/{path} +``` + +The request URI consists of: + +| Component | Description | +|---------------|--------------------------------------------------------------------------------------------------------| +| `{HTTP method}` | The HTTP method which is used in the request. | +| `{webdav-base}` | The WebDAV base path component. Possible options are | +| | `dav/spaces/` This is the default and optimized endpoint for all WebDAV requests. | +| | `remote.php/dav/spaces/`* | +| | `remote.php/webdav/`* | +| | `webdav/`* | +| | `dav/`* | +| `{resourceID}` | This resourceID is used as the WebDAV root element. All children are accessed by their relative paths. | +| `{path}` | The relative path to the WebDAV root. In most of the casese, this is the space root. | + +\* these dav endpoints are implemented for legacy reasons and should not be used. Note: The legacy endpoints **do not take the resourceID as an argument.** + +### HTTP methods + +| Method | Description | +|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| PROPFIND | Retrieve properties as XML from a web resource. It is also overloaded to retrieve the collection structure (a.k.a. directory hierarchy) of a remote system. | +| PROPPATCH | Process instructions specified in the request body to set and/or remove properties defined on the resource identified by the request uri. | +| MKCOL | Create a WebDAV collection (folder) at the location specified by the request uri. | +| GET | Retrieve a WebDAV resource. | +| HEAD | Retrieve a WebDAV resource without reading the body. | +| PUT | A PUT performed on an existing resource replaces the GET response entity of the resource. | +| POST | Not part of the WebDAV rfc and has no effect on a WebDAV resource. However, this method is used in the TUS protocol for uploading resources. | +| PATCH | Not part of the WebDAV rfc and has no effect on a WebDAV resource. However, this method is used in the TUS protocol for uploading resources. | +| COPY | Creates a duplicate of the source resource identified by the Request-URI, in the destination resource identified by the URI in the Destination header. | +| MOVE | The MOVE operation on a non-collection resource is the logical equivalent of a copy (COPY), followed by consistency maintenance processing, followed by a delete of the source, where all three actions are performed in a single operation. | | +| DELETE | Delete the resource identified by the Request-URI. | +| LOCK | A LOCK request to an existing resource will create a lock on the resource identified by the Request-URI, provided the resource is not already locked with a conflicting lock. | +| UNLOCK | The UNLOCK method removes the lock identified by the lock token in the Lock-Token request header. The Request-URI must identify a resource within the scope of the lock. | + +The methods `MKCOL`, `GET`, `HEAD`, `LOCK`, `COPY`, `MOVE`, `UNLOCK` and `DELETE` need no request body. + +The methods `PROPFIND`, `PROPPATCH`, `PUT` require a request body, normally in XML format to provide the needed values. + +:::tip Tooling +**WebDAV is not REST** + +The WebDAV protocol was created before the REST paradigm has become the de-facto standard for API design. WebDAV uses http methods which are not part of REST. Therefore all the tooling around API design and documentation is not usable (like OpenApi 3.0 / Swagger or others). +::: + +### Authentication + +For development purposes the examples in the developer documentation use Basic Auth. It is disabled by default and should only be enabled by setting `PROXY_ENABLE_BASIC_AUTH` in [the proxy](../../../services/proxy/configuration/#environment-variables) for development or test instances. + +To authenticate with a Bearer token or OpenID Connect access token replace the `-u user:password` Basic Auth option of curl with a `-H 'Authorization: Bearer '` header. A `` can be obtained by copying it from a request in the browser, although it will time out within minutes. To automatically refresh the OpenID Connect access token an ssh-agent like solution like [oidc-agent](https://github.com/indigo-dc/oidc-agent) should be used. + +## Listing Properties + +This method is used to list the properties of a resource in xml. This method can also be used to retrieve the listing of a WebDAV collection which means the content of a remote directory. + + + + +```shell +curl -L -X PROPFIND 'https://localhost:9200/dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/' \ +-H 'Depth: 1' \ +-d ' + + + + + + + + + + + + + + + + + +' +``` + + + + +```shell +PROPFIND /dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/ HTTP/1.1 +Host: localhost:9200 +Origin: https://localhost +Access-Control-Request-Method: PROPFIND +Depth: 1 +Content-Type: application/xml +Authorization: Basic YWRtaW46YWRtaW4= +Content-Length: 436 + + + + + + + + + + + + + + + + + + + + +``` + + + + +The request consists of a request body and an optional `Depth` Header. + +:::tip PROPFIND usage +**Metadata and Directory listings** + +Clients can use the `PROPFIND` method to retrieve properties of resources (metadata) and to list the content of a directories. +::: +### Response + + + + +#### Multi Status Response + +A Multi-Status response conveys information about multiple resources +in situations where multiple status codes might be appropriate. The +default Multi-Status response body is an application/xml +HTTP entity with a `multistatus` root element. Further elements +contain `200`, `300`, `400`, and `500` series status codes generated during +the method invocation. + +Although `207` is used as the overall response status code, the +recipient needs to consult the contents of the multistatus response +body for further information about the success or failure of the +method execution. The response MAY be used in success, partial +success and also in failure situations. + +The `multistatus` root element holds zero or more `response` elements +in any order, each with information about an individual resource. + +#### Body + +```xml + + + /dav/spaces/storage-users-1$some-admin-user-id-0000-000000000000/ + + + RDNVCKZP + 0 + storage-users-1$some-admin-user-id-0000-000000000000!some-admin-user-id-0000-000000000000 + storage-users-1$some-admin-user-id-0000-000000000000!some-admin-user-id-0000-000000000000 + admin + Admin + https://localhost:9200/f/storage-users-1$some-admin-user-id-0000-000000000000%21some-admin-user-id-0000-000000000000 + 10364682 + Mon, 04 Sep 2023 20:10:09 GMT + "c4d3610dfe4fac9b44e1175cfc44b12b" + + + + + HTTP/1.1 200 OK + + + + + + + + + HTTP/1.1 404 Not Found + + + + /dav/spaces/storage-users-1$some-admin-user-id-0000-000000000000/New%20file.txt + + + RDNVWZP + + SHA1:1c68ea370b40c06fcaf7f26c8b1dba9d9caf5dea MD5:2205e48de5f93c784733ffcca841d2b5 ADLER32:058801ab + + 0 + storage-users-1$some-admin-user-id-0000-000000000000!90cc3e73-0c6c-4346-9c4d-f529976d4990 + storage-users-1$some-admin-user-id-0000-000000000000!90cc3e73-0c6c-4346-9c4d-f529976d4990 + admin + Admin + + 0 + 1 + 3 + + https://localhost:9200/f/storage-users-1$some-admin-user-id-0000-000000000000%2190cc3e73-0c6c-4346-9c4d-f529976d4990 + 5 + 5 + Mon, 28 Aug 2023 20:45:03 GMT + "75115347c74701a3be9c635ddebbf5c4" + text/plain + + + HTTP/1.1 200 OK + + + + /dav/spaces/storage-users-1$some-admin-user-id-0000-000000000000/NewFolder/ + + + RDNVCKZP + 0 + storage-users-1$some-admin-user-id-0000-000000000000!5c73ecd9-d9f4-44f4-b685-ca4cb40aa6b7 + storage-users-1$some-admin-user-id-0000-000000000000!5c73ecd9-d9f4-44f4-b685-ca4cb40aa6b7 + admin + Admin + https://localhost:9200/f/storage-users-1$some-admin-user-id-0000-000000000000%215c73ecd9-d9f4-44f4-b685-ca4cb40aa6b7 + 0 + Mon, 28 Aug 2023 20:45:10 GMT + "e83367534cc595a45d706857fa5f03d8" + + + + + HTTP/1.1 200 OK + + + + + + + + + HTTP/1.1 404 Not Found + + + +``` + + + +#### Body + +```xml + + + Sabre\DAV\Exception\BadRequest + Invalid Depth header value: 3 + +``` + +This can occur if the request is malformed e.g. due to an invalid xml request body or an invalid depth header value. + + + +#### Body + +```xml + + + Sabre\DAV\Exception\NotFound + Resource not found + +``` + + + +### Request Body + +The `PROPFIND` Request can include an XML request body containing a list of namespaced property names. + +### Namespaces + +When building the body of your DAV request, you will request properties that are available under a specific namespace URI. It is usual to declare prefixes for those namespace in the `d:propfind` element of the body. + +Available namespaces: + +| URI | Prefix | +|-------------------------------------------|--------| +| DAV: | d | +| http://sabredav.org/ns | s | +| http://owncloud.org/ns | oc | +| http://open-collaboration-services.org/ns | ocs | +| http://open-cloud-mesh.org/ns | ocm | + +### Request Example with declared namespaces + +```xml + + + +``` + +### Supported WebDAV Properties + +| Property | Desription | Example | +|---------------------------------------| -------------------------------------------------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------| +| `` | The latest modification time. | `Fri, 30 Dec 2022 14:22:43 GMT` | +| `` | The file's etag. | `"c3a1ee4a0c28edc15b9635c3bf798013"` | +| `` | The mime type of the file. | `image/jpeg` | +| `` | Specifies the nature of the resource. | `` for a folder | +| `` | The size if it is a file in bytes. | `5` bytes | +| `` | Describes the active locks on a resource. | | +| `` | The globally unique ID of the resource. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | The globally unique ID of the resource. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | Direct URL to download a file from. | Not implemented. | +| `` | Determines the actions a user can take on the resource. | The value is a string containing letters that clients can use to determine available actions. | +| | | `S`: Shared | +| | | `M`: Mounted | +| | | `D`: Deletable | +| | | `NV`: Updateable, Renameable, Moveable | +| | | `W`: Updateable (file) | +| | | `CK`: Creatable (folders only) | +| | | `Z`: Deniable | +| | | `P`: Trashbin Purgable | +| | | `X`: Securely Viewable | +| | | In the early stages this was indeed a list of permissions. Over time, more flags were added and the term permissions no longer really fits well. | +| `` | List of user specified tags. | `test` | +| ` ` | The favorite state. | `0` for not favourited, `1` for favourited | +| `` | The user id of the owner of a resource. Project spaces have no owner. | `einstein` | +| `` | The display name of the owner of a resource. Project spaces have no owner. | `Albert Einstein` | +| `` | List of share types. | `0` = User Share | +| | | `1` = Group Share | +| | | `2` = Public Link | +| `` | | ``
`SHA1:1c68ea370b40c06fcaf7f26c8b1dba9d9caf5dea MD5:2205e48de5f93c784733ffcca841d2b5 ADLER32:058801ab`
`
` | +| | | Due to a bug in the very early development of OpenCloud, this value is not an array, but a string separated by whitespaces. | +| `` | Similar to `getcontentlength` but it also works for folders. | `10` bytes | +| `` | The ID of the share if the resource is part of such. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | The root path of the shared resource if the resource is part of such. | `/shared-folder` | +| `` | The ID of the shared resource if the resource is part of such. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | The type of the resource if it's a public link. | `folder` | +| `` | The share permissions of the resource if it's a public link. | `1` | +| `` | The expiration date of the public link. | `Tue, 14 May 2024 12:44:29 GMT` | +| `` | The date the public link was created. | `Tue, 14 May 2024 12:44:29 GMT` | +| `` | The username of the user who created the public link. | `admin` | +| `` | The original name of the resource before it was deleted. | `some-file.txt` | +| `` | The original location of the resource before it was deleted. | `some-file.txt` | +| `` | The date the resource was deleted. | `Tue, 14 May 2024 12:44:29 GMT` | +| `` | Audio meta data if the resource contains such. | `MetallicaMetallicaEnter Sandman` | +| `` | Location meta data if the resource contains such. | `51.504106-0.074575` | + +### Request Headers + +A client executing a `PROPFIND` request MUST submit a Depth Header value. In practice, support for infinite-depth requests MAY be disabled, due to the performance and security concerns associated with this behavior. Servers SHOULD treat a +request without a Depth header as if a `Depth: infinity` header was included. Infinite depth requests are disabled by default in opencloud. + +| Name | Value | +|-------------------------------------------|---------------------------------------------------------------------------------------| +| Depth | `0` = Only return the desired resource. | +| | `1` = Return the desired resource and all resources one level below in the hierarchy. | +| | `infinity` = Return all resources below the root. | + +:::warning Use the Depth header with caution +**Depth: infinity** + +Using the `Depth: infinity` header value can cause heavy load on the server, depending on the size of the file tree. + +The request can run into a timeout and the server performance could be affected for other users. +::: + +## Create a Directory + +Clients create directories (WebDAV collections) by executing a `MKCOL` request at the location specified by the request url. + + + +```shell +curl -L -X MKCOL 'https://localhost:9200/dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/NewFolder/' \ +-H 'Authorization: Basic YWRtaW46YWRtaW4=' +``` + + +```shell +MKCOL /dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/NewFolder/ HTTP/1.1 +Host: localhost:9200 +Authorization: Basic YWRtaW46YWRtaW4= +``` + + +### Response + + + +This indicates that the Resource has been created successfully. + +#### Body + +The response has no body. + + + +#### Body + +```xml + + + Sabre\DAV\Exception\Forbidden + + +``` + + + +#### Body + +```xml + + + Sabre\DAV\Exception\MethodNotAllowed + The resource you tried to create already exists + +``` + + + +## Upload File + +To upload files to the remote server, clients can use the `PUT` method to create or fully replace the content of the remote file. + +### Request Headers + +| Name | Usage | +|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `X-OC-Mtime` | Send the last modified
time of the file to the server in unixtime format. The server applies this mtime to the resource rather than the actual time. | +| `OC-Checksum` | Provide the checksum of the
file content to the server.
This is used to prevent corrupted data transfers. | +| `If-Match` | The If-Match request-header field is used with a method to make it
conditional. A client that has one or more entities previously
obtained from the resource can verify that one of those entities is
current by including a list of their associated entity tags in the
If-Match header field. | + + + +```shell +curl -L -X PUT 'https://localhost:9200/dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/test.txt' \ +-H 'X-OC-Mtime: 1692369418' \ +-H 'OC-Checksum: SHA1:40bd001563085fc35165329ea1ff5c5ecbdbbeef' \ +-H 'If-Match: "4436aef907f41f1ac7dfd1ac3d0d455f"' \ +-H 'Content-Type: text/plain' \ +-H 'Authorization: Basic YWRtaW46YWRtaW4=' \ +-d '123' +``` + + +```shell +PUT /dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/test.txt HTTP/1.1 +Host: localhost:9200 +X-OC-Mtime: 1692369418 +OC-Checksum: SHA1:40bd001563085fc35165329ea1ff5c5ecbdbbeef +If-Match: "4436aef907f41f1ac7dfd1ac3d0d455f" +Content-Type: text/plain +Authorization: Basic YWRtaW46YWRtaW4= +Content-Length: 3 + +123 +``` + + + +### Response + + + +This indicates that the Resource has been created successfully. + +#### Body + +The response has no body. + +#### Headers + +```yaml +Oc-Etag: "4436aef907f41f1ac7dfd1ac3d0d455f" +Oc-Fileid: storage-users-1$some-admin-user-id-0000-000000000000!07452b22-0ba9-4539-96e1-3511aff7fd2f +Last-Modified: Fri, 18 Aug 2023 14:36:58 +0000 +X-Oc-Mtime: accepted +``` + + +This indicates that the Resource has been updated successfully. + +#### Body + +The response has no body. + +#### Headers + +```yaml +Oc-Etag: "4436aef907f41f1ac7dfd1ac3d0d455f" +Oc-Fileid: storage-users-1$some-admin-user-id-0000-000000000000!07452b22-0ba9-4539-96e1-3511aff7fd2f +Last-Modified: Fri, 18 Aug 2023 14:36:58 +0000 +X-Oc-Mtime: accepted +``` + + +This indicates that the checksum, which was sent by the client, does not match the computed one after all bytes have been received by the server. + +#### Body + +```xml + + + Sabre\DAV\Exception\BadRequest + The computed checksum does not match the one received from the client. + +``` + + + +The user cannot create files in that remote location. + + + +The remote target space cannot be found. + + + +This error can occur when the request cannot be executed due to a missing precondition. One example is a PUT into a non-existing remote folder. It can also happen when the client sends the wrong etag in the `If-Match` header. + + diff --git a/docs/dev/server/Apis/index.md b/docs/dev/server/Apis/index.md new file mode 100644 index 00000000..3662cfda --- /dev/null +++ b/docs/dev/server/Apis/index.md @@ -0,0 +1,44 @@ +--- +title: APIs +sidebar_position: 1 +--- + + +OpenCloud provides a large set of different **application programming interfaces (APIs)**. OpenCloud is built by microservices. That means many calls to "functions" in the code are remote calls. + +Basically we have two different API "universes": [HTTP](http) and [gRPC](grpc_apis). + +| HTTP | gRPC | +|----------------------------------|----------------------------------| +| ![HTTP Logo](/img/http-logo.png) | ![gRPC Logo](/img/grpc-logo.png) | + + +For inter-service-communication we are using mostly gRPC calls because it has some advantages. In the future, clients may decide to use gRPC directly to make use of these advantages. + +![OpenCloud APIs Architecture](/img/oc-apis.drawio.svg) + +## [HTTP](http) + +HTTP APIs are mostly used for client < > server communication. Modern applications are embracing a [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) software architecture style. REST APIs are using the HTTP protocol to transfer data between clients and servers. All our clients talk to the Server using HTTP APIs. This has legacy reasons and is well-supported across many platforms and technologies. OpenCloud uses an [HTTP API gateway](../services/proxy) to route client requests to the correct service. + +### OpenAPI + +It is best practise to define APIs and their behavior by a spec. We are using the OpenAPI standard for all new APIs. The [OpenAPI Specification](https://swagger.io/specification/), previously known as the Swagger Specification, is a specification for a machine-readable interface definition language for describing, producing, consuming and visualizing RESTful web services. Previously part of the Swagger framework, it became a separate project in 2016, overseen by the OpenAPI Initiative, an open-source collaboration project of the Linux Foundation. Swagger and some other tools can generate code, documentation and test cases from interface files. + +### RFC + +Some APIs have become a de facto standard and are additionally covered by an [RFC](https://en.wikipedia.org/wiki/Request_for_Comments). + +## [gRPC](grpc_apis) + +In gRPC, a client application can directly call methods on a server application on a different machine as if it was a local object. This makes it easier to create distributed applications based on microservices. In gRPC we can define a service and specify the methods that can be called remotely. A gRPC client has a stub that provides the same methods and types as the server. +OpenCloud uses a [gRPC API Gateway](../services/gateway) to route the requests to the correct service. + +### Protobuf + +gRPC APIs are typically defined by [Protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). The different client and server stubs are created from ``*.proto`` files by code generation tools. + +## Versioning + +There are different standards for API versioning: Through URL, through request parameter, through custom header and through content negotiation. OpenCloud uses the versioning by URL concept although this creates a big code footprint. The versioning follows [SemVer](https://semver.org). We update the major version number when breaking changes are needed. Clients can decide which major version they use through the request URL. The specific implementation is documented on each API. + diff --git a/static/img/cs3org.png b/static/img/cs3org.png new file mode 100644 index 0000000000000000000000000000000000000000..6f06e21695926c46def206f0029cc35904ecd8c4 GIT binary patch literal 10081 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%0A)ljwdPTbXP49k@nwnp+9jJ$vJC0$czdn66 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 z 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 LJeL + + + + + + +
+
+
+ Client 1 +
+
+
+
+ + Client 1 + +
+
+ + + + +
+
+
+ HTTP Gateway +
+ (proxy service) +
+
+
+
+ + HTTP Gateway... + +
+
+ + + + +
+
+
+ Client 2 +
+
+
+
+ + Client 2 + +
+
+ + + + +
+
+
+ Client 3 +
+
+
+
+ + Client 3 + +
+
+ + + + + + +
+
+
+ HTTP +
+
+
+
+ + HTTP + +
+
+ + + + + + +
+
+
+ HTTP +
+
+
+
+ + HTTP + +
+
+ + + + + + +
+
+
+ HTTP +
+
+
+
+ + HTTP + +
+
+ + + + +
+
+
+ opencloud service +
+
+
+
+ + opencloud service + +
+
+ + + + +
+
+
+ opencloud service +
+
+
+
+ + opencloud service + +
+
+ + + + +
+
+
+ opencloud service +
+
+
+
+ + opencloud service + +
+
+ + + + + + +
+
+
+ HTTP +
+
+
+
+ + HTTP + +
+
+ + + + +
+
+
+ GRPC Gateway +
+ (gateway service) +
+
+
+
+ + GRPC Gateway... + +
+
+ + + + + + +
+
+
+ gRPC +
+
+
+
+ + gRPC + +
+
+ + + + + + +
+
+
+ gRPC +
+
+
+
+ + gRPC + +
+
+ + + + + + +
+
+
+ HTTP +
+
+
+
+ + HTTP + +
+
+
+ + + + + Viewer does not support full SVG 1.1 + + + + \ No newline at end of file From 0e1993fd2235fe9c52d899c2cf9ea10eb7e9d6b5 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Wed, 10 Sep 2025 14:33:26 +0200 Subject: [PATCH 2/6] style: make linter happy --- .../keycloak.md | 2 +- docs/dev/server/Apis/grpc_apis/index.md | 3 +- docs/dev/server/Apis/http/authorization.md | 21 ++- docs/dev/server/Apis/http/graph/groups.md | 45 +++--- docs/dev/server/Apis/http/graph/index.md | 2 +- .../dev/server/Apis/http/graph/permissions.md | 15 +- docs/dev/server/Apis/http/graph/role.md | 10 +- docs/dev/server/Apis/http/graph/spaces.md | 21 +-- docs/dev/server/Apis/http/graph/users.md | 49 +++--- docs/dev/server/Apis/http/tus_upload.md | 20 ++- docs/dev/server/Apis/http/webdav/index.md | 149 ++++++++++-------- docs/dev/server/Apis/index.md | 2 - 12 files changed, 182 insertions(+), 157 deletions(-) diff --git a/docs/admin/configuration/authentication-and-user-management/keycloak.md b/docs/admin/configuration/authentication-and-user-management/keycloak.md index c77bd168..dbf3d020 100644 --- a/docs/admin/configuration/authentication-and-user-management/keycloak.md +++ b/docs/admin/configuration/authentication-and-user-management/keycloak.md @@ -307,7 +307,7 @@ IDP_DOMAIN=your-idp-domain.example.com IDP_ACCOUNT_URL=https://your-idp-domain.example.com/realms/openCloud/account ``` -The Docker Compose file `idm/external-idp.yml` contains the complete configuration for each opencloud component. The file `10_opencloud_ldap_schema.ldif` contains the OpenCloud LDAP schema and is loaded during the startup of the OpenLdap container. In this mode, your IdP setup is not part of the OpenCloud Deployment. +The Docker Compose file `idm/external-idp.yml` contains the complete configuration for each OpenCloud component. The file `10_opencloud_ldap_schema.ldif` contains the OpenCloud LDAP schema and is loaded during the startup of the OpenLdap container. In this mode, your IdP setup is not part of the OpenCloud Deployment. :::warning diff --git a/docs/dev/server/Apis/grpc_apis/index.md b/docs/dev/server/Apis/grpc_apis/index.md index 8de33a56..007fedcc 100644 --- a/docs/dev/server/Apis/grpc_apis/index.md +++ b/docs/dev/server/Apis/grpc_apis/index.md @@ -17,6 +17,7 @@ gRPC uses http/2 by default and is faster than REST. When using protocol buffers ### 🛡️ Robustness gRPC empowers better relationships between clients and servers. The rules of communication are strictly enforced. That is not the case in REST calls, where the client and the server can send and receive anything they like and hopefully the other end understands what to do with it. In gRPC, to make changes to the communication, both client and server need to change accordingly. This prevents mistakes specially in microservice architectures. + ### 🔍 Debuggability gRPC requests are re-using the same context and can be tracked or traced across multiple service boundaries. @@ -46,7 +47,7 @@ standards combined with freedom of choice between different programming language The [CS3 APIs](https://github.com/cs3org/cs3apis) connect storages and application providers. The CS3 APIs follow Google and Uber API design guidelines, specially on error handling and naming convention. You can read more about these -guidelines at https://cloud.google.com/apis/design/ and https://github.com/uber/prototool/blob/dev/style/README.md. +guidelines at and . The CS3 APIs use [Protocol Buffers version 3 (proto3)](https://github.com/protocolbuffers/protobuf) as their Interface Definition Language (IDL) to define the API interface and the structure of the payload messages. diff --git a/docs/dev/server/Apis/http/authorization.md b/docs/dev/server/Apis/http/authorization.md index 223cd474..fc8ab138 100644 --- a/docs/dev/server/Apis/http/authorization.md +++ b/docs/dev/server/Apis/http/authorization.md @@ -4,6 +4,7 @@ sidebar_position: 40 --- In its default configuration, OpenCloud supports three authentication methods as outlined on the [OIDC official site](https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3): + 1. Authorization Code Flow 2. Implicit Flow 3. Hybrid Flow @@ -12,11 +13,11 @@ For detailed information on OpenCloud's support for OpenID Connect (OIDC), pleas While selecting an OpenCloud client for authentication, take note of specific limitations such as the `Redirect URI`: -| Source | Redirect URI | -|---------|------------------------------------------| -| Android | oc://android.opencloud.eu | -| iOS | oc://ios.opencloud.eu | -| Desktop | http://127.0.0.1
http://localhost | +| Source | Redirect URI | +|---------|----------------------------------------------| +| Android | oc://android.opencloud.eu | +| iOS | oc://ios.opencloud.eu | +| Desktop |
| In this example, the desktop app's `client_id` are being used. @@ -60,6 +61,7 @@ client_id=OpenCloudDesktop ``` Response looks like this: + ```json { "access_token": "eyJhbGciOid...", @@ -73,6 +75,7 @@ client_id=OpenCloudDesktop 3. Refreshing an access token If the access token has expired, you can get a new one using the refresh token. + ```bash curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ -d "grant_type=refresh_token" \ @@ -82,6 +85,7 @@ client_id=OpenCloudDesktop ``` Response looks like this: + ```json { "access_token": "eyJhbGciOi...", @@ -103,29 +107,36 @@ If you are using the implicit flow, `nonce` parameter is required in the initial `nonce=pL3UkpAQPZ8bTMGYOmxHY/dQABin8yrqipZ7iN0PY18=` bash command to generate cryptographically random value + ```bash openssl rand -base64 32 ``` + ::: The user should be directed to a URL to authenticate and give their consent (bypassing consent is against the standard): + ```bash https://cloud.opencloud.test/signin/v1/identifier/_/authorize?client_id=client_id&scope=openid+profile+email+offline_access&response_type=id_token+token&redirect_uri=http://path-to-redirect-uri&nonce=pL3UkpAQPZ8bTMGYOmxHY/dQABin8yrqipZ7iN0PY18= ``` After a successful authentication, the browser will redirect to a URL that looks like this: + ```bash http://path-to-redirect-uri#access_token=eyJhbGciOiJQUzI...&expires_in=300&id_token=eyJhbGciOiJ...&scope=email%20openid%20profile&session_state=c8a1019f5e054d...&state=&token_type=Bearer ``` For the next step, extract the access_token from the URL. + ```bash access_token = 'eyJhbGciOiJQ...' ``` ## Hybrid Flow + The Hybrid Flow in OpenID Connect melds features from both the Implicit and Authorization Code flows. It allows clients to directly retrieve certain tokens from the Authorization Endpoint, yet also offers the option to acquire additional tokens from the Token Endpoint. The Authorization Server redirects back to the client with appropriate parameters in the response, based on the value of the response_type request parameter: + - code token - code id_token - code id_token token diff --git a/docs/dev/server/Apis/http/graph/groups.md b/docs/dev/server/Apis/http/graph/groups.md index 0fef118e..22f96862 100644 --- a/docs/dev/server/Apis/http/graph/groups.md +++ b/docs/dev/server/Apis/http/graph/groups.md @@ -9,7 +9,7 @@ The Groups API is implementing a subset of the functionality of the [MS Graph Group resource](https://docs.microsoft.com/en-us/graph/api/resources/group?view=graph-rest-1.0) The JSON representation of a Group as handled by the Groups API looks like this: -``` +```json { "displayName": "group", "id": "f0d97060-da16-4b0d-9fa4-d1ec43afc5f1" @@ -23,7 +23,6 @@ Our implementation currently supports two Attributes for a Group: | displayName | The groups name | | id | An unique, stable readonly identifier for the group that stays the same for the whole lifetime of the Group, usually a UUID | - ### Reading groups #### `GET /groups` @@ -32,14 +31,14 @@ Returns a list of all groups Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/groups' -u user:password ``` Response: -``` +```json { "value": [ { @@ -54,21 +53,20 @@ Response: } ``` - #### `GET /groups?$expand=members` Returns a list of all groups including its members Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/groups?$expand=members' -u user:password ``` Response: -``` +```json { "value": [ { @@ -109,13 +107,13 @@ Response: Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f' -u user:password ``` Response: -``` +```json { "displayName": "Example Users", "id": "7a20f238-8a22-4458-902d-47674c317e5f" @@ -126,13 +124,13 @@ Response: Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f?$expand=members' -u user:password ``` Response: -``` +```json { "displayName": "Example Users", "id": "7a20f238-8a22-4458-902d-47674c317e5f", @@ -146,6 +144,7 @@ Response: ] } ``` + ### Getting Group Members #### `GET /groups/{groupid}/members` @@ -154,14 +153,14 @@ Returns a list of User objects that are members of a group. Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members' -u user:password ``` Response: -``` +```json [ { "displayName": "Test User", @@ -183,12 +182,12 @@ Response: #### `POST /groups` Use this to create a new group. -h + ##### Request Body Note the missing `"id"` Attribute. It will be generated by the server: -``` +```json { "displayName": "Example Users" } @@ -198,7 +197,7 @@ Note the missing `"id"` Attribute. It will be generated by the server: When successful, the response will return the new group including the newly allocated `"id"`: -``` +```json { "displayName": "Example Users", "id": "7a20f238-8a22-4458-902d-47674c317e5f" @@ -209,7 +208,7 @@ When successful, the response will return the new group including the newly allo Example: -``` +```bash curl -k --request DELETE 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f' -u user:password ``` @@ -227,11 +226,11 @@ See below. The request body contains a single attribute "`@odata.id`" referencing the new member of the group by URI. Example: -``` +```bash curl -k --header "Content-Type: application/json" \ --request POST --data \ - '{ "@odata.id": "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51" }' \ - 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members/$ref' -u user:password + '{ "@odata.id": "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51" }' \ + 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members/$ref' -u user:password ``` @@ -244,11 +243,11 @@ When successful the API returns no response body and the HTTP status code 204 (N The request body contains the attribute `members@odata.bind` holding a list of URI references for the new members. Example: -``` +```json { "members@odata.bind": [ "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51", - "https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71" + "https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71" ] } ``` @@ -261,7 +260,7 @@ When successful the API returns no response body and the HTTP status code 204 (N Example -``` +```bash curl -k --request DELETE \ 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members/4c510ada-c86b-4815-8820-42cdf82c3d51/$ref' \ -u user:password diff --git a/docs/dev/server/Apis/http/graph/index.md b/docs/dev/server/Apis/http/graph/index.md index 944e5094..2aa7ed2d 100644 --- a/docs/dev/server/Apis/http/graph/index.md +++ b/docs/dev/server/Apis/http/graph/index.md @@ -54,6 +54,7 @@ For example, adding the following filter parameter restricts the drives returned ```shell GET https://cloud.opencloud.test/graph/v1.0/drives?$filter=driveType eq 'project' ``` + For more information about OData query options please check the [API specification](https://github.com/opencloud-eu/libre-graph-api) and the provided examples. ### Authorization @@ -63,4 +64,3 @@ For development purposes the examples in the developer documentation use Basic A To authenticate with a Bearer token or OpenID Connect access token replace the `-u user:password` Basic Auth option of curl with a `-H 'Authorization: Bearer '` header. A `` can be obtained by copying it from a request in the browser, although it will time out within minutes. To automatically refresh the OpenID Connect access token an ssh-agent like solution like [oidc-agent](https://github.com/indigo-dc/oidc-agent) should be used. The graph endpoints that support a preconfigured token can be found in the [API specification](https://github.com/opencloud-eu/libre-graph-api) ## Resources - diff --git a/docs/dev/server/Apis/http/graph/permissions.md b/docs/dev/server/Apis/http/graph/permissions.md index f7763165..898778e8 100644 --- a/docs/dev/server/Apis/http/graph/permissions.md +++ b/docs/dev/server/Apis/http/graph/permissions.md @@ -11,6 +11,7 @@ The Permissions API is implementing a subset of the functionality of the ### Example Permissions The JSON representation of a Drive, as handled by the Spaces API, looks like this: + ````json { "@libre.graph.permissions.roles.allowedValues": [ @@ -148,32 +149,32 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi ### Create a link share `POST /drives/{drive-id}/items/{item-id}/createLink` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/CreateLink + ### Create a user/group share `POST /drives/{drive-id}/items/{item-id}/invite` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/Invite + ## Reading Permissions ### List the effective sharing permissions on a driveitem `GET /drives/{drive-id}/items/{item-id}/permissions` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/ListPermissions + ### List Get sharing permission for a file or folder `GET /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/GetPermission + ## Updating Permissions ### Updating sharing permission `POST /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/UpdatePermission + ### Set password of permission `POST /drives/{drive-id}/items/{item-id}/permissions/{perm-id}/setPassword` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/SetPermissionPassword + ### Deleting permission `DELETE /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/DeletePermission \ No newline at end of file + diff --git a/docs/dev/server/Apis/http/graph/role.md b/docs/dev/server/Apis/http/graph/role.md index 64c4b28f..04aee5a2 100644 --- a/docs/dev/server/Apis/http/graph/role.md +++ b/docs/dev/server/Apis/http/graph/role.md @@ -12,22 +12,22 @@ The Roles API is implementing a subset of the functionality of the ### List roleDefinitions `GET /v1beta1/roleManagement/permissions/roleDefinitions` -https://docs.opencloud.eu/swagger/libre-graph-api/#/roleManagement/ListPermissionRoleDefinitions + ### Get unifiedRoleDefinition `GET /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/roleManagement/GetPermissionRoleDefinition + ## Role Assignment ### Get appRoleAssignments of a user `GET /v1.0/users/{user-id}/appRoleAssignments` -https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.ListAppRoleAssignments + ### Grant an appRoleAssignment to a user `POST /v1.0/users/{user-id}/appRoleAssignments` -https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.CreateAppRoleAssignments + ### Delete the appRoleAssignment from a user `DELETE /v1.0/users/{user-id}/appRoleAssignments/{appRoleAssignment-id}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.DeleteAppRoleAssignments \ No newline at end of file + diff --git a/docs/dev/server/Apis/http/graph/spaces.md b/docs/dev/server/Apis/http/graph/spaces.md index 1eebb967..9653a6fe 100644 --- a/docs/dev/server/Apis/http/graph/spaces.md +++ b/docs/dev/server/Apis/http/graph/spaces.md @@ -14,6 +14,7 @@ The Spaces API is implementing a subset of the functionality of the ### Example Space The JSON representation of a Drive, as handled by the Spaces API, looks like this: + ````json { "driveAlias": "project/mars", @@ -91,11 +92,11 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi ### Create a single space `POST /drives` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives/CreateDrive + ### Create a space item (Enable sync) `POST /drives/\{drive-id\}/root/children` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/CreateDriveItem + ## Reading Spaces @@ -113,7 +114,6 @@ GET https://cloud.opencloud.test/graph/{version}/{me/}drives/?{query-parameters} Returns a list of all available spaces, even ones where the acting user is not a regular member of. You need elevated permissions to do list all spaces. If you don't have the elevated permissions, the result is the same like `GET /me/drives`. - :::info[Multiple Administration Personas] The openCloud spaces concept draws a strict line between users which can work with the content of a space and others who have the permission to manage the space. A user which is able to manage quota and space metadata does not necessarily need to be able to access the content of a space. @@ -128,7 +128,7 @@ The "Space Manager" is a user which is a regular member of a space because he ha ### List My Spaces `GET /me/drives` -https://docs.opencloud.eu/swagger/libre-graph-api/#/me.drives/ListMyDrives + ## Modifying Spaces @@ -458,6 +458,7 @@ The space to be deleted was not disabled before. } } ``` + @@ -465,28 +466,28 @@ The space to be deleted was not disabled before. ### Add member to space `POST /drives/\{drive-id\}/root/invite` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/Invite + ### Sharing space as a link `POST /drives/\{drive-id\}/root/createLink` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/CreateLinkSpaceRoot + ## Reading Space Permissions ### Listing permissions of a space `GET /drives/\{drive-id\}/root/permissions` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/ListPermissionsSpaceRoot + ## Modifying / Deleting Space Permissions ### Update permissions of a drive `PATCH /drives/\{drive-id\}/root/permissions/\{perm-id\}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/UpdatePermissionSpaceRoot + ### Set password of a link share `POST /drives/\{drive-id\}/root/permissions/\{perm-id\}/setPassword` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/SetPermissionPasswordSpaceRoot + ### Removing acess to a space `DELETE /drives/\{drive-id\}/root/permissions/\{perm-id\}` -https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/DeletePermissionSpaceRoot + diff --git a/docs/dev/server/Apis/http/graph/users.md b/docs/dev/server/Apis/http/graph/users.md index 7d7a7c3a..342f494b 100644 --- a/docs/dev/server/Apis/http/graph/users.md +++ b/docs/dev/server/Apis/http/graph/users.md @@ -9,7 +9,7 @@ The Users API is implementing a subset of the functionality of the [MS Graph User resource](https://docs.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0) The JSON representation of a User handled by the Users API looks like this: -``` +```json { "displayName": "Albert Einstein", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", @@ -28,7 +28,6 @@ Our implementation currently supports only a limited set of Attributes of Users: | id | An unique, stable readonly identifier for the user that stays the same for the whole lifetime of the User, usually a UUID | | passwordProfile | Contains the password of the users. This is only present when updating or creating users. It is never returned by the API | - ### Reading users #### `GET /me` @@ -36,12 +35,14 @@ Our implementation currently supports only a limited set of Attributes of Users: Returns the user object of the currently signed-in user Example: -``` + +```bash curl -k 'https://localhost:9200/graph/v1.0/me' -u user:password ``` Response: -``` + +```json { "displayName": "Albert Einstein", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", @@ -56,14 +57,14 @@ Returns a list of all users Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/users' -u user:password ``` Response: -``` +```json { "value": [ { @@ -88,14 +89,14 @@ Returns a list of all users Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/users?$expand=memberOf' -u user:password ``` Response: -``` +```json { "value": [ { @@ -120,7 +121,7 @@ Response: "displayName": "physics-lovers", "id": "262982c1-2362-4afa-bfdf-8cbfef64a06e" } - ], + ] }, { "displayName": "Maurice Moss", @@ -132,7 +133,7 @@ Response: "displayName": "users", "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" } - ], + ] } ] } @@ -142,13 +143,13 @@ Response: Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/users/058bff95-6708-4fe5-91e4-9ea3d377588b' -u user:password ``` Response: -``` +```json { "displayName": "Maurice Moss", "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", @@ -161,13 +162,13 @@ Response: Example: -``` +```bash curl -k 'https://localhost:9200/graph/v1.0/users/058bff95-6708-4fe5-91e4-9ea3d377588b?$expand=memberOf' -u user:password ``` Response: -``` +```json { "displayName": "Maurice Moss", "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", @@ -178,7 +179,7 @@ Response: "displayName": "users", "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" } - ], + ] } ``` @@ -192,13 +193,13 @@ Use this to create a new user. Note the missing `"id"` Attribute. It will be generated by the server: -``` +```json { "displayName": "Example User", "mail": "example@example.org", "onPremisesSamAccountName": "example", "passwordProfile": { - "password": "ThePassword" + "password": "ThePassword" } } ``` @@ -207,7 +208,7 @@ Note the missing `"id"` Attribute. It will be generated by the server: When successful, the response will return the new user, without the password, but including the newly allocated `"id"`: -``` +```json { "displayName":"Example User", "id":"c067b139-c91c-4e47-8be6-669156a0587b", @@ -220,22 +221,21 @@ When successful, the response will return the new user, without the password, bu Example: -``` +```bash curl -k --request DELETE 'https://localhost:9200/graph/v1.0/users/c067b139-c91c-4e47-8be6-669156a0587b' -u user:password ``` When successful the API returns no response body and the HTTP status code 204 (No Content) - #### `PATCH /users/{id}` Updating attributes of a single user can be done with a patch request. The Request Body contains the new values of the attributes to be updated. E.g. to update the `displayName` Attribute: -``` +```bash curl -k --header "Content-Type: application/json" \ --request PATCH --data '{"displayName": "Test User" }' \ - 'https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71' -u user:password + 'https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71' -u user:password ``` Similar to creating a user via `POST`, the `PATCH` request will return the user object containing the new attribute values. @@ -248,17 +248,16 @@ Users can change their own password by sending a POST request to `/me/changePass ##### Request Body -``` +```json { "currentPassword": "current", "newPassword": "new" } - ``` When successful the API returns no response body and the HTTP status code 204 (No Content) -``` +```bash curl -i -k --header "Content-Type: application/json" \ --request POST --data '{"currentPassword": "current", "newPassword": "new" }' \ 'https://localhost:9200/graph/v1.0/me/changePassword' -u user:current diff --git a/docs/dev/server/Apis/http/tus_upload.md b/docs/dev/server/Apis/http/tus_upload.md index 22b19fce..0794a40e 100644 --- a/docs/dev/server/Apis/http/tus_upload.md +++ b/docs/dev/server/Apis/http/tus_upload.md @@ -16,6 +16,7 @@ This documentation shows some basic examples, refer [tus official site](https:// The backend announces certain tus features to clients. WebDAV responses come with tus HTTP headers for the official tus features, and additional, OpenCloud specific features are announced via the capabilities endpoint (e.g. `https://localhost:9200/ocs/v1.php/cloud/capabilities?format=json`). The following snippet shows the relevant part of the server capabilities of OpenCloud that concerns the tus upload: + ```json { "ocs": { @@ -34,7 +35,6 @@ The following snippet shows the relevant part of the server capabilities of Open } } } - ``` | Parameter | Environment Variable | Default Value | Description | @@ -49,14 +49,15 @@ The client must send a POST request against a known upload creation URL to reque The filename has to be provided in base64-encoded format. Example: -```shell + +```bash # base64 encoded filename 'tustest.txt' is 'dHVzdGVzdC50eHQ=' echo -n 'tustest.txt' | base64 ``` -```shell +```bash curl -ks -XPOST https://cloud.opencloud.test/remote.php/dav/spaces/8d72036d-14a5-490f-889e-414064156402$196ac304-7b88-44ce-a4db-c4becef0d2e0 \ -H "Authorization: Bearer eyJhbGciOiJQUzI..."\ -H "Tus-Resumable: 1.0.0" \ @@ -66,7 +67,7 @@ curl -ks -XPOST https://cloud.opencloud.test/remote.php/dav/spaces/8d72036d-14a5 -``` +```bash < HTTP/1.1 201 Created < Access-Control-Allow-Headers: Tus-Resumable, Upload-Length, Upload-Metadata, If-Match < Access-Control-Allow-Origin: * @@ -92,7 +93,8 @@ curl -ks -XPOST https://cloud.opencloud.test/remote.php/dav/spaces/8d72036d-14a5 The server will return a temporary upload URL in the location header of the response: -``` + +```bash < Location: https://cloud.opencloud.test/data/eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJhdWQiOiJyZXZhIiwiZXhwIjoxNjk3NTMyNTc5LCJpYXQiOjE2OTc0NDYxNzksInRhcmdldCI6Imh0dHA6Ly9sb2NhbGhvc3Q6OTE1OC9kYXRhL3R1cy8zYTU3ZWZlMS04MzE0LTQ4MGEtOWY5Ny04N2Q1YzBjYTJhMTgifQ.FbrlY7mdOfsbFgMrP8OtcHlCEq72a2ZVnPD2iBo9MfM ``` @@ -101,6 +103,7 @@ The server will return a temporary upload URL in the location header of the resp Once a temporary upload URL has been created, a client can send a PATCH request to upload a file. The file content should be sent in the body of the request: + ```shell curl -ks -XPATCH https://temporary-upload-url \ -H "Authorization: Bearer eyJhbGciOiJQUzI..." \ @@ -108,10 +111,11 @@ curl -ks -XPATCH https://temporary-upload-url \ -H "Upload-Offset: 0" \ -H "Content-Type: application/offset+octet-stream" -d "01234" ``` + -``` +```bash < HTTP/1.1 204 No Content < Date: Tue, 17 Oct 2023 04:10:52 GMT < Oc-Fileid: 8d72036d-14a5-490f-889e-414064156402$73bb5450-816b-4cae-90aa-1f96adc95bd4!84e319e4-de1d-4dd8-bbd0-e51d933cdbcd @@ -144,7 +148,7 @@ curl -ks -XPATCH https://temporary-upload-url \ -``` +```bash < HTTP/1.1 204 No Content < Date: Tue, 17 Oct 2023 04:11:00 GMT < Oc-Fileid: 8d72036d-14a5-490f-889e-414064156402$73bb5450-816b-4cae-90aa-1f96adc95bd4!84e319e4-de1d-4dd8-bbd0-e51d933cdbcd @@ -240,7 +244,7 @@ Upload-metadata key-value pairs aren't specified in the general tus docs. The fo | `currentFolderId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993!a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | | `uppyId` | uppy-example/pdf-1e-application/pdf-238300 | Sent by the web UI | | `relativeFolder` | | File path relative to the folder that is being uploaded, without filename. Sent by the web UI. | -| `tusEndpoint` | https://cloud.opencloud.test/remote.php/dav/spaces/8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | +| `tusEndpoint` | | Sent by the web UI | | `uploadId` | 71d5f878-a96c-4d7b-9627-658d782c93d7 | Sent by the web UI | | `topLevelFolderId` | undefined | Sent by the web UI | | `routeName` | files-spaces-generic | Sent by the web UI | diff --git a/docs/dev/server/Apis/http/webdav/index.md b/docs/dev/server/Apis/http/webdav/index.md index 1fbe7b60..0f1e9930 100644 --- a/docs/dev/server/Apis/http/webdav/index.md +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -6,16 +6,15 @@ sidebar_position: 2 import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; - **Web** **D**istributed **A**uthoring and **V**ersioning (WebDAV) consists of a set of methods, headers, and content-types extending HTTP/1.1 for the management of resources and -properties, creation and management of resource collections, URL namespace manipulation, and resource locking (collision avoidance). WebDAV is one of the central APIs that OpenCloud uses for handling file resources, metadata and locks. - :::info RFC **WebDAV RFCs** RFC 2518 was published in February 1999. [RFC 4918](https://datatracker.ietf.org/doc/html/rfc4918), published in June 2008 obsoletes RFC 2518 with minor revisions mostly due to interoperability experience. ::: + ## Calling the WebDAV API ### Request URI @@ -26,17 +25,17 @@ RFC 2518 was published in February 1999. [RFC 4918](https://datatracker.ietf.org The request URI consists of: -| Component | Description | -|---------------|--------------------------------------------------------------------------------------------------------| +| Component | Description | +|-----------------|--------------------------------------------------------------------------------------------------------| | `{HTTP method}` | The HTTP method which is used in the request. | | `{webdav-base}` | The WebDAV base path component. Possible options are | -| | `dav/spaces/` This is the default and optimized endpoint for all WebDAV requests. | -| | `remote.php/dav/spaces/`* | -| | `remote.php/webdav/`* | -| | `webdav/`* | -| | `dav/`* | +| | `dav/spaces/` This is the default and optimized endpoint for all WebDAV requests. | +| | `remote.php/dav/spaces/`* | +| | `remote.php/webdav/`* | +| | `webdav/`* | +| | `dav/`* | | `{resourceID}` | This resourceID is used as the WebDAV root element. All children are accessed by their relative paths. | -| `{path}` | The relative path to the WebDAV root. In most of the casese, this is the space root. | +| `{path}` | The relative path to the WebDAV root. In most of the cases, this is the space root. | \* these dav endpoints are implemented for legacy reasons and should not be used. Note: The legacy endpoints **do not take the resourceID as an argument.** @@ -50,10 +49,10 @@ The request URI consists of: | GET | Retrieve a WebDAV resource. | | HEAD | Retrieve a WebDAV resource without reading the body. | | PUT | A PUT performed on an existing resource replaces the GET response entity of the resource. | -| POST | Not part of the WebDAV rfc and has no effect on a WebDAV resource. However, this method is used in the TUS protocol for uploading resources. | -| PATCH | Not part of the WebDAV rfc and has no effect on a WebDAV resource. However, this method is used in the TUS protocol for uploading resources. | +| POST | Not part of the WebDAV rfc and has no effect on a WebDAV resource. However, this method is used in the TUS protocol for uploading resources. | +| PATCH | Not part of the WebDAV rfc and has no effect on a WebDAV resource. However, this method is used in the TUS protocol for uploading resources. | | COPY | Creates a duplicate of the source resource identified by the Request-URI, in the destination resource identified by the URI in the Destination header. | -| MOVE | The MOVE operation on a non-collection resource is the logical equivalent of a copy (COPY), followed by consistency maintenance processing, followed by a delete of the source, where all three actions are performed in a single operation. | | +| MOVE | The MOVE operation on a non-collection resource is the logical equivalent of a copy (COPY), followed by consistency maintenance processing, followed by a delete of the source, where all three actions are performed in a single operation. | | DELETE | Delete the resource identified by the Request-URI. | | LOCK | A LOCK request to an existing resource will create a lock on the resource identified by the Request-URI, provided the resource is not already locked with a conflicting lock. | | UNLOCK | The UNLOCK method removes the lock identified by the lock token in the Lock-Token request header. The Request-URI must identify a resource within the scope of the lock. | @@ -149,6 +148,7 @@ The request consists of a request body and an optional `Depth` Header. Clients can use the `PROPFIND` method to retrieve properties of resources (metadata) and to list the content of a directories. ::: + ### Response @@ -267,6 +267,7 @@ in any order, each with information about an individual resource. ``` + @@ -293,6 +294,7 @@ This can occur if the request is malformed e.g. due to an invalid xml request bo Resource not found ``` + @@ -309,10 +311,10 @@ Available namespaces: | URI | Prefix | |-------------------------------------------|--------| | DAV: | d | -| http://sabredav.org/ns | s | -| http://owncloud.org/ns | oc | -| http://open-collaboration-services.org/ns | ocs | -| http://open-cloud-mesh.org/ns | ocm | +| | s | +| | oc | +| | ocs | +| | ocm | ### Request Example with declared namespaces @@ -324,51 +326,51 @@ Available namespaces: ### Supported WebDAV Properties -| Property | Desription | Example | -|---------------------------------------| -------------------------------------------------------------------------- |--------------------------------------------------------------------------------------------------------------------------------------------------| -| `` | The latest modification time. | `Fri, 30 Dec 2022 14:22:43 GMT` | -| `` | The file's etag. | `"c3a1ee4a0c28edc15b9635c3bf798013"` | -| `` | The mime type of the file. | `image/jpeg` | -| `` | Specifies the nature of the resource. | `` for a folder | -| `` | The size if it is a file in bytes. | `5` bytes | -| `` | Describes the active locks on a resource. | | -| `` | The globally unique ID of the resource. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | -| `` | The globally unique ID of the resource. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | -| `` | Direct URL to download a file from. | Not implemented. | -| `` | Determines the actions a user can take on the resource. | The value is a string containing letters that clients can use to determine available actions. | -| | | `S`: Shared | -| | | `M`: Mounted | -| | | `D`: Deletable | -| | | `NV`: Updateable, Renameable, Moveable | -| | | `W`: Updateable (file) | -| | | `CK`: Creatable (folders only) | -| | | `Z`: Deniable | -| | | `P`: Trashbin Purgable | -| | | `X`: Securely Viewable | -| | | In the early stages this was indeed a list of permissions. Over time, more flags were added and the term permissions no longer really fits well. | -| `` | List of user specified tags. | `test` | -| ` ` | The favorite state. | `0` for not favourited, `1` for favourited | -| `` | The user id of the owner of a resource. Project spaces have no owner. | `einstein` | -| `` | The display name of the owner of a resource. Project spaces have no owner. | `Albert Einstein` | -| `` | List of share types. | `0` = User Share | -| | | `1` = Group Share | -| | | `2` = Public Link | -| `` | | ``
`SHA1:1c68ea370b40c06fcaf7f26c8b1dba9d9caf5dea MD5:2205e48de5f93c784733ffcca841d2b5 ADLER32:058801ab`
`
` | -| | | Due to a bug in the very early development of OpenCloud, this value is not an array, but a string separated by whitespaces. | -| `` | Similar to `getcontentlength` but it also works for folders. | `10` bytes | -| `` | The ID of the share if the resource is part of such. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | -| `` | The root path of the shared resource if the resource is part of such. | `/shared-folder` | -| `` | The ID of the shared resource if the resource is part of such. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | -| `` | The type of the resource if it's a public link. | `folder` | -| `` | The share permissions of the resource if it's a public link. | `1` | -| `` | The expiration date of the public link. | `Tue, 14 May 2024 12:44:29 GMT` | -| `` | The date the public link was created. | `Tue, 14 May 2024 12:44:29 GMT` | -| `` | The username of the user who created the public link. | `admin` | -| `` | The original name of the resource before it was deleted. | `some-file.txt` | -| `` | The original location of the resource before it was deleted. | `some-file.txt` | -| `` | The date the resource was deleted. | `Tue, 14 May 2024 12:44:29 GMT` | -| `` | Audio meta data if the resource contains such. | `MetallicaMetallicaEnter Sandman` | -| `` | Location meta data if the resource contains such. | `51.504106-0.074575` | +| Property | Desription | Example | +|-------------------------------------|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| `` | The latest modification time. | `Fri, 30 Dec 2022 14:22:43 GMT` | +| `` | The file's etag. | `"c3a1ee4a0c28edc15b9635c3bf798013"` | +| `` | The mime type of the file. | `image/jpeg` | +| `` | Specifies the nature of the resource. | `` for a folder | +| `` | The size if it is a file in bytes. | `5` bytes | +| `` | Describes the active locks on a resource. | | +| `` | The globally unique ID of the resource. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | The globally unique ID of the resource. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | Direct URL to download a file from. | Not implemented. | +| `` | Determines the actions a user can take on the resource. | The value is a string containing letters that clients can use to determine available actions. | +| | | `S`: Shared | +| | | `M`: Mounted | +| | | `D`: Deletable | +| | | `NV`: Updateable, Renameable, Moveable | +| | | `W`: Updateable (file) | +| | | `CK`: Creatable (folders only) | +| | | `Z`: Deniable | +| | | `P`: Trashbin Purgable | +| | | `X`: Securely Viewable | +| | | In the early stages this was indeed a list of permissions. Over time, more flags were added and the term permissions no longer really fits well. | +| `` | List of user specified tags. | `test` | +| `` | The favorite state. | `0` for not favourited, `1` for favourited | +| `` | The user id of the owner of a resource. Project spaces have no owner. | `einstein` | +| `` | The display name of the owner of a resource. Project spaces have no owner. | `Albert Einstein` | +| `` | List of share types. | `0` = User Share | +| | | `1` = Group Share | +| | | `2` = Public Link | +| `` | | ``
`SHA1:1c68ea370b40c06fcaf7f26c8b1dba9d9caf5dea MD5:2205e48de5f93c784733ffcca841d2b5 ADLER32:058801ab`
`
` | +| | | Due to a bug in the very early development of OpenCloud, this value is not an array, but a string separated by whitespaces. | +| `` | Similar to `getcontentlength` but it also works for folders. | `10` bytes | +| `` | The ID of the share if the resource is part of such. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | The root path of the shared resource if the resource is part of such. | `/shared-folder` | +| `` | The ID of the shared resource if the resource is part of such. | `storage-1$27475553-7fb7-4689-b4cf-bbb635daff79!27475553-7fb7-4689-b4cf-bbb635daff79` | +| `` | The type of the resource if it's a public link. | `folder` | +| `` | The share permissions of the resource if it's a public link. | `1` | +| `` | The expiration date of the public link. | `Tue, 14 May 2024 12:44:29 GMT` | +| `` | The date the public link was created. | `Tue, 14 May 2024 12:44:29 GMT` | +| `` | The username of the user who created the public link. | `admin` | +| `` | The original name of the resource before it was deleted. | `some-file.txt` | +| `` | The original location of the resource before it was deleted. | `some-file.txt` | +| `` | The date the resource was deleted. | `Tue, 14 May 2024 12:44:29 GMT` | +| `` | Audio meta data if the resource contains such. | `MetallicaMetallicaEnter Sandman` | +| `` | Location meta data if the resource contains such. | `51.504106-0.074575` | ### Request Headers @@ -414,13 +416,13 @@ Authorization: Basic YWRtaW46YWRtaW4= This indicates that the Resource has been created successfully. -#### Body +### Body The response has no body. -#### Body +### Body ```xml @@ -429,10 +431,11 @@ The response has no body. ``` + -#### Body +### Body ```xml @@ -441,6 +444,7 @@ The response has no body. The resource you tried to create already exists ``` + @@ -450,14 +454,15 @@ To upload files to the remote server, clients can use the `PUT` method to create ### Request Headers -| Name | Usage | -|---------------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| `X-OC-Mtime` | Send the last modified
time of the file to the server in unixtime format. The server applies this mtime to the resource rather than the actual time. | -| `OC-Checksum` | Provide the checksum of the
file content to the server.
This is used to prevent corrupted data transfers. | +| Name | Usage | +|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| `X-OC-Mtime` | Send the last modified
time of the file to the server in unixtime format. The server applies this mtime to the resource rather than the actual time. | +| `OC-Checksum` | Provide the checksum of the
file content to the server.
This is used to prevent corrupted data transfers. | | `If-Match` | The If-Match request-header field is used with a method to make it
conditional. A client that has one or more entities previously
obtained from the resource can verify that one of those entities is
current by including a list of their associated entity tags in the
If-Match header field. | + ```shell curl -L -X PUT 'https://localhost:9200/dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/test.txt' \ -H 'X-OC-Mtime: 1692369418' \ @@ -467,8 +472,10 @@ curl -L -X PUT 'https://localhost:9200/dav/spaces/storage-users-1%24some-admin-u -H 'Authorization: Basic YWRtaW46YWRtaW4=' \ -d '123' ``` + + ```shell PUT /dav/spaces/storage-users-1%24some-admin-user-id-0000-000000000000/test.txt HTTP/1.1 Host: localhost:9200 @@ -481,6 +488,7 @@ Content-Length: 3 123 ``` + @@ -502,6 +510,7 @@ Oc-Fileid: storage-users-1$some-admin-user-id-0000-000000000000!07452b22-0ba9-45 Last-Modified: Fri, 18 Aug 2023 14:36:58 +0000 X-Oc-Mtime: accepted ``` + This indicates that the Resource has been updated successfully. @@ -518,6 +527,7 @@ Oc-Fileid: storage-users-1$some-admin-user-id-0000-000000000000!07452b22-0ba9-45 Last-Modified: Fri, 18 Aug 2023 14:36:58 +0000 X-Oc-Mtime: accepted ``` + This indicates that the checksum, which was sent by the client, does not match the computed one after all bytes have been received by the server. @@ -531,6 +541,7 @@ This indicates that the checksum, which was sent by the client, does not match t The computed checksum does not match the one received from the client. ``` + diff --git a/docs/dev/server/Apis/index.md b/docs/dev/server/Apis/index.md index 3662cfda..d0ca2a6b 100644 --- a/docs/dev/server/Apis/index.md +++ b/docs/dev/server/Apis/index.md @@ -12,7 +12,6 @@ Basically we have two different API "universes": [HTTP](http) and [gRPC](grpc_ap |----------------------------------|----------------------------------| | ![HTTP Logo](/img/http-logo.png) | ![gRPC Logo](/img/grpc-logo.png) | - For inter-service-communication we are using mostly gRPC calls because it has some advantages. In the future, clients may decide to use gRPC directly to make use of these advantages. ![OpenCloud APIs Architecture](/img/oc-apis.drawio.svg) @@ -41,4 +40,3 @@ gRPC APIs are typically defined by [Protocol buffers](https://developers.google. ## Versioning There are different standards for API versioning: Through URL, through request parameter, through custom header and through content negotiation. OpenCloud uses the versioning by URL concept although this creates a big code footprint. The versioning follows [SemVer](https://semver.org). We update the major version number when breaking changes are needed. Clients can decide which major version they use through the request URL. The specific implementation is documented on each API. - From a1c4420f4557a546ba46b8b3df46b61f0fabc51e Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Wed, 10 Sep 2025 15:07:09 +0200 Subject: [PATCH 3/6] style: fix markdown links --- docs/dev/server/Apis/grpc_apis/index.md | 2 +- docs/dev/server/Apis/http/authorization.md | 2 +- .../dev/server/Apis/http/graph/permissions.md | 14 +++---- docs/dev/server/Apis/http/graph/role.md | 10 ++--- docs/dev/server/Apis/http/graph/spaces.md | 26 ++++++++---- docs/dev/server/Apis/http/tus_upload.md | 42 +++++++++---------- docs/dev/server/Apis/http/webdav/index.md | 14 +++---- 7 files changed, 59 insertions(+), 51 deletions(-) diff --git a/docs/dev/server/Apis/grpc_apis/index.md b/docs/dev/server/Apis/grpc_apis/index.md index 007fedcc..dcc551f0 100644 --- a/docs/dev/server/Apis/grpc_apis/index.md +++ b/docs/dev/server/Apis/grpc_apis/index.md @@ -47,7 +47,7 @@ standards combined with freedom of choice between different programming language The [CS3 APIs](https://github.com/cs3org/cs3apis) connect storages and application providers. The CS3 APIs follow Google and Uber API design guidelines, specially on error handling and naming convention. You can read more about these -guidelines at and . +guidelines at [Google Api Design](https://cloud.google.com/apis/design) and [Uber Protocol](https://github.com/uber/prototool/blob/dev/style/README.md). The CS3 APIs use [Protocol Buffers version 3 (proto3)](https://github.com/protocolbuffers/protobuf) as their Interface Definition Language (IDL) to define the API interface and the structure of the payload messages. diff --git a/docs/dev/server/Apis/http/authorization.md b/docs/dev/server/Apis/http/authorization.md index fc8ab138..0891ab4a 100644 --- a/docs/dev/server/Apis/http/authorization.md +++ b/docs/dev/server/Apis/http/authorization.md @@ -17,7 +17,7 @@ While selecting an OpenCloud client for authentication, take note of specific li |---------|----------------------------------------------| | Android | oc://android.opencloud.eu | | iOS | oc://ios.opencloud.eu | -| Desktop |
| +| Desktop | `http://127.0.0.1`
`http://localhost` | In this example, the desktop app's `client_id` are being used. diff --git a/docs/dev/server/Apis/http/graph/permissions.md b/docs/dev/server/Apis/http/graph/permissions.md index 898778e8..17e94a1d 100644 --- a/docs/dev/server/Apis/http/graph/permissions.md +++ b/docs/dev/server/Apis/http/graph/permissions.md @@ -149,32 +149,32 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi ### Create a link share `POST /drives/{drive-id}/items/{item-id}/createLink` - +[CreateLink](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/CreateLink) ### Create a user/group share `POST /drives/{drive-id}/items/{item-id}/invite` - +[Invite](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/Invite) ## Reading Permissions ### List the effective sharing permissions on a driveitem `GET /drives/{drive-id}/items/{item-id}/permissions` - +[ListPermissions](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/ListPermissions) ### List Get sharing permission for a file or folder `GET /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` - +[GetPermission](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/GetPermission) ## Updating Permissions ### Updating sharing permission `POST /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` - +[UpdatePermission](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/UpdatePermission) ### Set password of permission `POST /drives/{drive-id}/items/{item-id}/permissions/{perm-id}/setPassword` - +[SetPermissionPassword](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/SetPermissionPassword) ### Deleting permission `DELETE /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` - +[DeletePermission](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/DeletePermission) diff --git a/docs/dev/server/Apis/http/graph/role.md b/docs/dev/server/Apis/http/graph/role.md index 04aee5a2..a06781a5 100644 --- a/docs/dev/server/Apis/http/graph/role.md +++ b/docs/dev/server/Apis/http/graph/role.md @@ -12,22 +12,22 @@ The Roles API is implementing a subset of the functionality of the ### List roleDefinitions `GET /v1beta1/roleManagement/permissions/roleDefinitions` - +[ListPermissionRoleDefinitions](https://docs.opencloud.eu/swagger/libre-graph-api/#/roleManagement/ListPermissionRoleDefinitions) ### Get unifiedRoleDefinition `GET /drives/{drive-id}/items/{item-id}/permissions/{perm-id}` - +[GetPermissionRoleDefinition](https://docs.opencloud.eu/swagger/libre-graph-api/#/roleManagement/GetPermissionRoleDefinition) ## Role Assignment ### Get appRoleAssignments of a user `GET /v1.0/users/{user-id}/appRoleAssignments` - +[ListAppRoleAssignments](https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.ListAppRoleAssignments) ### Grant an appRoleAssignment to a user `POST /v1.0/users/{user-id}/appRoleAssignments` - +[CreateAppRoleAssignments](https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.CreateAppRoleAssignments) ### Delete the appRoleAssignment from a user `DELETE /v1.0/users/{user-id}/appRoleAssignments/{appRoleAssignment-id}` - +[DeleteAppRoleAssignments](https://docs.opencloud.eu/swagger/libre-graph-api/#/user.appRoleAssignment/user.DeleteAppRoleAssignments) diff --git a/docs/dev/server/Apis/http/graph/spaces.md b/docs/dev/server/Apis/http/graph/spaces.md index 9653a6fe..99f7845c 100644 --- a/docs/dev/server/Apis/http/graph/spaces.md +++ b/docs/dev/server/Apis/http/graph/spaces.md @@ -92,11 +92,11 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi ### Create a single space `POST /drives` - +[Create Drive](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives/CreateDrive) ### Create a space item (Enable sync) `POST /drives/\{drive-id\}/root/children` - +[Create Drive Item](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/CreateDriveItem) ## Reading Spaces @@ -128,7 +128,7 @@ The "Space Manager" is a user which is a regular member of a space because he ha ### List My Spaces `GET /me/drives` - +[List My Drives](https://docs.opencloud.eu/swagger/libre-graph-api/#/me.drives/ListMyDrives) ## Modifying Spaces @@ -158,6 +158,7 @@ To limit the quota of a space you need to set the `quota.total` value. The API r + ```shell curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff' \ -H 'Content-Type: application/json' \ @@ -167,8 +168,10 @@ curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$53 } }' ``` + + ```json title="Response" {17} { "description": "Marketing team resources", @@ -212,6 +215,7 @@ curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$53 "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" } ``` + @@ -233,6 +237,7 @@ curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$53
+ ```json title="Response" {2,3,7} { "description": "Mission to mars", @@ -276,6 +281,7 @@ curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$53 "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" } ``` + @@ -287,9 +293,11 @@ This operation will make the space content unavailable for all space members. No + ```shell curl -L -k -X DELETE 'https://localhost:9200/graph/v1.0/drives/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff/' ``` + @@ -466,28 +474,28 @@ The space to be deleted was not disabled before. ### Add member to space `POST /drives/\{drive-id\}/root/invite` - +[Invite](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.permissions/Invite) ### Sharing space as a link `POST /drives/\{drive-id\}/root/createLink` - +[CreateLinkSpaceRoot](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/CreateLinkSpaceRoot) ## Reading Space Permissions ### Listing permissions of a space `GET /drives/\{drive-id\}/root/permissions` - +[ListPermissionsSpaceRoot](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/ListPermissionsSpaceRoot) ## Modifying / Deleting Space Permissions ### Update permissions of a drive `PATCH /drives/\{drive-id\}/root/permissions/\{perm-id\}` - +[UpdatePermissionSpaceRoot](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/UpdatePermissionSpaceRoot) ### Set password of a link share `POST /drives/\{drive-id\}/root/permissions/\{perm-id\}/setPassword` - +[SetPermissionPasswordSpaceRoot](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/SetPermissionPasswordSpaceRoot) ### Removing acess to a space `DELETE /drives/\{drive-id\}/root/permissions/\{perm-id\}` - +[DeletePermissionSpaceRoot](https://docs.opencloud.eu/swagger/libre-graph-api/#/drives.root/DeletePermissionSpaceRoot) diff --git a/docs/dev/server/Apis/http/tus_upload.md b/docs/dev/server/Apis/http/tus_upload.md index 0794a40e..ad653db7 100644 --- a/docs/dev/server/Apis/http/tus_upload.md +++ b/docs/dev/server/Apis/http/tus_upload.md @@ -229,24 +229,24 @@ The `Upload-Length` header of the request has to contain the exact size of the u Upload-metadata key-value pairs aren't specified in the general tus docs. The following ones are supported in the OpenCloud ecosystem: -| Parameter (key) | Example (value, MUST be Base64 encoded) | Description | -|----------------------------------|------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| -| `name` OR `filename` (mandatory) | example.pdf | Filename | -| `mtime` (recommended) | 1701708712 | Modification time (Unix time format) | -| `checksum` (recommended) | SHA1 a330de5886e5a92d78fb3f8d59fe469857759e72 | Checksum, computed from the client | -| `type` OR `filetype` | application/pdf | MIME Type, sent by the web UI | -| `relativePath` | undefined | File path relative to the folder that is being uploaded, including the filename. Sent by the web UI | -| `spaceId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | -| `spaceName` | Personal | Sent by the web UI | -| `driveAlias` | personal/admin | Sent by the web UI | -| `driveType` | personal | Sent by the web UI | -| `currentFolder` | / | Sent by the web UI | -| `currentFolderId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993!a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | -| `uppyId` | uppy-example/pdf-1e-application/pdf-238300 | Sent by the web UI | -| `relativeFolder` | | File path relative to the folder that is being uploaded, without filename. Sent by the web UI. | -| `tusEndpoint` | | Sent by the web UI | -| `uploadId` | 71d5f878-a96c-4d7b-9627-658d782c93d7 | Sent by the web UI | -| `topLevelFolderId` | undefined | Sent by the web UI | -| `routeName` | files-spaces-generic | Sent by the web UI | -| `routeDriveAliasAndItem` | cGVyc29uYWwvYWRtaW4= | Sent by the web UI | -| `routeShareId` | | Share ID when uploading into a received folder share. Sent by the web UI | +| Parameter (key) | Example (value, MUST be Base64 encoded) | Description | +|----------------------------------|--------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| `name` OR `filename` (mandatory) | example.pdf | Filename | +| `mtime` (recommended) | 1701708712 | Modification time (Unix time format) | +| `checksum` (recommended) | SHA1 a330de5886e5a92d78fb3f8d59fe469857759e72 | Checksum, computed from the client | +| `type` OR `filetype` | application/pdf | MIME Type, sent by the web UI | +| `relativePath` | undefined | File path relative to the folder that is being uploaded, including the filename. Sent by the web UI | +| `spaceId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | +| `spaceName` | Personal | Sent by the web UI | +| `driveAlias` | personal/admin | Sent by the web UI | +| `driveType` | personal | Sent by the web UI | +| `currentFolder` | / | Sent by the web UI | +| `currentFolderId` | 8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993!a9778d63-21e7-4d92-9b47-1b81144b9993 | Sent by the web UI | +| `uppyId` | uppy-example/pdf-1e-application/pdf-238300 | Sent by the web UI | +| `relativeFolder` | | File path relative to the folder that is being uploaded, without filename. Sent by the web UI. | +| `tusEndpoint` | `https://cloud.opencloud.test/remote.php/dav/spaces/8748cddf-66b7-4b85-91a7-e6d08d8e1639$a9778d63-21e7-4d92-9b47-1b81144b9993` | Sent by the web UI | +| `uploadId` | 71d5f878-a96c-4d7b-9627-658d782c93d7 | Sent by the web UI | +| `topLevelFolderId` | undefined | Sent by the web UI | +| `routeName` | files-spaces-generic | Sent by the web UI | +| `routeDriveAliasAndItem` | cGVyc29uYWwvYWRtaW4= | Sent by the web UI | +| `routeShareId` | | Share ID when uploading into a received folder share. Sent by the web UI | diff --git a/docs/dev/server/Apis/http/webdav/index.md b/docs/dev/server/Apis/http/webdav/index.md index 0f1e9930..94add850 100644 --- a/docs/dev/server/Apis/http/webdav/index.md +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -308,13 +308,13 @@ When building the body of your DAV request, you will request properties that are Available namespaces: -| URI | Prefix | -|-------------------------------------------|--------| -| DAV: | d | -| | s | -| | oc | -| | ocs | -| | ocm | +| URI | Prefix | +|---------------------------------------------|--------| +| DAV: | d | +| `http://sabredav.org/ns` | s | +| `http://owncloud.org/ns` | oc | +| `http://open-collaboration-services.org/ns` | ocs | +| `http://open-cloud-mesh.org/ns` | ocm | ### Request Example with declared namespaces From 406d3521f94fd23630dcd531d5b635b52edfb985 Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Wed, 10 Sep 2025 22:45:39 +0200 Subject: [PATCH 4/6] style: apply formatting --- docs/dev/server/Apis/grpc_apis/index.md | 3 +- docs/dev/server/Apis/http/authorization.md | 86 ++--- docs/dev/server/Apis/http/graph/groups.md | 144 ++++---- docs/dev/server/Apis/http/graph/index.md | 4 +- .../dev/server/Apis/http/graph/permissions.md | 24 +- docs/dev/server/Apis/http/graph/spaces.md | 312 +++++++++--------- docs/dev/server/Apis/http/graph/users.md | 164 ++++----- docs/dev/server/Apis/http/tus_upload.md | 16 +- docs/dev/server/Apis/http/webdav/index.md | 40 +-- docs/dev/server/Apis/index.md | 5 +- 10 files changed, 388 insertions(+), 410 deletions(-) diff --git a/docs/dev/server/Apis/grpc_apis/index.md b/docs/dev/server/Apis/grpc_apis/index.md index dcc551f0..5b6aa07e 100644 --- a/docs/dev/server/Apis/grpc_apis/index.md +++ b/docs/dev/server/Apis/grpc_apis/index.md @@ -3,8 +3,7 @@ title: gRPC sidebar_position: 2 --- - -## **R**emote   **P**rocedure   **C**alls +## **R**emote   **P**rocedure   **C**alls [gRPC](https://grpc.io) is a modern open source high performance Remote Procedure Call (RPC) framework that can run in any environment. It can efficiently connect services in and across data centers with pluggable support for load balancing, tracing, health checking and authentication. It is also applicable in last mile of distributed computing to connect devices, mobile applications and browsers to backend services. diff --git a/docs/dev/server/Apis/http/authorization.md b/docs/dev/server/Apis/http/authorization.md index 0891ab4a..366042ce 100644 --- a/docs/dev/server/Apis/http/authorization.md +++ b/docs/dev/server/Apis/http/authorization.md @@ -14,7 +14,7 @@ For detailed information on OpenCloud's support for OpenID Connect (OIDC), pleas While selecting an OpenCloud client for authentication, take note of specific limitations such as the `Redirect URI`: | Source | Redirect URI | -|---------|----------------------------------------------| +| ------- | -------------------------------------------- | | Android | oc://android.opencloud.eu | | iOS | oc://ios.opencloud.eu | | Desktop | `http://127.0.0.1`
`http://localhost` | @@ -32,67 +32,67 @@ client_id=OpenCloudDesktop To initiate the OIDC Code Flow, you can use tools like curl and a web browser. The user should be directed to a URL to authenticate and give their consent (bypassing consent is against the standard): - ```plaintext - https://cloud.opencloud.test/signin/v1/identifier/_/authorize?client_id=client_id&scope=openid+profile+email+offline_access&response_type=code&redirect_uri=http://path-to-redirect-uri - ``` + ```plaintext + https://cloud.opencloud.test/signin/v1/identifier/_/authorize?client_id=client_id&scope=openid+profile+email+offline_access&response_type=code&redirect_uri=http://path-to-redirect-uri + ``` - After a successful authentication, the browser will redirect to a URL that looks like this: + After a successful authentication, the browser will redirect to a URL that looks like this: - ```plaintext - http://path-to-redirect-uri?code=mfWsjEL0mc8gx0ftF9LFkGb__uFykaBw&scope=openid%20profile%20email%20offline_access&session_state=32b08dd...&state= - ``` + ```plaintext + http://path-to-redirect-uri?code=mfWsjEL0mc8gx0ftF9LFkGb__uFykaBw&scope=openid%20profile%20email%20offline_access&session_state=32b08dd...&state= + ``` - For the next step extract the code from the URL. + For the next step extract the code from the URL. - In the above example, - the code is `mfWsjEL0mc8gx0ftF9LFkGb__uFykaBw` + In the above example, + the code is `mfWsjEL0mc8gx0ftF9LFkGb__uFykaBw` 2. Requesting an access token The next step in the OIDC Code Flow involves an HTTP POST request to the token endpoint of the **OpenCloud Identity Server**. - ```bash - curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ - -d "grant_type=authorization_code" \ - -d "code=3a3PTcO-WWXfN3l1mDN4u7G5PzWFxatU" \ - -d "redirect_uri=http:path-to-redirect-uri" \ - -d "client_id=client_id" - ``` + ```bash + curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ + -d "grant_type=authorization_code" \ + -d "code=3a3PTcO-WWXfN3l1mDN4u7G5PzWFxatU" \ + -d "redirect_uri=http:path-to-redirect-uri" \ + -d "client_id=client_id" + ``` Response looks like this: - ```json - { - "access_token": "eyJhbGciOid...", - "token_type": "Bearer", - "id_token": "eyJhbGciOi...", - "refresh_token": "eyJhbGciOiJ...", - "expires_in": 300 - } - ``` + ```json + { + "access_token": "eyJhbGciOid...", + "token_type": "Bearer", + "id_token": "eyJhbGciOi...", + "refresh_token": "eyJhbGciOiJ...", + "expires_in": 300 + } + ``` 3. Refreshing an access token If the access token has expired, you can get a new one using the refresh token. - ```bash - curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ - -d "grant_type=refresh_token" \ - -d "refresh_token=eyJhbGciOiJ..." \ - -d "redirect_uri=http://path-to-redirect-uri" \ - -d "client_id=client_id" - ``` + ```bash + curl -vk -X POST https://cloud.opencloud.test/konnect/v1/token \ + -d "grant_type=refresh_token" \ + -d "refresh_token=eyJhbGciOiJ..." \ + -d "redirect_uri=http://path-to-redirect-uri" \ + -d "client_id=client_id" + ``` Response looks like this: - ```json - { - "access_token": "eyJhbGciOi...", - "token_type": "Bearer", - "expires_in": 300 - } - ``` + ```json + { + "access_token": "eyJhbGciOi...", + "token_type": "Bearer", + "expires_in": 300 + } + ``` ## Implicit Code Flow @@ -117,7 +117,7 @@ The user should be directed to a URL to authenticate and give their consent (byp ```bash https://cloud.opencloud.test/signin/v1/identifier/_/authorize?client_id=client_id&scope=openid+profile+email+offline_access&response_type=id_token+token&redirect_uri=http://path-to-redirect-uri&nonce=pL3UkpAQPZ8bTMGYOmxHY/dQABin8yrqipZ7iN0PY18= - ``` +``` After a successful authentication, the browser will redirect to a URL that looks like this: @@ -129,7 +129,7 @@ For the next step, extract the access_token from the URL. ```bash access_token = 'eyJhbGciOiJQ...' - ``` +``` ## Hybrid Flow diff --git a/docs/dev/server/Apis/http/graph/groups.md b/docs/dev/server/Apis/http/graph/groups.md index 22f96862..5fc2df0a 100644 --- a/docs/dev/server/Apis/http/graph/groups.md +++ b/docs/dev/server/Apis/http/graph/groups.md @@ -11,15 +11,15 @@ The JSON representation of a Group as handled by the Groups API looks like this: ```json { - "displayName": "group", - "id": "f0d97060-da16-4b0d-9fa4-d1ec43afc5f1" + "displayName": "group", + "id": "f0d97060-da16-4b0d-9fa4-d1ec43afc5f1" } ``` Our implementation currently supports two Attributes for a Group: | Attribute | Description | -|-------------|-----------------------------------------------------------------------------------------------------------------------------| +| ----------- | --------------------------------------------------------------------------------------------------------------------------- | | displayName | The groups name | | id | An unique, stable readonly identifier for the group that stays the same for the whole lifetime of the Group, usually a UUID | @@ -40,16 +40,16 @@ Response: ```json { - "value": [ - { - "displayName": "group", - "id": "38580a2e-7018-42ed-aff6-b2af0b4e9790" - }, - { - "displayName": "Example Users", - "id": "7a20f238-8a22-4458-902d-47674c317e5f" - } - ] + "value": [ + { + "displayName": "group", + "id": "38580a2e-7018-42ed-aff6-b2af0b4e9790" + }, + { + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" + } + ] } ``` @@ -68,38 +68,38 @@ Response: ```json { - "value": [ + "value": [ + { + "displayName": "group", + "id": "38580a2e-7018-42ed-aff6-b2af0b4e9790", + "members": [ { - "displayName": "group", - "id": "38580a2e-7018-42ed-aff6-b2af0b4e9790", - "members": [ - { - "displayName": "user1", - "id": "2e7b7e23-6c42-4d34-81b0-2bed34e51983", - "mail": "user1@example.org", - "onPremisesSamAccountName": "user1" - }, - { - "displayName": "user2", - "id": "b45c9e35-0d95-4165-96bc-68bff4a316ed", - "mail": "user2@example.org", - "onPremisesSamAccountName": "user2" - } - ] + "displayName": "user1", + "id": "2e7b7e23-6c42-4d34-81b0-2bed34e51983", + "mail": "user1@example.org", + "onPremisesSamAccountName": "user1" }, { - "displayName": "Example Users", - "id": "7a20f238-8a22-4458-902d-47674c317e5f", - "members": [ - { - "displayName": "user3", - "id": "026fbfef-79ef-4f5d-887b-9eaf42777239", - "mail": "user3@example.org", - "onPremisesSamAccountName": "user3" - } - ] + "displayName": "user2", + "id": "b45c9e35-0d95-4165-96bc-68bff4a316ed", + "mail": "user2@example.org", + "onPremisesSamAccountName": "user2" + } + ] + }, + { + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f", + "members": [ + { + "displayName": "user3", + "id": "026fbfef-79ef-4f5d-887b-9eaf42777239", + "mail": "user3@example.org", + "onPremisesSamAccountName": "user3" } - ] + ] + } + ] } ``` @@ -115,8 +115,8 @@ Response: ```json { - "displayName": "Example Users", - "id": "7a20f238-8a22-4458-902d-47674c317e5f" + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" } ``` @@ -132,16 +132,16 @@ Response: ```json { - "displayName": "Example Users", - "id": "7a20f238-8a22-4458-902d-47674c317e5f", - "members": [ - { - "displayName": "user3", - "id": "026fbfef-79ef-4f5d-887b-9eaf42777239", - "mail": "user3@example.org", - "onPremisesSamAccountName": "user3" - } - ] + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f", + "members": [ + { + "displayName": "user3", + "id": "026fbfef-79ef-4f5d-887b-9eaf42777239", + "mail": "user3@example.org", + "onPremisesSamAccountName": "user3" + } + ] } ``` @@ -162,18 +162,18 @@ Response: ```json [ - { - "displayName": "Test User", - "id": "c54b0588-7157-4521-bb52-c1c8ca84ea71", - "mail": "example@example.org", - "onPremisesSamAccountName": "example" - }, - { - "displayName": "Albert Einstein", - "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" - } + { + "displayName": "Test User", + "id": "c54b0588-7157-4521-bb52-c1c8ca84ea71", + "mail": "example@example.org", + "onPremisesSamAccountName": "example" + }, + { + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" + } ] ``` @@ -189,7 +189,7 @@ Note the missing `"id"` Attribute. It will be generated by the server: ```json { - "displayName": "Example Users" + "displayName": "Example Users" } ``` @@ -199,8 +199,8 @@ When successful, the response will return the new group including the newly allo ```json { - "displayName": "Example Users", - "id": "7a20f238-8a22-4458-902d-47674c317e5f" + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" } ``` @@ -245,10 +245,10 @@ Example: ```json { - "members@odata.bind": [ - "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51", - "https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71" - ] + "members@odata.bind": [ + "https://localhost:9200/graph/v1.0/users/4c510ada-c86b-4815-8820-42cdf82c3d51", + "https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71" + ] } ``` diff --git a/docs/dev/server/Apis/http/graph/index.md b/docs/dev/server/Apis/http/graph/index.md index 2aa7ed2d..faca31ea 100644 --- a/docs/dev/server/Apis/http/graph/index.md +++ b/docs/dev/server/Apis/http/graph/index.md @@ -15,7 +15,7 @@ The [API specification](https://github.com/opencloud-eu/libre-graph-api) is avai The request component consists of: | Component | Description | -|----------------------|-------------------------------------------------------------------------| +| -------------------- | ----------------------------------------------------------------------- | | `{HTTP method}` | The HTTP method which is used in the request. | | `{version}` | The version of the LibreGraph API used by the client. | | `{resource}` | The LibreGraph Resource which the client is referencing in the request. | @@ -24,7 +24,7 @@ The request component consists of: ### HTTP methods | Method | Description | -|--------|-------------------------------| +| ------ | ----------------------------- | | GET | Read data from a resource. | | POST | Create a new resource. | | PATCH | Update an existing resource. | diff --git a/docs/dev/server/Apis/http/graph/permissions.md b/docs/dev/server/Apis/http/graph/permissions.md index 17e94a1d..1d5869b6 100644 --- a/docs/dev/server/Apis/http/graph/permissions.md +++ b/docs/dev/server/Apis/http/graph/permissions.md @@ -12,7 +12,7 @@ The Permissions API is implementing a subset of the functionality of the The JSON representation of a Drive, as handled by the Spaces API, looks like this: -````json +```json { "@libre.graph.permissions.roles.allowedValues": [ { @@ -57,9 +57,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi }, { "id": "34646ab6-be32-43c9-89e6-987e0c237e9b", - "roles": [ - "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" - ], + "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"], "grantedToV2": [ { "user": { @@ -71,9 +69,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi }, { "id": "81d5bad3-3eff-410a-a2ea-eda2d14d4474", - "roles": [ - "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" - ], + "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"], "grantedToV2": [ { "user": { @@ -85,9 +81,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi }, { "id": "b470677e-a7f5-4304-8ef5-f5056a21fff1", - "roles": [ - "b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5" - ], + "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"], "grantedToV2": [ { "user": { @@ -99,9 +93,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi }, { "id": "453b02be-4ec2-4e7d-b576-09fc153de812", - "roles": [ - "fb6c3e19-e378-47e5-b277-9732f9de6e21" - ], + "roles": ["fb6c3e19-e378-47e5-b277-9732f9de6e21"], "grantedToV2": [ { "user": { @@ -114,9 +106,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi }, { "id": "86765c0d-3905-444a-9b07-76201f8cf7df", - "roles": [ - "312c0871-5ef7-4b3a-85b6-0e4074c64049" - ], + "roles": ["312c0871-5ef7-4b3a-85b6-0e4074c64049"], "grantedToV2": [ { "group": { @@ -143,7 +133,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi } ] } -```` +``` ## Creating Share Invitation / Link diff --git a/docs/dev/server/Apis/http/graph/spaces.md b/docs/dev/server/Apis/http/graph/spaces.md index 99f7845c..526507a1 100644 --- a/docs/dev/server/Apis/http/graph/spaces.md +++ b/docs/dev/server/Apis/http/graph/spaces.md @@ -15,7 +15,7 @@ The Spaces API is implementing a subset of the functionality of the The JSON representation of a Drive, as handled by the Spaces API, looks like this: -````json +```json { "driveAlias": "project/mars", "driveType": "project", @@ -47,9 +47,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi } } ], - "roles": [ - "manager" - ] + "roles": ["manager"] } ], "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925" @@ -86,7 +84,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi ], "webUrl": "https://localhost:9200/f/storage-users-1$89ad5ad2-5fdb-4877-b8c9-601a9670b925" } -```` +``` ## Creating Spaces @@ -105,7 +103,7 @@ GET https://cloud.opencloud.test/graph/{version}/{me/}drives/?{query-parameters} ``` | Component | Description | -|----------------------|------------------------------------------------------------------------------------------------------------------------| +| -------------------- | ---------------------------------------------------------------------------------------------------------------------- | | \{version\} | The version of the LibreGraph API used by the client. | | \{/me\} | The `me` component of the part is optional. If used, you only see spaces where the acting user is a regular member of. | | \{query-parameters\} | Optional parameters for the request to customize the response. | @@ -138,7 +136,7 @@ Modify the properties of a space. You need elevated permissions to execute this To limit the quota of a space you need to set the `quota.total` value. The API response will give back all actual quota properties. -````json +```json { "quota": { "remaining": 5368709120, @@ -147,10 +145,10 @@ To limit the quota of a space you need to set the `quota.total` value. The API r "used": 0 } } -```` +``` | Attribute | Description | -|-----------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | remaining | The remaining disk space in `bytes`. If the quota is not limited, this will show the total available disk space. | | state | The state of the space in regards to quota usage. This can be used for visual indicators. It can be `normal`(\<75%), `nearing`(between 75% and 89%), `critical`(between 90% and 99%) and `exceeded`(100%). | | total | The space id. The value needs to be a space ID. | @@ -174,45 +172,43 @@ curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$53 ```json title="Response" {17} { - "description": "Marketing team resources", - "driveAlias": "project/marketing", - "driveType": "project", + "description": "Marketing team resources", + "driveAlias": "project/marketing", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-18T17:13:48.385204589+01:00", + "name": "Marketing", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "remaining": 5368709120, + "state": "normal", + "total": 5368709120, + "used": 0 + }, + "root": { + "eTag": "\"f91e56554fd9305db81a93778c0fae96\"", "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "lastModifiedDateTime": "2023-01-18T17:13:48.385204589+01:00", - "name": "Marketing", - "owner": { - "user": { - "displayName": "", - "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - } - }, - "quota": { - "remaining": 5368709120, - "state": "normal", - "total": 5368709120, - "used": 0 - }, - "root": { - "eTag": "\"f91e56554fd9305db81a93778c0fae96\"", - "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "permissions": [ - { - "grantedToIdentities": [ - { - "user": { - "displayName": "Admin", - "id": "some-admin-user-id-0000-000000000000" - } - } - ], - "roles": [ - "manager" - ] + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" } + } ], - "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - }, - "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + "roles": ["manager"] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" } ``` @@ -240,45 +236,43 @@ curl -L -k -X PATCH 'https://localhost:9200/graph/v1.0/drives/storage-users-1$53 ```json title="Response" {2,3,7} { - "description": "Mission to mars", - "driveAlias": "project/mission-to-mars", - "driveType": "project", + "description": "Mission to mars", + "driveAlias": "project/mission-to-mars", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", + "name": "Mars", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "remaining": 15, + "state": "normal", + "total": 15, + "used": 0 + }, + "root": { + "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", - "name": "Mars", - "owner": { - "user": { - "displayName": "", - "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - } - }, - "quota": { - "remaining": 15, - "state": "normal", - "total": 15, - "used": 0 - }, - "root": { - "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", - "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "permissions": [ - { - "grantedToIdentities": [ - { - "user": { - "displayName": "Admin", - "id": "some-admin-user-id-0000-000000000000" - } - } - ], - "roles": [ - "manager" - ] + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" } + } ], - "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - }, - "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + "roles": ["manager"] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" } ``` @@ -308,45 +302,43 @@ A disabled space will appear in listings with a `root.deleted.state=trashed` pro ```json title="Response" {18,19,20} { - "description": "Marketing team resources", - "driveAlias": "project/marketing", - "driveType": "project", - "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", - "name": "Marketing", - "owner": { - "user": { - "displayName": "", - "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - } - }, - "quota": { - "total": 15 + "description": "Marketing team resources", + "driveAlias": "project/marketing", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", + "name": "Marketing", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "total": 15 + }, + "root": { + "deleted": { + "state": "trashed" }, - "root": { - "deleted": { - "state": "trashed" - }, - "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", - "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "permissions": [ - { - "grantedToIdentities": [ - { - "user": { - "displayName": "Admin", - "id": "some-admin-user-id-0000-000000000000" - } - } - ], - "roles": [ - "manager" - ] + "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" } + } ], - "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - }, - "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + "roles": ["manager"] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" } ``` @@ -379,45 +371,43 @@ This request needs an empty body (--data-raw '{}') to fulfil the standard libreg ```json { - "description": "Marketing team resources", - "driveAlias": "project/marketing", - "driveType": "project", + "description": "Marketing team resources", + "driveAlias": "project/marketing", + "driveType": "project", + "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", + "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", + "name": "Marketing", + "owner": { + "user": { + "displayName": "", + "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + } + }, + "quota": { + "remaining": 15, + "state": "normal", + "total": 15, + "used": 0 + }, + "root": { + "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "lastModifiedDateTime": "2023-01-19T14:17:36.094283+01:00", - "name": "Marketing", - "owner": { - "user": { - "displayName": "", - "id": "535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - } - }, - "quota": { - "remaining": 15, - "state": "normal", - "total": 15, - "used": 0 - }, - "root": { - "eTag": "\"f5fee4fdfeedd6f98956500779eee15e\"", - "id": "storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff", - "permissions": [ - { - "grantedToIdentities": [ - { - "user": { - "displayName": "Admin", - "id": "some-admin-user-id-0000-000000000000" - } - } - ], - "roles": [ - "manager" - ] + "permissions": [ + { + "grantedToIdentities": [ + { + "user": { + "displayName": "Admin", + "id": "some-admin-user-id-0000-000000000000" } + } ], - "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" - }, - "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + "roles": ["manager"] + } + ], + "webDavUrl": "https://localhost:9200/dav/spaces/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" + }, + "webUrl": "https://localhost:9200/f/storage-users-1$535aa42d-a3c7-4329-9eba-5ef48fcaa3ff" } ``` @@ -456,14 +446,14 @@ The space to be deleted was not disabled before. ```json { - "error": { - "code": "invalidRequest", - "innererror": { - "date": "2023-01-24T19:57:19Z", - "request-id": "f62af40f-bc18-475e-acd7-e9008d6bd326" - }, - "message": "error: bad request: can't purge enabled space" - } + "error": { + "code": "invalidRequest", + "innererror": { + "date": "2023-01-24T19:57:19Z", + "request-id": "f62af40f-bc18-475e-acd7-e9008d6bd326" + }, + "message": "error: bad request: can't purge enabled space" + } } ``` diff --git a/docs/dev/server/Apis/http/graph/users.md b/docs/dev/server/Apis/http/graph/users.md index 342f494b..429ad2b2 100644 --- a/docs/dev/server/Apis/http/graph/users.md +++ b/docs/dev/server/Apis/http/graph/users.md @@ -11,17 +11,17 @@ The JSON representation of a User handled by the Users API looks like this: ```json { - "displayName": "Albert Einstein", - "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" } ``` Our implementation currently supports only a limited set of Attributes of Users: | Attribute | Description | -|--------------------------|---------------------------------------------------------------------------------------------------------------------------| +| ------------------------ | ------------------------------------------------------------------------------------------------------------------------- | | displayName | The full name of the user, usually a combination of given name and last name | | mail | The user's email address | | onPremisesSamAccountName | The loginname/account name of the user | @@ -44,10 +44,10 @@ Response: ```json { - "displayName": "Albert Einstein", - "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" } ``` @@ -66,20 +66,20 @@ Response: ```json { - "value": [ - { - "displayName": "Albert Einstein", - "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" - }, - { - "displayName": "Maurice Moss", - "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss" - } - ] + "value": [ + { + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein" + }, + { + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss" + } + ] } ``` @@ -98,44 +98,44 @@ Response: ```json { - "value": [ + "value": [ + { + "displayName": "Albert Einstein", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "einstein@example.org", + "onPremisesSamAccountName": "einstein", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + }, { - "displayName": "Albert Einstein", - "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein", - "memberOf": [ - { - "displayName": "users", - "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" - }, - { - "displayName": "sailing-lovers", - "id": "6040aa17-9c64-4fef-9bd0-77234d71bad0" - }, - { - "displayName": "violin-haters", - "id": "dd58e5ec-842e-498b-8800-61f2ec6f911f" - }, - { - "displayName": "physics-lovers", - "id": "262982c1-2362-4afa-bfdf-8cbfef64a06e" - } - ] + "displayName": "sailing-lovers", + "id": "6040aa17-9c64-4fef-9bd0-77234d71bad0" }, { - "displayName": "Maurice Moss", - "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss", - "memberOf": [ - { - "displayName": "users", - "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" - } - ] + "displayName": "violin-haters", + "id": "dd58e5ec-842e-498b-8800-61f2ec6f911f" + }, + { + "displayName": "physics-lovers", + "id": "262982c1-2362-4afa-bfdf-8cbfef64a06e" + } + ] + }, + { + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" } - ] + ] + } + ] } ``` @@ -151,10 +151,10 @@ Response: ```json { - "displayName": "Maurice Moss", - "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss" + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss" } ``` @@ -170,16 +170,16 @@ Response: ```json { - "displayName": "Maurice Moss", - "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss", - "memberOf": [ - { - "displayName": "users", - "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" - } - ] + "displayName": "Maurice Moss", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "moss@example.org", + "onPremisesSamAccountName": "moss", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + } + ] } ``` @@ -195,12 +195,12 @@ Note the missing `"id"` Attribute. It will be generated by the server: ```json { - "displayName": "Example User", - "mail": "example@example.org", - "onPremisesSamAccountName": "example", - "passwordProfile": { - "password": "ThePassword" - } + "displayName": "Example User", + "mail": "example@example.org", + "onPremisesSamAccountName": "example", + "passwordProfile": { + "password": "ThePassword" + } } ``` @@ -210,10 +210,10 @@ When successful, the response will return the new user, without the password, bu ```json { - "displayName":"Example User", - "id":"c067b139-c91c-4e47-8be6-669156a0587b", - "mail":"example@example.org", - "onPremisesSamAccountName":"example" + "displayName": "Example User", + "id": "c067b139-c91c-4e47-8be6-669156a0587b", + "mail": "example@example.org", + "onPremisesSamAccountName": "example" } ``` @@ -238,7 +238,7 @@ to be updated. E.g. to update the `displayName` Attribute: 'https://localhost:9200/graph/v1.0/users/c54b0588-7157-4521-bb52-c1c8ca84ea71' -u user:password ``` -Similar to creating a user via `POST`, the `PATCH` request will return the user object containing the new attribute values. +Similar to creating a user via `POST`, the `PATCH` request will return the user object containing the new attribute values. ### Change password diff --git a/docs/dev/server/Apis/http/tus_upload.md b/docs/dev/server/Apis/http/tus_upload.md index ad653db7..55f6bbd0 100644 --- a/docs/dev/server/Apis/http/tus_upload.md +++ b/docs/dev/server/Apis/http/tus_upload.md @@ -24,21 +24,21 @@ The following snippet shows the relevant part of the server capabilities of Open "capabilities": { "files": { "tus_support": { - "version": "1.0.0", - "resumable": "1.0.0", - "extension": "creation,creation-with-upload", - "max_chunk_size": 10000000, - "http_method_override": "" - } + "version": "1.0.0", + "resumable": "1.0.0", + "extension": "creation,creation-with-upload", + "max_chunk_size": 10000000, + "http_method_override": "" } } } } + } } ``` | Parameter | Environment Variable | Default Value | Description | -|----------------|--------------------------------|---------------|---------------------------------------------------------------------| +| -------------- | ------------------------------ | ------------- | ------------------------------------------------------------------- | | max_chunk_size | FRONTEND_UPLOAD_MAX_CHUNK_SIZE | 10000000 | Announces the max chunk sizes in bytes for uploads via the clients. | ## Upload in Chunks @@ -230,7 +230,7 @@ The `Upload-Length` header of the request has to contain the exact size of the u Upload-metadata key-value pairs aren't specified in the general tus docs. The following ones are supported in the OpenCloud ecosystem: | Parameter (key) | Example (value, MUST be Base64 encoded) | Description | -|----------------------------------|--------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------------------------| +| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------- | | `name` OR `filename` (mandatory) | example.pdf | Filename | | `mtime` (recommended) | 1701708712 | Modification time (Unix time format) | | `checksum` (recommended) | SHA1 a330de5886e5a92d78fb3f8d59fe469857759e72 | Checksum, computed from the client | diff --git a/docs/dev/server/Apis/http/webdav/index.md b/docs/dev/server/Apis/http/webdav/index.md index 94add850..f46864b1 100644 --- a/docs/dev/server/Apis/http/webdav/index.md +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -26,14 +26,14 @@ RFC 2518 was published in February 1999. [RFC 4918](https://datatracker.ietf.org The request URI consists of: | Component | Description | -|-----------------|--------------------------------------------------------------------------------------------------------| +| --------------- | ------------------------------------------------------------------------------------------------------ | | `{HTTP method}` | The HTTP method which is used in the request. | | `{webdav-base}` | The WebDAV base path component. Possible options are | | | `dav/spaces/` This is the default and optimized endpoint for all WebDAV requests. | -| | `remote.php/dav/spaces/`* | -| | `remote.php/webdav/`* | -| | `webdav/`* | -| | `dav/`* | +| | `remote.php/dav/spaces/`\* | +| | `remote.php/webdav/`\* | +| | `webdav/`\* | +| | `dav/`\* | | `{resourceID}` | This resourceID is used as the WebDAV root element. All children are accessed by their relative paths. | | `{path}` | The relative path to the WebDAV root. In most of the cases, this is the space root. | @@ -42,7 +42,7 @@ The request URI consists of: ### HTTP methods | Method | Description | -|-----------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| --------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | PROPFIND | Retrieve properties as XML from a web resource. It is also overloaded to retrieve the collection structure (a.k.a. directory hierarchy) of a remote system. | | PROPPATCH | Process instructions specified in the request body to set and/or remove properties defined on the resource identified by the request uri. | | MKCOL | Create a WebDAV collection (folder) at the location specified by the request uri. | @@ -157,16 +157,16 @@ Clients can use the `PROPFIND` method to retrieve properties of resources (metad #### Multi Status Response A Multi-Status response conveys information about multiple resources -in situations where multiple status codes might be appropriate. The +in situations where multiple status codes might be appropriate. The default Multi-Status response body is an application/xml -HTTP entity with a `multistatus` root element. Further elements +HTTP entity with a `multistatus` root element. Further elements contain `200`, `300`, `400`, and `500` series status codes generated during the method invocation. Although `207` is used as the overall response status code, the recipient needs to consult the contents of the multistatus response body for further information about the success or failure of the -method execution. The response MAY be used in success, partial +method execution. The response MAY be used in success, partial success and also in failure situations. The `multistatus` root element holds zero or more `response` elements @@ -309,7 +309,7 @@ When building the body of your DAV request, you will request properties that are Available namespaces: | URI | Prefix | -|---------------------------------------------|--------| +| ------------------------------------------- | ------ | | DAV: | d | | `http://sabredav.org/ns` | s | | `http://owncloud.org/ns` | oc | @@ -327,7 +327,7 @@ Available namespaces: ### Supported WebDAV Properties | Property | Desription | Example | -|-------------------------------------|----------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------| +| ----------------------------------- | -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------ | | `` | The latest modification time. | `Fri, 30 Dec 2022 14:22:43 GMT` | | `` | The file's etag. | `"c3a1ee4a0c28edc15b9635c3bf798013"` | | `` | The mime type of the file. | `image/jpeg` | @@ -374,14 +374,14 @@ Available namespaces: ### Request Headers -A client executing a `PROPFIND` request MUST submit a Depth Header value. In practice, support for infinite-depth requests MAY be disabled, due to the performance and security concerns associated with this behavior. Servers SHOULD treat a +A client executing a `PROPFIND` request MUST submit a Depth Header value. In practice, support for infinite-depth requests MAY be disabled, due to the performance and security concerns associated with this behavior. Servers SHOULD treat a request without a Depth header as if a `Depth: infinity` header was included. Infinite depth requests are disabled by default in opencloud. -| Name | Value | -|-------------------------------------------|---------------------------------------------------------------------------------------| -| Depth | `0` = Only return the desired resource. | -| | `1` = Return the desired resource and all resources one level below in the hierarchy. | -| | `infinity` = Return all resources below the root. | +| Name | Value | +| ----- | ------------------------------------------------------------------------------------- | +| Depth | `0` = Only return the desired resource. | +| | `1` = Return the desired resource and all resources one level below in the hierarchy. | +| | `infinity` = Return all resources below the root. | :::warning Use the Depth header with caution **Depth: infinity** @@ -455,7 +455,7 @@ To upload files to the remote server, clients can use the `PUT` method to create ### Request Headers | Name | Usage | -|---------------|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| ------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | `X-OC-Mtime` | Send the last modified
time of the file to the server in unixtime format. The server applies this mtime to the resource rather than the actual time. | | `OC-Checksum` | Provide the checksum of the
file content to the server.
This is used to prevent corrupted data transfers. | | `If-Match` | The If-Match request-header field is used with a method to make it
conditional. A client that has one or more entities previously
obtained from the resource can verify that one of those entities is
current by including a list of their associated entity tags in the
If-Match header field. | @@ -505,7 +505,7 @@ The response has no body. #### Headers ```yaml -Oc-Etag: "4436aef907f41f1ac7dfd1ac3d0d455f" +Oc-Etag: '4436aef907f41f1ac7dfd1ac3d0d455f' Oc-Fileid: storage-users-1$some-admin-user-id-0000-000000000000!07452b22-0ba9-4539-96e1-3511aff7fd2f Last-Modified: Fri, 18 Aug 2023 14:36:58 +0000 X-Oc-Mtime: accepted @@ -522,7 +522,7 @@ The response has no body. #### Headers ```yaml -Oc-Etag: "4436aef907f41f1ac7dfd1ac3d0d455f" +Oc-Etag: '4436aef907f41f1ac7dfd1ac3d0d455f' Oc-Fileid: storage-users-1$some-admin-user-id-0000-000000000000!07452b22-0ba9-4539-96e1-3511aff7fd2f Last-Modified: Fri, 18 Aug 2023 14:36:58 +0000 X-Oc-Mtime: accepted diff --git a/docs/dev/server/Apis/index.md b/docs/dev/server/Apis/index.md index d0ca2a6b..355c5e97 100644 --- a/docs/dev/server/Apis/index.md +++ b/docs/dev/server/Apis/index.md @@ -3,13 +3,12 @@ title: APIs sidebar_position: 1 --- - OpenCloud provides a large set of different **application programming interfaces (APIs)**. OpenCloud is built by microservices. That means many calls to "functions" in the code are remote calls. Basically we have two different API "universes": [HTTP](http) and [gRPC](grpc_apis). | HTTP | gRPC | -|----------------------------------|----------------------------------| +| -------------------------------- | -------------------------------- | | ![HTTP Logo](/img/http-logo.png) | ![gRPC Logo](/img/grpc-logo.png) | For inter-service-communication we are using mostly gRPC calls because it has some advantages. In the future, clients may decide to use gRPC directly to make use of these advantages. @@ -35,7 +34,7 @@ OpenCloud uses a [gRPC API Gateway](../services/gateway) to route the requests t ### Protobuf -gRPC APIs are typically defined by [Protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). The different client and server stubs are created from ``*.proto`` files by code generation tools. +gRPC APIs are typically defined by [Protocol buffers](https://developers.google.com/protocol-buffers/docs/overview). The different client and server stubs are created from `*.proto` files by code generation tools. ## Versioning From e5a70ffcc2f2dbd8645276c0fdcec3b620ff6fef Mon Sep 17 00:00:00 2001 From: Michael Barz Date: Thu, 11 Sep 2025 10:06:34 +0200 Subject: [PATCH 5/6] fix: link paths and svg --- docs/dev/server/Apis/http/graph/index.md | 2 +- docs/dev/server/Apis/http/webdav/index.md | 2 +- docs/dev/server/Apis/index.md | 4 +- static/img/oc-apis.drawio.svg | 360 +++++----------------- 4 files changed, 80 insertions(+), 288 deletions(-) diff --git a/docs/dev/server/Apis/http/graph/index.md b/docs/dev/server/Apis/http/graph/index.md index faca31ea..5fda8236 100644 --- a/docs/dev/server/Apis/http/graph/index.md +++ b/docs/dev/server/Apis/http/graph/index.md @@ -59,7 +59,7 @@ For more information about OData query options please check the [API specificati ### Authorization -For development purposes the examples in the developer documentation use Basic Auth. It is disabled by default and should only be enabled by setting `PROXY_ENABLE_BASIC_AUTH` in [the proxy](../../../services/proxy/configuration/#environment-variables) for development or test instances. +For development purposes the examples in the developer documentation use Basic Auth. It is disabled by default and should only be enabled by setting `PROXY_ENABLE_BASIC_AUTH` in [the proxy](../../../Services/proxy/proxy-envvars) for development or test instances. To authenticate with a Bearer token or OpenID Connect access token replace the `-u user:password` Basic Auth option of curl with a `-H 'Authorization: Bearer '` header. A `` can be obtained by copying it from a request in the browser, although it will time out within minutes. To automatically refresh the OpenID Connect access token an ssh-agent like solution like [oidc-agent](https://github.com/indigo-dc/oidc-agent) should be used. The graph endpoints that support a preconfigured token can be found in the [API specification](https://github.com/opencloud-eu/libre-graph-api) diff --git a/docs/dev/server/Apis/http/webdav/index.md b/docs/dev/server/Apis/http/webdav/index.md index f46864b1..466307a9 100644 --- a/docs/dev/server/Apis/http/webdav/index.md +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -69,7 +69,7 @@ The WebDAV protocol was created before the REST paradigm has become the de-facto ### Authentication -For development purposes the examples in the developer documentation use Basic Auth. It is disabled by default and should only be enabled by setting `PROXY_ENABLE_BASIC_AUTH` in [the proxy](../../../services/proxy/configuration/#environment-variables) for development or test instances. +For development purposes the examples in the developer documentation use Basic Auth. It is disabled by default and should only be enabled by setting `PROXY_ENABLE_BASIC_AUTH` in [the proxy](../../../Services/proxy/proxy-envvars) for development or test instances. To authenticate with a Bearer token or OpenID Connect access token replace the `-u user:password` Basic Auth option of curl with a `-H 'Authorization: Bearer '` header. A `` can be obtained by copying it from a request in the browser, although it will time out within minutes. To automatically refresh the OpenID Connect access token an ssh-agent like solution like [oidc-agent](https://github.com/indigo-dc/oidc-agent) should be used. diff --git a/docs/dev/server/Apis/index.md b/docs/dev/server/Apis/index.md index 355c5e97..c0281a34 100644 --- a/docs/dev/server/Apis/index.md +++ b/docs/dev/server/Apis/index.md @@ -17,7 +17,7 @@ For inter-service-communication we are using mostly gRPC calls because it has so ## [HTTP](http) -HTTP APIs are mostly used for client < > server communication. Modern applications are embracing a [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) software architecture style. REST APIs are using the HTTP protocol to transfer data between clients and servers. All our clients talk to the Server using HTTP APIs. This has legacy reasons and is well-supported across many platforms and technologies. OpenCloud uses an [HTTP API gateway](../services/proxy) to route client requests to the correct service. +HTTP APIs are mostly used for client < > server communication. Modern applications are embracing a [RESTful](https://en.wikipedia.org/wiki/Representational_state_transfer) software architecture style. REST APIs are using the HTTP protocol to transfer data between clients and servers. All our clients talk to the Server using HTTP APIs. This has legacy reasons and is well-supported across many platforms and technologies. OpenCloud uses an [HTTP API gateway](../../../category/proxy) to route client requests to the correct service. ### OpenAPI @@ -30,7 +30,7 @@ Some APIs have become a de facto standard and are additionally covered by an [RF ## [gRPC](grpc_apis) In gRPC, a client application can directly call methods on a server application on a different machine as if it was a local object. This makes it easier to create distributed applications based on microservices. In gRPC we can define a service and specify the methods that can be called remotely. A gRPC client has a stub that provides the same methods and types as the server. -OpenCloud uses a [gRPC API Gateway](../services/gateway) to route the requests to the correct service. +OpenCloud uses a [gRPC API Gateway](../../../category/gateway) to route the requests to the correct service. ### Protobuf diff --git a/static/img/oc-apis.drawio.svg b/static/img/oc-apis.drawio.svg index 05e81a3a..586ef3e2 100644 --- a/static/img/oc-apis.drawio.svg +++ b/static/img/oc-apis.drawio.svg @@ -1,286 +1,78 @@ - + - - - - - -
-
-
- Client 1 -
-
-
-
- - Client 1 - -
-
- - - - -
-
-
- HTTP Gateway -
- (proxy service) -
-
-
-
- - HTTP Gateway... - -
-
- - - - -
-
-
- Client 2 -
-
-
-
- - Client 2 - -
-
- - - - -
-
-
- Client 3 -
-
-
-
- - Client 3 - -
-
- - - - - - -
-
-
- HTTP -
-
-
-
- - HTTP - -
-
- - - - - - -
-
-
- HTTP -
-
-
-
- - HTTP - -
-
- - - - - - -
-
-
- HTTP -
-
-
-
- - HTTP - -
-
- - - - -
-
-
- opencloud service -
-
-
-
- - opencloud service - -
-
- - - - -
-
-
- opencloud service -
-
-
-
- - opencloud service - -
-
- - - - -
-
-
- opencloud service -
-
-
-
- - opencloud service - -
-
- - - - - - -
-
-
- HTTP -
-
-
-
- - HTTP - -
-
- - - - -
-
-
- GRPC Gateway -
- (gateway service) -
-
-
-
- - GRPC Gateway... - -
-
- - - - - - -
-
-
- gRPC -
-
-
-
- - gRPC - -
-
- - - - - - -
-
-
- gRPC -
-
-
-
- - gRPC - -
-
- - - - - - -
-
-
- HTTP -
-
-
-
- - HTTP - -
-
-
- - - - - Viewer does not support full SVG 1.1 - - - + + + + Client 1 + + + + HTTP Gateway + (proxy service) + + + + Client 2 + + + + Client 3 + + + + opencloud service + + + opencloud service + + + opencloud service + + + + GRPC Gateway + (gateway service) + + + + + + + + HTTP + + + + + + HTTP + + + + + + HTTP + + + + + + HTTP + + + + + + HTTP + + + + + + gRPC + + + + + gRPC
\ No newline at end of file From 3af3bba4d175374e2521cf808274e333d9c3ee8b Mon Sep 17 00:00:00 2001 From: Anja Barz Date: Thu, 11 Sep 2025 13:19:26 +0200 Subject: [PATCH 6/6] remove Einstein etc. as examples and write Dennis instead --- docs/dev/server/Apis/http/graph/groups.md | 6 +-- .../dev/server/Apis/http/graph/permissions.md | 12 ++--- docs/dev/server/Apis/http/graph/users.md | 54 +++++++++---------- docs/dev/server/Apis/http/webdav/index.md | 4 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/docs/dev/server/Apis/http/graph/groups.md b/docs/dev/server/Apis/http/graph/groups.md index 5fc2df0a..e2b16100 100644 --- a/docs/dev/server/Apis/http/graph/groups.md +++ b/docs/dev/server/Apis/http/graph/groups.md @@ -169,10 +169,10 @@ Response: "onPremisesSamAccountName": "example" }, { - "displayName": "Albert Einstein", + "displayName": "Dennis Ritchie", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" } ] ``` diff --git a/docs/dev/server/Apis/http/graph/permissions.md b/docs/dev/server/Apis/http/graph/permissions.md index 1d5869b6..74127dff 100644 --- a/docs/dev/server/Apis/http/graph/permissions.md +++ b/docs/dev/server/Apis/http/graph/permissions.md @@ -62,7 +62,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi { "user": { "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "displayName": "Albert Einstein" + "displayName": "Dennis Ritchie" } } ] @@ -74,7 +74,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi { "user": { "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "displayName": "Albert Einstein" + "displayName": "Dennis Ritchie" } } ] @@ -86,7 +86,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi { "user": { "id": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", - "displayName": "Marie Skłodowska Curie" + "displayName": "Alan Turing" } } ] @@ -98,7 +98,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi { "user": { "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "displayName": "Albert Einstein" + "displayName": "Dennis Ritchie" } } ], @@ -111,7 +111,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi { "group": { "id": "167cbee2-0518-455a-bfb2-031fe0621e5d", - "displayName": "Philosophy Haters" + "displayName": "Programmers" } } ] @@ -122,7 +122,7 @@ The JSON representation of a Drive, as handled by the Spaces API, looks like thi { "user": { "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "displayName": "Albert Einstein" + "displayName": "Dennis Ritchie" } } ], diff --git a/docs/dev/server/Apis/http/graph/users.md b/docs/dev/server/Apis/http/graph/users.md index 429ad2b2..dffab4ef 100644 --- a/docs/dev/server/Apis/http/graph/users.md +++ b/docs/dev/server/Apis/http/graph/users.md @@ -11,10 +11,10 @@ The JSON representation of a User handled by the Users API looks like this: ```json { - "displayName": "Albert Einstein", + "displayName": "Dennis Ritchie", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" } ``` @@ -44,10 +44,10 @@ Response: ```json { - "displayName": "Albert Einstein", + "displayName": "Dennis Ritchie", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" } ``` @@ -68,16 +68,16 @@ Response: { "value": [ { - "displayName": "Albert Einstein", + "displayName": "Dennis Ritchie", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein" + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" }, { - "displayName": "Maurice Moss", + "displayName": "Lynn Conway", "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss" + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn" } ] } @@ -100,34 +100,34 @@ Response: { "value": [ { - "displayName": "Albert Einstein", + "displayName": "Dennis Ritchie", "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", - "mail": "einstein@example.org", - "onPremisesSamAccountName": "einstein", + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis", "memberOf": [ { "displayName": "users", "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" }, { - "displayName": "sailing-lovers", + "displayName": "basic-haters", "id": "6040aa17-9c64-4fef-9bd0-77234d71bad0" }, { - "displayName": "violin-haters", + "displayName": "bible-readers", "id": "dd58e5ec-842e-498b-8800-61f2ec6f911f" }, { - "displayName": "physics-lovers", + "displayName": "programmers", "id": "262982c1-2362-4afa-bfdf-8cbfef64a06e" } ] }, { - "displayName": "Maurice Moss", + "displayName": "Lynn Conway", "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss", + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn", "memberOf": [ { "displayName": "users", @@ -151,10 +151,10 @@ Response: ```json { - "displayName": "Maurice Moss", + "displayName": "Lynn Conway", "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss" + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn" } ``` @@ -170,10 +170,10 @@ Response: ```json { - "displayName": "Maurice Moss", + "displayName": "Lynn Conway", "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", - "mail": "moss@example.org", - "onPremisesSamAccountName": "moss", + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn", "memberOf": [ { "displayName": "users", diff --git a/docs/dev/server/Apis/http/webdav/index.md b/docs/dev/server/Apis/http/webdav/index.md index 466307a9..2698dda6 100644 --- a/docs/dev/server/Apis/http/webdav/index.md +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -350,8 +350,8 @@ Available namespaces: | | | In the early stages this was indeed a list of permissions. Over time, more flags were added and the term permissions no longer really fits well. | | `` | List of user specified tags. | `test` | | `` | The favorite state. | `0` for not favourited, `1` for favourited | -| `` | The user id of the owner of a resource. Project spaces have no owner. | `einstein` | -| `` | The display name of the owner of a resource. Project spaces have no owner. | `Albert Einstein` | +| `` | The user id of the owner of a resource. Project spaces have no owner. | `dennis` | +| `` | The display name of the owner of a resource. Project spaces have no owner. | `Dennis Ritchie` | | `` | List of share types. | `0` = User Share | | | | `1` = Group Share | | | | `2` = Public Link |