Nextcloud deprecated

Unlike oauth based file storages the Nextcloud integration works with basic authentication. Currently the user can configure his credentials himself and they are stored encrypted. The admin just needs to configure the appropriate capability for this user:

com.openexchange.capability.filestorage_nextcloud=true 

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 WebDAV root URL and the user's Nextcloud credentials.

For example:

{
  "filestorageService": "nextcloud",
  "displayName": "My Nextcloud Account",
  "configuration": {
    "login": "username",
    "password": "password",
    "url": "http://example.com:8080/remote.php/dav/files/username"
  }
}

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 nextcloud capability is 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 Nextcloud, and the storage stays available with basic authentication.

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

The deployment authenticates as a confidential client, so both the client identifier and the client secret are required. A public client that authenticates with PKCE and states no secret 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 Nextcloud otherwise.

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

The identity provider built into Nextcloud binds an access token to the user that authorized the deployment, and states no claims about that user, so it needs no scopes at all. An instance that is fronted by an identity provider of its own associates a token with one of its users by the claims the token states instead, which the identity provider may only state if a further scope asks for them, so the scopes that are requested are stated by

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

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": "nextcloud",
  "displayName": "My Nextcloud Account",
  "configuration": {
    "account": "17"
  }
}

Users and groups

The users and groups of a Nextcloud 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.nextcloud.entityResolver.attribute=mail

A Nextcloud instance identifies a user by a single value, which a deployment sets to either its mail address or the name it logs in with, so that value is matched against both attributes of a user of the server.

The user an account accesses the instance as is asked from the instance itself, as the login of an account may be the mail address of the user, and the identity an OAuth account states may be the one of an identity provider in front of the instance, while the instance names the user by its own identifier within the paths it serves, e.g. the one of the trash bin. The answer is remembered as long as the credentials of the account stay the same.

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

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

The file states one entry per line, ':'-separated:

contextId:nextCloudUserId:oxUserId               - a user of the given context
group:contextId:nextCloudGroupId:oxGroupId       - a group of the given context

Only an entity that matches a search of the instance exactly is shared with, so that an item is never shared with a user whose attribute merely contains the one that was searched for.

Reducing requests

The shares of an item and the users of the instance are looked up through the OCS API, in addition to the WebDAV requests the files are accessed through. The following lookups are optional, as each of them is a request per item:

com.openexchange.file.storage.nextcloud.entityResolver.folderPermissions=false
com.openexchange.file.storage.nextcloud.entityResolver.objectPermissions=false
  • folderPermissions expresses the shares of a folder as its permissions. A listing tells whether a folder is shared, so only the shared ones require a request of their own. Disabled by default, in which case a shared folder states that it is shared, but not with whom.
  • objectPermissions expresses the shares of a file as its object permissions. A listing tells whether a file is shared as well, 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.

Neither of them applies to the permissions a client states for an item, which are applied to its shares in any case, as that needs no such lookup.

The folders that list the items the user shares look them up by their identifiers through a single SEARCH request, rather than by a request per shared item, so that the number of requests does not grow with the number of shares.

Rejected credentials

An instance that rejects the credentials of an account, e.g. because an app password was revoked or an OAuth authorization was withdrawn, is not asked again for a while. The rejection is remembered for the account, whose folders below the root are stated along with that error meanwhile, so that a client can tell the user to put the credentials right, rather than the instance being asked with every listing.

com.openexchange.file.storage.nextcloud.retryAfterErrorInterval=300

Credentials that are put right take effect right away, as changing the configuration of an account lets the rejection be forgotten. An OAuth account that is authorized anew is tried again once the period has passed.

Nextcloud as the default file storage

