OpenCloud deprecated

The OpenCloud integration accesses the files of an OpenCloud instance through WebDAV and its shares, spaces and permissions through the libre graph API. It authenticates either with basic authentication, i.e. a username and an app token, or with OAuth. The admin needs to configure the capability for the users the storage is available for:

com.openexchange.capability.filestorage_opencloud=true

Basic authentication

A client can trigger the account creation on behalf of the user by calling the new action in the fileaccount module (fileaccount?action=new). The configuration object within the request body must contain the URL the spaces of the instance are served under, along with the user's credentials.

{
  "filestorageService": "opencloud",
  "displayName": "My OpenCloud Account",
  "configuration": {
    "login": "username",
    "password": "app token",
    "url": "https://opencloud.example.com/dav/spaces"
  }
}

OAuth

OAuth requires the open-xchange-oauth package in addition to open-xchange-file-storage-webdav, see 3rd Party Integrations. OAuth itself is disabled by default and needs to be set up for the instance the deployment integrates with. Whether it is enabled is evaluated through the config cascade whenever the OAuth capabilities of a user are checked, so enabling or disabling it takes effect with a configuration reload, without a restart. As long as it is not enabled, no user is offered OAuth for OpenCloud, and the storage stays available with basic authentication.

com.openexchange.oauth.opencloud.enabled=true
com.openexchange.oauth.opencloud.hostname=opencloud.example.com
com.openexchange.oauth.opencloud.apiKey=<client id>
com.openexchange.oauth.opencloud.apiSecret=<client secret>
com.openexchange.oauth.opencloud.redirectUrl=https://appsuite.example.com/ajax/defer
com.openexchange.oauth.opencloud.productName=<name shown to the user>

The deployment authenticates as a confidential client towards the identity provider, so the client it is registered as needs a secret. A public client that authenticates with PKCE is not supported. An instance that is fronted by an identity provider of its own, e.g. Keycloak, states its endpoints, which default to the ones of the identity provider built into OpenCloud otherwise.

com.openexchange.oauth.opencloud.authorizationUrl=https://keycloak.example.com/realms/opencloud/protocol/openid-connect/auth
com.openexchange.oauth.opencloud.tokenUrl=https://keycloak.example.com/realms/opencloud/protocol/openid-connect/token
com.openexchange.oauth.opencloud.userInfoUrl=https://keycloak.example.com/realms/opencloud/protocol/openid-connect/userinfo

The token the middleware obtains needs to be accepted by the OpenCloud instance, which an instance with an identity provider of its own does not necessarily do for a client of its own registration. Such an instance associates a token with one of its users by the claims it states, e.g. the roles or the groups of that user, so the client the deployment authorizes with needs to state the same claims as the client of the instance itself. Whether a further scope is needed for the identity provider to state them is stated by

com.openexchange.oauth.opencloud.scope=openid profile email offline_access

A request the instance refuses is answered with the identifier of the request it logged the reason under, and the issuer, the party and the audience of the token that was used are stated within the debug log of the com.openexchange.file.storage.owncloud.opencloud package, so that a token the instance does not associate with a user of its own can be told apart from one it refuses altogether.

An account that is linked to an OAuth account states nothing but that account, as the URL is derived from the configured hostname. A URL that is stated for such an account is rejected, so that the access token is only ever sent to the instance that issued it.

{
  "filestorageService": "opencloud",
  "displayName": "My OpenCloud Account",
  "configuration": {
    "account": "17"
  }
}

Users and groups

The users and groups of an OpenCloud instance are matched with the ones of the server, so that a share can state who it is granted to, and so that a client can share an item with them. By default a user is matched by its mail address, falling back to the name it logs in with, and a group by its name:

com.openexchange.file.storage.opencloud.entityResolver.attribute=mail

Matching by mail address requires the OpenCloud instance to state the mail addresses of its users towards the users it serves. By default, OpenCloud omits the mail attribute from the users it lists or looks up on behalf of a user that is not an administrator, so that the users a share is granted to cannot be matched, and the shares of an item are stated without them. The graph service of the instance needs to be configured to include the addresses:

OC_SHOW_USER_EMAIL_IN_RESULTS=true

Note that this exposes the mail addresses of the users within the OpenCloud instance itself as well, e.g. within the search for a user to share an item with. A deployment that does not want that can match the users by the name they log in with instead, or map them statically as described below.

A deployment whose users and groups do not agree on those attributes can map them statically instead, which takes precedence:

com.openexchange.file.storage.opencloud.entityResolver.mappingFile=/opt/open-xchange/etc/opencloud-entities.list

The file states one entry per line, ':'-separated, just like the one of the ownCloud storage:

contextId:openCloudUserId:oxUserId               - a user of the given context
group:contextId:openCloudGroupId:oxGroupId       - a group of the given context

Reducing requests

The spaces of an account, the shares of an item and the users of the instance are looked up through the libre graph API, in addition to the WebDAV requests the files are accessed through. The spaces are looked up once per request and the role definitions and matched users are held in the cache of the deployment, while the following lookups are optional, as each of them is a request per item:

