diff --git a/docs/admin/configuration/authentication-and-user-management/keycloak.md b/docs/admin/configuration/authentication-and-user-management/keycloak.md index ec2aebdf..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/_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..5b6aa07e --- /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 [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 new file mode 100644 index 00000000..366042ce --- /dev/null +++ b/docs/dev/server/Apis/http/authorization.md @@ -0,0 +1,142 @@ +--- +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..e2b16100 --- /dev/null +++ b/docs/dev/server/Apis/http/graph/groups.md @@ -0,0 +1,269 @@ +--- +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: + +```json +{ + "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: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/groups' -u user:password + +``` + +Response: + +```json +{ + "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: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/groups?$expand=members' -u user:password + +``` + +Response: + +```json +{ + "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: + +```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" +} +``` + +#### `GET /groups/{groupid}?$expand=members` + +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", + "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: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/groups/7a20f238-8a22-4458-902d-47674c317e5f/members' -u user:password + +``` + +Response: + +```json +[ + { + "displayName": "Test User", + "id": "c54b0588-7157-4521-bb52-c1c8ca84ea71", + "mail": "example@example.org", + "onPremisesSamAccountName": "example" + }, + { + "displayName": "Dennis Ritchie", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" + } +] +``` + +### Creating / Updating Groups + +#### `POST /groups` + +Use this to create a new group. + +##### Request Body + +Note the missing `"id"` Attribute. It will be generated by the server: + +```json +{ + "displayName": "Example Users" +} +``` + +##### Response + +When successful, the response will return the new group including the newly allocated `"id"`: + +```json +{ + "displayName": "Example Users", + "id": "7a20f238-8a22-4458-902d-47674c317e5f" +} +``` + +#### `DELETE /groups/{id}` + +Example: + +```bash +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: + +```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 + +``` + +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: + +```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" + ] +} +``` + +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 + +```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 +``` + +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..5fda8236 --- /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/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) + +## 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..74127dff --- /dev/null +++ b/docs/dev/server/Apis/http/graph/permissions.md @@ -0,0 +1,170 @@ +--- +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": "Dennis Ritchie" + } + } + ] + }, + { + "id": "81d5bad3-3eff-410a-a2ea-eda2d14d4474", + "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"], + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Dennis Ritchie" + } + } + ] + }, + { + "id": "b470677e-a7f5-4304-8ef5-f5056a21fff1", + "roles": ["b1e2218d-eef8-4d4c-b82d-0f1a1b48f3b5"], + "grantedToV2": [ + { + "user": { + "id": "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", + "displayName": "Alan Turing" + } + } + ] + }, + { + "id": "453b02be-4ec2-4e7d-b576-09fc153de812", + "roles": ["fb6c3e19-e378-47e5-b277-9732f9de6e21"], + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Dennis Ritchie" + } + } + ], + "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": "Programmers" + } + } + ] + }, + { + "id": "c42b5cbd-2d65-42cf-b0b6-fb6d2b762256", + "grantedToV2": [ + { + "user": { + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "displayName": "Dennis Ritchie" + } + } + ], + "@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` + +[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 new file mode 100644 index 00000000..a06781a5 --- /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` + +[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 new file mode 100644 index 00000000..526507a1 --- /dev/null +++ b/docs/dev/server/Apis/http/graph/spaces.md @@ -0,0 +1,491 @@ +--- +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` + +[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 + +```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` + +[List My 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` + +[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/graph/users.md b/docs/dev/server/Apis/http/graph/users.md new file mode 100644 index 00000000..dffab4ef --- /dev/null +++ b/docs/dev/server/Apis/http/graph/users.md @@ -0,0 +1,264 @@ +--- +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: + +```json +{ + "displayName": "Dennis Ritchie", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" +} +``` + +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: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/me' -u user:password +``` + +Response: + +```json +{ + "displayName": "Dennis Ritchie", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" +} +``` + +#### `GET /users` + +Returns a list of all users + +Example: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/users' -u user:password + +``` + +Response: + +```json +{ + "value": [ + { + "displayName": "Dennis Ritchie", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis" + }, + { + "displayName": "Lynn Conway", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn" + } + ] +} +``` + +#### `GET /users?$expand=memberOf` + +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": [ + { + "displayName": "Dennis Ritchie", + "id": "4c510ada-c86b-4815-8820-42cdf82c3d51", + "mail": "dennis@example.org", + "onPremisesSamAccountName": "dennis", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + }, + { + "displayName": "basic-haters", + "id": "6040aa17-9c64-4fef-9bd0-77234d71bad0" + }, + { + "displayName": "bible-readers", + "id": "dd58e5ec-842e-498b-8800-61f2ec6f911f" + }, + { + "displayName": "programmers", + "id": "262982c1-2362-4afa-bfdf-8cbfef64a06e" + } + ] + }, + { + "displayName": "Lynn Conway", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn", + "memberOf": [ + { + "displayName": "users", + "id": "509a9dcd-bb37-4f4f-a01a-19dca27d9cfa" + } + ] + } + ] +} +``` + +#### `GET /users/{userid or accountname}` + +Example: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/users/058bff95-6708-4fe5-91e4-9ea3d377588b' -u user:password +``` + +Response: + +```json +{ + "displayName": "Lynn Conway", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn" +} +``` + +#### `GET /users/{userid or accountname}?$expand=memberOf` + +Example: + +```bash +curl -k 'https://localhost:9200/graph/v1.0/users/058bff95-6708-4fe5-91e4-9ea3d377588b?$expand=memberOf' -u user:password +``` + +Response: + +```json +{ + "displayName": "Lynn Conway", + "id": "058bff95-6708-4fe5-91e4-9ea3d377588b", + "mail": "lynn@example.org", + "onPremisesSamAccountName": "lynn", + "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: + +```json +{ + "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"`: + +```json +{ + "displayName": "Example User", + "id": "c067b139-c91c-4e47-8be6-669156a0587b", + "mail": "example@example.org", + "onPremisesSamAccountName": "example" +} +``` + +#### `DELETE /users/{id}` + +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 +``` + +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 + +```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/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..55f6bbd0 --- /dev/null +++ b/docs/dev/server/Apis/http/tus_upload.md @@ -0,0 +1,252 @@ +--- +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: + +```bash +# base64 encoded filename 'tustest.txt' is 'dHVzdGVzdC50eHQ=' +echo -n 'tustest.txt' | base64 +``` + + + +```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" \ +-H "Upload-Length: 10" \ +-H "Upload-Metadata: filename dHVzdGVzdC50eHQ=" +``` + + + +```bash +< 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: + +```bash +< 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" +``` + + + + +```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 +< 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" +``` + + + +```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 +< 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..2698dda6 --- /dev/null +++ b/docs/dev/server/Apis/http/webdav/index.md @@ -0,0 +1,558 @@ +--- +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 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.** + +### 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/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. + +## 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. | `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 | +| `` | | ``
`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..c0281a34 --- /dev/null +++ b/docs/dev/server/Apis/index.md @@ -0,0 +1,41 @@ +--- +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](../../../category/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](../../../category/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 00000000..6f06e216 Binary files /dev/null and b/static/img/cs3org.png differ diff --git a/static/img/grpc-logo.png b/static/img/grpc-logo.png new file mode 100644 index 00000000..11ad9b1e Binary files /dev/null and b/static/img/grpc-logo.png differ diff --git a/static/img/http-logo.png b/static/img/http-logo.png new file mode 100644 index 00000000..b37f7467 Binary files /dev/null and b/static/img/http-logo.png differ diff --git a/static/img/oc-apis.drawio.svg b/static/img/oc-apis.drawio.svg new file mode 100644 index 00000000..586ef3e2 --- /dev/null +++ b/static/img/oc-apis.drawio.svg @@ -0,0 +1,78 @@ + + + + + + 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