A Nextcloud 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=nextcloud://6

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 folder, which holds the files of the user, is the home folder.
  • The team folders the user is a member of are the public folders, below a folder named Team folders.
  • The folders and files other users share with the user, which the instance mounts into the user's files, are the shared folders.
  • The trash bin is the trash.

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 Nextcloud as well: with com.openexchange.file.storage.nextcloud.identityProvider.url and .audience set, the user has the account nextcloud://idp, accessed with an app password the server obtains once 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 and for the secret the app password is kept with. The token has to carry the attribute the OpenID Connect app maps the user by and the display name, and the app's provider has to be configured to check bearer tokens on API and WebDAV requests. The app password is used rather than the token, as the OpenID Connect app logs the user in anew for every request that authenticates with a token, each login leaving a session among the user's devices and sessions. It shows up there under the name com.openexchange.file.storage.nextcloud.identityProvider.deviceName states, by default OX App Suite (required, do not delete), and once the user removes it, the request the instance rejects obtains a new one and is retried with it, so that the user notices nothing.
  • The folders and files are identified by the identifier the instance keeps with an item across renames and moves, its oc:fileid, rather than by the path of the item, so that the favorites and the synchronization state of the clients survive a rename. As the instance serves an item under its path only, an identifier that was not seen in a listing before is resolved by a SEARCH request for it, one per identifier. The folders without a counterpart in the storage and the items of the trash bin keep being identified as before, and an identifier that was built from a path keeps being resolved.
  • 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.nextcloud.entityResolver.folderPermissions=true for folders and com.openexchange.file.storage.nextcloud.entityResolver.objectPermissions=true for files. Both settings apply to every Nextcloud 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 Nextcloud instance that carry that address; no guest user of the installation is created, and Nextcloud notifies them. An address no user of the instance carries is shared with by mail, if the instance offers that, in which case Nextcloud mails a link to the recipient; otherwise the address is rejected. Both grants are 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. A share by mail is therefore not listed among the links of an item.
  • Drive Mail stores the attachments of a mail in the Personal folder and shares them through a sharing link of Nextcloud, which the mail carries. The attachments are not deleted once the link has expired; a deployment that wants them removed can use the retention of the instance.
  • 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.
  • Nextcloud serves no contents for deleted files, so the data export lists the trash bin with its structure only, and a deleted file can only be read again once it is restored.

Notable differences

  • The account's root holds the virtual folders Personal, Shares and Team folders, along with Trash. Shares 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.
  • The folders listing the shared items are stated as virtual folders, so that a traversal of the folder tree, such as the one archiving a folder as a ZIP file, passes them over rather than archiving those items beside the folders they are located in.
  • A Nextcloud instance limits the storage of a user as a whole, while a team folder it mounts into the files of a user may be limited on its own, so the quota of a folder is the one of the storage it belongs to. The Personal folder and each team folder state that quota within their meta as well, a storage that is not limited stating -1 as its total.
  • Each file and folder that has a counterpart in the storage states the link that opens it within the web interface of the instance as backwardLink within the nextcloud entry of its meta, so that a client needs not ask for it by a request of its own. The virtual folders and the items of the trash bin state none.
  • A preview of an item is served under an endpoint of its own, which takes the path of the item as an argument, rather than under the path of the item as an ownCloud instance serves it. A request that states the parameters of a preview along with the path of an item is answered with that item itself, so the middleware addresses that endpoint, and states no preview for an item the instance answers with 404.
  • The trash bin is served under a path of its own rather than being a folder within the user's files. A deleted item is restored by the instance to the location it was deleted from, or to the account's root if that location no longer exists, and is renamed by the instance if an item of the same name already exists there. The middleware looks the restored item up by its identifier afterwards, and moves it to the folder the request states if the instance restored it to the account's root because its original location is gone.
  • The contents of a deleted item are not served by the instance, so a deleted item can be listed, restored and purged, but not read.
  • 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 instance through a SEARCH request. It evaluates the name and the media type of an item, and combines those criteria with "and", "or" and "not", while it states an item no matter what a criterion on its size or on its modification date demands. Those criteria are therefore evaluated by the middleware against the items the instance states, and a term that states a criterion the middleware cannot evaluate either, e.g. the content of an item, is rejected.
  • The instance searches a folder recursively no matter whether a search asks for the items below it, so a search that does not is answered by the middleware leaving them out.
  • The matches of a search are sorted and paged by the middleware, as the instance orders them by its own collation. A search that is sorted by the modification date, whose term the instance evaluates completely and that asks for the items below the searched folder, is the exception: its page is requested from the instance, so that the number of matches it states does not grow with the number of files.
  • A share that grants access through a link is managed through the sharing API, while the ones granted to a user or a group of the instance, and the ones granted to a recipient by mail, are managed as the permissions of an item. Applying permissions therefore never changes the links of an item. A share granted to a user of the instance who has no counterpart on the server is stated as a guest permission carrying the user's mail address, so that a client can state and revoke it.
  • A link that is meant to grant the rights to upload into a folder cannot be created for a file, and a link that grants the rights to delete a file is granted the ones to change it instead, as a Nextcloud instance grants a link to a file the rights to read and to change it only.