com.openexchange.file.storage.opencloud.entityResolver.folderPermissions=false
com.openexchange.file.storage.opencloud.entityResolver.objectPermissions=false
com.openexchange.file.storage.opencloud.entityResolver.createdModifiedBy=true
  • folderPermissions expresses the shares of a folder as its permissions, and applies the permissions stated for a folder as its shares. Disabled by default, as a listing does not tell whether a folder is shared, so it requires a request per listed folder.
  • objectPermissions expresses the shares of a file as its object permissions. A listing tells whether a file is shared, and the shares are only looked up if a client asks for the object permissions, but still with a request per shared file. Disabled by default, in which case a shared file states that it is shared, but not with whom. A request that states the object permissions of a file is rejected while this is disabled, just like one that states permissions for a folder while folderPermissions is disabled, as a client cannot manage what it does not see.
  • createdModifiedBy looks up the users that created and modified the items of a folder, which is a single request per listing, and only if a client asks for those users. Enabled by default.

OpenCloud as the default file storage

An OpenCloud account can serve as the default file storage of a user: the storage new files go to, next to the built-in infostore and the other storages the user holds. The account has to exist for the user, and is configured per user through the config cascade:

com.openexchange.file.storage.default=opencloud://10

The account answers the folders of the Drive module as follows, below its root folder and through the folders actions home, public and sharedWithMe where it is the default storage:

  • The personal space is the home folder, under the name the space has in OpenCloud, e.g. Personal.
  • The project spaces the user is a member of are the public folders, below a folder named Spaces.
  • The folders and files other users share with the user are the shared folders, once the user has accepted them within OpenCloud.
  • The trash bins of the spaces are the trash, one subfolder per space, named after the space.

What applies in addition to the rest of this document:

  • The account may be the identity the user logged in with, if the identity provider grants access to OpenCloud as well: with com.openexchange.file.storage.opencloud.identityProvider.url and .audience set, the user has the account opencloud://idp, accessed with the session's token exchanged for one meant for the instance; see the default file storage article for the setup at the identity provider. The token has to carry the user name and the mail address, as the instance updates the user from them.
  • Managing the shares of an item from the groupware, with users of the installation as well as with external recipients, requires the shares to be expressed as the item's permissions, as a client can only manage what it sees: com.openexchange.file.storage.opencloud.entityResolver.folderPermissions=true for folders, whose permissions are only applied as shares where they are expressed as such, and which only then state the capability permissions that lets a client offer to share them with users and groups, and com.openexchange.file.storage.opencloud.entityResolver.objectPermissions=true for files. Both settings apply to every OpenCloud account of a user, not only to the default one, with the cost described under Reducing requests.
  • External recipients, i.e. the people invited by their mail address, are granted access as the users of the OpenCloud instance that carry that address; no guest user of the installation is created, and OpenCloud notifies them. An address no user of the instance carries is rejected. Such a grant is shown as a guest permission with the recipient's address, and users of the instance who have no counterpart in the installation appear the same way, so that their access can be revoked from the groupware.
  • Drive Mail stores the attachments of a mail in the personal space and shares them through a sharing link of OpenCloud, which the mail carries. The password rules of the instance apply to the password of such a link, and a password the instance rejects is reported to the user. The attachments are not deleted once the link has expired, as OpenCloud offers no retention for files.
  • The versions of a file are counted when a single file is fetched, so that the clients offer them; a listing does not count them.
  • OpenCloud serves no contents for deleted files, so the data export lists the trash bins with their structure only, and a deleted file can only be read again once it is restored.

Notable differences

  • The account's root holds the personal space, along with the virtual folders Spaces, Shared and Trash. Shared holds Shared with me, Shared with others and Shared via link. Each of them states its kind within the meta of the folder, so that a client can tell them apart.
  • Each file and folder the instance states a private link for carries the link that opens it within the web interface of the instance as backwardLink within the opencloud entry of its meta, so that a client needs not ask for it by a request of its own. A space states the URL of its web interface the same way. The virtual folders and the items of a trash bin state none.
  • OpenCloud limits the storage per space, so the quota of a folder is the one of the space it belongs to, and the quota of the account's root is the one of the personal space. A space that is not limited states no quota rather than a limit of zero.
  • An item another user shares is listed only once the user has accepted it within OpenCloud, as an item that is not synchronized there is served under no path.
  • The permissions of a folder always apply to the items it holds, so a request that asks for them not to be cascaded is answered with a warning.
  • A search is performed by the search service of the instance, which is asked through a REPORT request, OpenCloud implementing no SEARCH method. It matches the name, the size, the media type and the modification date of an item, and combines those criteria with "and", "or" and "not". A term that states another criterion, e.g. the content of an item, is rejected.
  • The items a search yields are taken from an index rather than from the folders themselves, so an item that was stored a moment ago may not be found yet. No more than 1000 items are considered for a single search, which is stated as a warning towards the client if a search yields more. Folders are searched by name the same way, through the search service of the instance.
  • A share states the role that matches the requested permissions best. If no role grants exactly those permissions, the least permissive role that exceeds them is granted, as OpenCloud knows no role that allows writing a file without allowing to delete it. So a file that is shared for writing is shared with a role that allows deleting it, too. A share that is meant to be read-only is never granted a role that allows to change the item, though.
  • The folders of an account that have a counterpart in the instance are not held in the folder cache of the middleware, as their names and the permissions their shares yield are changed within the OpenCloud instance just as well, which the middleware would not notice. Only the virtual folders, whose properties never change, are cached.
  • Restoring a previous version of a file copies that version onto the file, so the contents that were current before are kept as a further version.