Merge pull request #49973 from nextcloud/feat/auto-accept-trusted-server

This commit is contained in:
John Molakvoæ 2025-01-09 16:39:03 +01:00 committed by GitHub
commit e346cf63eb
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
44 changed files with 2473 additions and 389 deletions

View file

@ -39,9 +39,6 @@ use Psr\Log\LoggerInterface;
#[OpenAPI(scope: OpenAPI::SCOPE_FEDERATION)]
class RequestHandlerController extends OCSController {
/** @var string */
private $shareTable = 'share';
public function __construct(
string $appName,
IRequest $request,

View file

@ -909,99 +909,90 @@ class FederatedShareProvider implements IShareProvider {
}
/**
* check if users from other Nextcloud instances are allowed to mount public links share by this instance
*
* @return bool
* Check if users from other Nextcloud instances are allowed to mount public links share by this instance
*/
public function isOutgoingServer2serverShareEnabled() {
public function isOutgoingServer2serverShareEnabled(): bool {
if ($this->gsConfig->onlyInternalFederation()) {
return false;
}
$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes');
return ($result === 'yes');
return $result === 'yes';
}
/**
* check if users are allowed to mount public links from other Nextclouds
*
* @return bool
* Check if users are allowed to mount public links from other Nextclouds
*/
public function isIncomingServer2serverShareEnabled() {
public function isIncomingServer2serverShareEnabled(): bool {
if ($this->gsConfig->onlyInternalFederation()) {
return false;
}
$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_share_enabled', 'yes');
return ($result === 'yes');
return $result === 'yes';
}
/**
* check if users from other Nextcloud instances are allowed to send federated group shares
*
* @return bool
* Check if users from other Nextcloud instances are allowed to send federated group shares
*/
public function isOutgoingServer2serverGroupShareEnabled() {
public function isOutgoingServer2serverGroupShareEnabled(): bool {
if ($this->gsConfig->onlyInternalFederation()) {
return false;
}
$result = $this->config->getAppValue('files_sharing', 'outgoing_server2server_group_share_enabled', 'no');
return ($result === 'yes');
return $result === 'yes';
}
/**
* check if users are allowed to receive federated group shares
*
* @return bool
* Check if users are allowed to receive federated group shares
*/
public function isIncomingServer2serverGroupShareEnabled() {
public function isIncomingServer2serverGroupShareEnabled(): bool {
if ($this->gsConfig->onlyInternalFederation()) {
return false;
}
$result = $this->config->getAppValue('files_sharing', 'incoming_server2server_group_share_enabled', 'no');
return ($result === 'yes');
return $result === 'yes';
}
/**
* check if federated group sharing is supported, therefore the OCM API need to be enabled
*
* @return bool
* Check if federated group sharing is supported, therefore the OCM API need to be enabled
*/
public function isFederatedGroupSharingSupported() {
public function isFederatedGroupSharingSupported(): bool {
return $this->cloudFederationProviderManager->isReady();
}
/**
* Check if querying sharees on the lookup server is enabled
*
* @return bool
*/
public function isLookupServerQueriesEnabled() {
public function isLookupServerQueriesEnabled(): bool {
// in a global scale setup we should always query the lookup server
if ($this->gsConfig->isGlobalScaleEnabled()) {
return true;
}
$result = $this->config->getAppValue('files_sharing', 'lookupServerEnabled', 'yes');
return ($result === 'yes');
return $result === 'yes';
}
/**
* Check if it is allowed to publish user specific data to the lookup server
*
* @return bool
*/
public function isLookupServerUploadEnabled() {
public function isLookupServerUploadEnabled(): bool {
// in a global scale setup the admin is responsible to keep the lookup server up-to-date
if ($this->gsConfig->isGlobalScaleEnabled()) {
return false;
}
$result = $this->config->getAppValue('files_sharing', 'lookupServerUploadEnabled', 'yes');
return ($result === 'yes');
return $result === 'yes';
}
/**
* @inheritdoc
* Check if auto accepting incoming shares from trusted servers is enabled
*/
public function isFederatedTrustedShareAutoAccept(): bool {
$result = $this->config->getAppValue('files_sharing', 'federatedTrustedShareAutoAccept', 'yes');
return $result === 'yes';
}
public function getAccessList($nodes, $currentAccess) {
$ids = [];
foreach ($nodes as $node) {

View file

@ -10,6 +10,7 @@ use OC\AppFramework\Http;
use OC\Files\Filesystem;
use OCA\FederatedFileSharing\AddressHandler;
use OCA\FederatedFileSharing\FederatedShareProvider;
use OCA\Federation\TrustedServers;
use OCA\Files_Sharing\Activity\Providers\RemoteShares;
use OCA\Files_Sharing\External\Manager;
use OCA\GlobalSiteSelector\Service\SlaveService;
@ -66,6 +67,7 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
private LoggerInterface $logger,
private IFilenameValidator $filenameValidator,
private readonly IProviderFactory $shareProviderFactory,
private TrustedServers $trustedServers,
) {
}
@ -163,6 +165,11 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
->setObject('remote_share', $shareId, $name);
\OC::$server->getActivityManager()->publish($event);
$this->notifyAboutNewShare($shareWith, $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
// If auto-accept is enabled, accept the share
if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $this->trustedServers->isTrustedServer($remote)) {
$this->externalShareManager->acceptShare($shareId, $shareWith);
}
} else {
$groupMembers = $this->groupManager->get($shareWith)->getUsers();
foreach ($groupMembers as $user) {
@ -174,8 +181,14 @@ class CloudFederationProviderFiles implements ISignedCloudFederationProvider {
->setObject('remote_share', $shareId, $name);
\OC::$server->getActivityManager()->publish($event);
$this->notifyAboutNewShare($user->getUID(), $shareId, $ownerFederatedId, $sharedByFederatedId, $name, $ownerDisplayName);
// If auto-accept is enabled, accept the share
if ($this->federatedShareProvider->isFederatedTrustedShareAutoAccept() && $this->trustedServers->isTrustedServer($remote)) {
$this->externalShareManager->acceptShare($shareId, $user->getUID());
}
}
}
return $shareId;
} catch (\Exception $e) {
$this->logger->error('Server can not add remote share.', [

View file

@ -40,6 +40,7 @@ class Admin implements IDelegatedSettings {
$this->initialState->provideInitialState('incomingServer2serverGroupShareEnabled', $this->fedShareProvider->isIncomingServer2serverGroupShareEnabled());
$this->initialState->provideInitialState('lookupServerEnabled', $this->fedShareProvider->isLookupServerQueriesEnabled());
$this->initialState->provideInitialState('lookupServerUploadEnabled', $this->fedShareProvider->isLookupServerUploadEnabled());
$this->initialState->provideInitialState('federatedTrustedShareAutoAccept', $this->fedShareProvider->isFederatedTrustedShareAutoAccept());
return new TemplateResponse('federatedfilesharing', 'settings-admin', [], '');
}
@ -76,6 +77,7 @@ class Admin implements IDelegatedSettings {
'incomingServer2serverGroupShareEnabled',
'lookupServerEnabled',
'lookupServerUploadEnabled',
'federatedTrustedShareAutoAccept',
],
];
}

View file

@ -43,6 +43,18 @@
@update:checked="update('lookupServerUploadEnabled', lookupServerUploadEnabled)">
{{ t('federatedfilesharing', 'Allow people to publish their data to a global and public address book') }}
</NcCheckboxRadioSwitch>
<!-- Trusted server handling -->
<div class="settings-subsection">
<h3 class="settings-subsection__name">
{{ t('federatedfilesharing', 'Trusted federation') }}
</h3>
<NcCheckboxRadioSwitch type="switch"
:checked.sync="federatedTrustedShareAutoAccept"
@update:checked="update('federatedTrustedShareAutoAccept', federatedTrustedShareAutoAccept)">
{{ t('federatedfilesharing', 'Automatically accept shares from trusted federated accounts and groups by default') }}
</NcCheckboxRadioSwitch>
</div>
</NcSettingsSection>
</template>
@ -74,6 +86,7 @@ export default {
federatedGroupSharingSupported: loadState('federatedfilesharing', 'federatedGroupSharingSupported'),
lookupServerEnabled: loadState('federatedfilesharing', 'lookupServerEnabled'),
lookupServerUploadEnabled: loadState('federatedfilesharing', 'lookupServerUploadEnabled'),
federatedTrustedShareAutoAccept: loadState('federatedfilesharing', 'federatedTrustedShareAutoAccept'),
internalOnly: loadState('federatedfilesharing', 'internalOnly'),
sharingFederatedDocUrl: loadState('federatedfilesharing', 'sharingFederatedDocUrl'),
}
@ -111,3 +124,18 @@ export default {
},
}
</script>
<style scoped>
.settings-subsection {
margin-top: 20px;
}
.settings-subsection__name {
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 16px;
font-weight: bold;
max-width: 900px;
margin-top: 0;
}
</style>

View file

@ -91,10 +91,14 @@ class AdminTest extends TestCase {
->expects($this->once())
->method('isIncomingServer2serverGroupShareEnabled')
->willReturn($state);
$this->federatedShareProvider
->expects($this->once())
->method('isFederatedTrustedShareAutoAccept')
->willReturn($state);
$this->gsConfig->expects($this->once())->method('onlyInternalFederation')
->willReturn($state);
$this->initialState->expects($this->exactly(9))
$this->initialState->expects($this->exactly(10))
->method('provideInitialState')
->withConsecutive(
['internalOnly', $state],
@ -106,6 +110,7 @@ class AdminTest extends TestCase {
['incomingServer2serverGroupShareEnabled', $state],
['lookupServerEnabled', $state],
['lookupServerUploadEnabled', $state],
['federatedTrustedShareAutoAccept', $state]
);
$expected = new TemplateResponse('federatedfilesharing', 'settings-admin', [], '');

View file

@ -6,18 +6,6 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
return [
'routes' => [
[
'name' => 'Settings#addServer',
'url' => '/trusted-servers',
'verb' => 'POST'
],
[
'name' => 'Settings#removeServer',
'url' => '/trusted-servers/{id}',
'verb' => 'DELETE'
],
],
'ocs' => [
// old endpoints, only used by Nextcloud and ownCloud
[

View file

@ -16,7 +16,6 @@ return array(
'OCA\\Federation\\DAV\\FedAuth' => $baseDir . '/../lib/DAV/FedAuth.php',
'OCA\\Federation\\DbHandler' => $baseDir . '/../lib/DbHandler.php',
'OCA\\Federation\\Listener\\SabrePluginAuthInitListener' => $baseDir . '/../lib/Listener/SabrePluginAuthInitListener.php',
'OCA\\Federation\\Middleware\\AddServerMiddleware' => $baseDir . '/../lib/Middleware/AddServerMiddleware.php',
'OCA\\Federation\\Migration\\Version1010Date20200630191302' => $baseDir . '/../lib/Migration/Version1010Date20200630191302.php',
'OCA\\Federation\\Settings\\Admin' => $baseDir . '/../lib/Settings/Admin.php',
'OCA\\Federation\\SyncFederationAddressBooks' => $baseDir . '/../lib/SyncFederationAddressBooks.php',

View file

@ -31,7 +31,6 @@ class ComposerStaticInitFederation
'OCA\\Federation\\DAV\\FedAuth' => __DIR__ . '/..' . '/../lib/DAV/FedAuth.php',
'OCA\\Federation\\DbHandler' => __DIR__ . '/..' . '/../lib/DbHandler.php',
'OCA\\Federation\\Listener\\SabrePluginAuthInitListener' => __DIR__ . '/..' . '/../lib/Listener/SabrePluginAuthInitListener.php',
'OCA\\Federation\\Middleware\\AddServerMiddleware' => __DIR__ . '/..' . '/../lib/Middleware/AddServerMiddleware.php',
'OCA\\Federation\\Migration\\Version1010Date20200630191302' => __DIR__ . '/..' . '/../lib/Migration/Version1010Date20200630191302.php',
'OCA\\Federation\\Settings\\Admin' => __DIR__ . '/..' . '/../lib/Settings/Admin.php',
'OCA\\Federation\\SyncFederationAddressBooks' => __DIR__ . '/..' . '/../lib/SyncFederationAddressBooks.php',

View file

@ -9,11 +9,13 @@
#listOfTrustedServers li {
padding-bottom: 10px;
display: flex;
align-items: center;
}
.removeTrustedServer {
display: none;
vertical-align:middle;
vertical-align: middle;
padding-inline-start: 10px;
}
@ -26,20 +28,20 @@
}
#listOfTrustedServers .icon {
cursor: pointer;
display: inline-block;
cursor: pointer;
vertical-align: middle;
margin-inline-start: 10px;
}
#ocFederationAddServer #serverUrl {
width: 270px;
.serverUrl-block {
display: flex;
align-items: center;
flex-direction: row;
justify-content: flex-start;
gap: 8px;
}
.serverUrl-block {
max-width: 310px;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
.serverUrl-block input {
width: 270px;
}

View file

@ -51,9 +51,6 @@
});
$inpServerUrl.on("change keyup", function (e) {
console.log("typing away");
var url = $(this).val();
// toggle add-button visibility based on input length
@ -79,11 +76,14 @@
OC.msg.startSaving('#ocFederationAddServer .msg');
$.post(
OC.generateUrl('/apps/federation/trusted-servers'),
OC.getRootPath() + '/ocs/v2.php/apps/federation/trusted-servers',
{
url: url
}
).done(function (data) {
},
null,
'json'
).done(function({ ocs }) {
var data = ocs.data;
$("#serverUrl").attr('value', '');
$("#listOfTrustedServers").prepend(
$('<li>')
@ -95,13 +95,13 @@
OC.msg.finishedSuccess('#ocFederationAddServer .msg', data.message);
})
.fail(function (jqXHR) {
OC.msg.finishedError('#ocFederationAddServer .msg', JSON.parse(jqXHR.responseText).message);
OC.msg.finishedError('#ocFederationAddServer .msg', JSON.parse(jqXHR.responseText).ocs.meta.message);
});
};
function removeServer( id ) {
$.ajax({
url: OC.generateUrl('/apps/federation/trusted-servers/' + id),
url: OC.getRootPath() + '/ocs/v2.php/apps/federation/trusted-servers/' + id,
type: 'DELETE',
success: function(response) {
$("#ocFederationSettings").find("#" + id).remove();

View file

@ -9,7 +9,6 @@ namespace OCA\Federation\AppInfo;
use OCA\DAV\Events\SabrePluginAuthInitEvent;
use OCA\Federation\Listener\SabrePluginAuthInitListener;
use OCA\Federation\Middleware\AddServerMiddleware;
use OCP\AppFramework\App;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\AppFramework\Bootstrap\IBootstrap;
@ -25,8 +24,6 @@ class Application extends App implements IBootstrap {
}
public function register(IRegistrationContext $context): void {
$context->registerMiddleware(AddServerMiddleware::class);
$context->registerEventListener(SabrePluginAuthInitEvent::class, SabrePluginAuthInitListener::class);
}

View file

@ -17,6 +17,7 @@ use OCP\BackgroundJob\Job;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\OCS\IDiscoveryService;
use Psr\Log\LoggerInterface;
@ -43,6 +44,7 @@ class GetSharedSecret extends Job {
private LoggerInterface $logger,
private IDiscoveryService $ocsDiscoveryService,
ITimeFactory $timeFactory,
private IConfig $config,
) {
parent::__construct($timeFactory);
$this->httpClient = $httpClientService->newClient();
@ -105,6 +107,7 @@ class GetSharedSecret extends Job {
],
'timeout' => 3,
'connect_timeout' => 3,
'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
]
);

View file

@ -18,6 +18,7 @@ use OCP\BackgroundJob\IJobList;
use OCP\BackgroundJob\Job;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\OCS\IDiscoveryService;
use Psr\Log\LoggerInterface;
@ -47,6 +48,7 @@ class RequestSharedSecret extends Job {
private IDiscoveryService $ocsDiscoveryService,
private LoggerInterface $logger,
ITimeFactory $timeFactory,
private IConfig $config,
) {
parent::__construct($timeFactory);
$this->httpClient = $httpClientService->newClient();
@ -116,6 +118,7 @@ class RequestSharedSecret extends Job {
],
'timeout' => 3,
'connect_timeout' => 3,
'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
]
);

View file

@ -9,34 +9,46 @@ namespace OCA\Federation\Controller;
use OCA\Federation\Settings\Admin;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\Attribute\ApiRoute;
use OCP\AppFramework\Http\Attribute\AuthorizedAdminSetting;
use OCP\AppFramework\Http\DataResponse;
use OCP\HintException;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\AppFramework\OCSController;
use OCP\IL10N;
use OCP\IRequest;
use Psr\Log\LoggerInterface;
class SettingsController extends Controller {
class SettingsController extends OCSController {
public function __construct(
string $AppName,
IRequest $request,
private IL10N $l,
private TrustedServers $trustedServers,
private LoggerInterface $logger,
) {
parent::__construct($AppName, $request);
}
/**
* Add server to the list of trusted Nextclouds.
* Add server to the list of trusted Nextcloud servers
*
* @throws HintException
* @param string $url The URL of the server to add
* @return DataResponse<Http::STATUS_OK, array{id: int, message: string, url: string}, array{}>|DataResponse<Http::STATUS_NOT_FOUND|Http::STATUS_CONFLICT, array{message: string}, array{}>
*
* 200: Server added successfully
* 404: Server not found at the given URL
* 409: Server is already in the list of trusted servers
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'POST', url: '/trusted-servers')]
public function addServer(string $url): DataResponse {
$this->checkServer($url);
$id = $this->trustedServers->addServer($url);
$this->checkServer(trim($url));
// Add the server to the list of trusted servers, all is well
$id = $this->trustedServers->addServer(trim($url));
return new DataResponse([
'url' => $url,
'id' => $id,
@ -45,33 +57,69 @@ class SettingsController extends Controller {
}
/**
* Add server to the list of trusted Nextclouds.
* Add server to the list of trusted Nextcloud servers
*
* @param int $id The ID of the trusted server to remove
* @return DataResponse<Http::STATUS_OK, array{id: int}, array{}>|DataResponse<Http::STATUS_NOT_FOUND, array{message: string}, array{}>
*
* 200: Server removed successfully
* 404: Server not found at the given ID
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
#[ApiRoute(verb: 'DELETE', url: '/trusted-servers/{id}', requirements: ['id' => '\d+'])]
public function removeServer(int $id): DataResponse {
$this->trustedServers->removeServer($id);
return new DataResponse();
try {
$this->trustedServers->getServer($id);
} catch (\Exception $e) {
throw new OCSNotFoundException($this->l->t('No server found with ID: %s', [$id]));
}
try {
$this->trustedServers->removeServer($id);
return new DataResponse(['id' => $id]);
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['e' => $e]);
throw new OCSException($this->l->t('Could not remove server'), Http::STATUS_INTERNAL_SERVER_ERROR);
}
}
/**
* Check if the server should be added to the list of trusted servers or not.
* List all trusted servers
*
* @throws HintException
* @return DataResponse<Http::STATUS_OK, list<array{id: int, status: int, url: string}>, array{}>
*
* 200: List of trusted servers
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
protected function checkServer(string $url): bool {
#[ApiRoute(verb: 'GET', url: '/trusted-servers')]
public function getServers(): DataResponse {
$servers = $this->trustedServers->getServers();
// obfuscate the shared secret
$servers = array_map(function ($server) {
return [
'url' => $server['url'],
'id' => $server['id'],
'status' => $server['status'],
];
}, $servers);
// return the list of trusted servers
return new DataResponse($servers);
}
/**
* Check if the server should be added to the list of trusted servers or not.
*/
#[AuthorizedAdminSetting(settings: Admin::class)]
protected function checkServer(string $url): void {
if ($this->trustedServers->isTrustedServer($url) === true) {
$message = 'Server is already in the list of trusted servers.';
$hint = $this->l->t('Server is already in the list of trusted servers.');
throw new HintException($message, $hint);
throw new OCSException($this->l->t('Server is already in the list of trusted servers.'), Http::STATUS_CONFLICT);
}
if ($this->trustedServers->isNextcloudServer($url) === false) {
$message = 'No server to federate with found';
$hint = $this->l->t('No server to federate with found');
throw new HintException($message, $hint);
throw new OCSNotFoundException($this->l->t('No server to federate with found'));
}
return true;
}
}

View file

@ -1,55 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Federation\Middleware;
use OCA\Federation\Controller\SettingsController;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Middleware;
use OCP\HintException;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
class AddServerMiddleware extends Middleware {
public function __construct(
protected string $appName,
protected IL10N $l,
protected LoggerInterface $logger,
) {
}
/**
* Log error message and return a response which can be displayed to the user
*
* @param Controller $controller
* @param string $methodName
* @param \Exception $exception
* @return JSONResponse
* @throws \Exception
*/
public function afterException($controller, $methodName, \Exception $exception) {
if (($controller instanceof SettingsController) === false) {
throw $exception;
}
$this->logger->error($exception->getMessage(), [
'app' => $this->appName,
'exception' => $exception,
]);
if ($exception instanceof HintException) {
$message = $exception->getHint();
} else {
$message = $exception->getMessage();
}
return new JSONResponse(
['message' => $message],
Http::STATUS_BAD_REQUEST
);
}
}

View file

@ -96,15 +96,34 @@ class TrustedServers {
* Get all trusted servers
*
* @return list<array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}>
* @throws Exception
* @throws \Exception
*/
public function getServers() {
public function getServers(): ?array {
if ($this->trustedServersCache === null) {
$this->trustedServersCache = $this->dbHandler->getAllServer();
}
return $this->trustedServersCache;
}
/**
* Get a trusted server
*
* @return array{id: int, url: string, url_hash: string, shared_secret: ?string, status: int, sync_token: ?string}
* @throws Exception
*/
public function getServer(int $id): ?array {
if ($this->trustedServersCache === null) {
$this->trustedServersCache = $this->dbHandler->getAllServer();
}
$server = array_filter($this->trustedServersCache, fn ($server) => $server['id'] === $id);
if (empty($server)) {
throw new \Exception('No server found with ID: ' . $id);
}
return $server[0];
}
/**
* Check if given server is a trusted Nextcloud server
*/
@ -138,6 +157,7 @@ class TrustedServers {
[
'timeout' => 3,
'connect_timeout' => 3,
'verify' => !$this->config->getSystemValue('sharing.federation.allowSelfSignedCertificates', false),
]
);
if ($result->getStatusCode() === Http::STATUS_OK) {

View file

@ -0,0 +1,431 @@
{
"openapi": "3.0.3",
"info": {
"title": "federation-administration",
"version": "0.0.1",
"description": "Federation allows you to connect with other trusted servers to exchange the account directory.",
"license": {
"name": "agpl"
}
},
"components": {
"securitySchemes": {
"basic_auth": {
"type": "http",
"scheme": "basic"
},
"bearer_auth": {
"type": "http",
"scheme": "bearer"
}
},
"schemas": {
"OCSMeta": {
"type": "object",
"required": [
"status",
"statuscode"
],
"properties": {
"status": {
"type": "string"
},
"statuscode": {
"type": "integer"
},
"message": {
"type": "string"
},
"totalitems": {
"type": "string"
},
"itemsperpage": {
"type": "string"
}
}
}
}
},
"paths": {
"/ocs/v2.php/apps/federation/trusted-servers": {
"post": {
"operationId": "settings-add-server",
"summary": "Add server to the list of trusted Nextcloud servers",
"description": "This endpoint requires admin access",
"tags": [
"settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "The URL of the server to add"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Server added successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"id",
"message",
"url"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"message": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Server not found at the given URL",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"message"
],
"properties": {
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"409": {
"description": "Server is already in the list of trusted servers",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"message"
],
"properties": {
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"get": {
"operationId": "settings-get-servers",
"summary": "List all trusted servers",
"description": "This endpoint requires admin access",
"tags": [
"settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "List of trusted servers",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"status",
"url"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"status": {
"type": "integer",
"format": "int64"
},
"url": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/federation/trusted-servers/{id}": {
"delete": {
"operationId": "settings-remove-server",
"summary": "Add server to the list of trusted Nextcloud servers",
"description": "This endpoint requires admin access",
"tags": [
"settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "id",
"in": "path",
"description": "The ID of the trusted server to remove",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Server removed successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Server not found at the given ID",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"message"
],
"properties": {
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"tags": [
{
"name": "ocs_authapi",
"description": "Class OCSAuthAPI\nOCS API end-points to exchange shared secret between two connected Nextclouds"
}
]
}

View file

@ -0,0 +1,2 @@
SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
SPDX-License-Identifier: AGPL-3.0-or-later

View file

@ -0,0 +1,511 @@
{
"openapi": "3.0.3",
"info": {
"title": "federation-federation",
"version": "0.0.1",
"description": "Federation allows you to connect with other trusted servers to exchange the account directory.",
"license": {
"name": "agpl"
}
},
"components": {
"securitySchemes": {
"basic_auth": {
"type": "http",
"scheme": "basic"
},
"bearer_auth": {
"type": "http",
"scheme": "bearer"
}
},
"schemas": {
"OCSMeta": {
"type": "object",
"required": [
"status",
"statuscode"
],
"properties": {
"status": {
"type": "string"
},
"statuscode": {
"type": "integer"
},
"message": {
"type": "string"
},
"totalitems": {
"type": "string"
},
"itemsperpage": {
"type": "string"
}
}
}
}
},
"paths": {
"/ocs/v2.php/apps/federation/api/v1/shared-secret": {
"get": {
"operationId": "ocs_authapi-get-shared-secret-legacy",
"summary": "Create shared secret and return it, for legacy end-points",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "url",
"in": "query",
"description": "URL of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "token",
"in": "query",
"description": "Token of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"sharedSecret"
],
"properties": {
"sharedSecret": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"403": {
"description": "Getting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/federation/api/v1/request-shared-secret": {
"post": {
"operationId": "ocs_authapi-request-shared-secret-legacy",
"summary": "Request received to ask remote server for a shared secret, for legacy end-points",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"url",
"token"
],
"properties": {
"url": {
"type": "string",
"description": "URL of the server"
},
"token": {
"type": "string",
"description": "Token of the server"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret requested successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Requesting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/cloud/shared-secret": {
"get": {
"operationId": "ocs_authapi-get-shared-secret",
"summary": "Create shared secret and return it",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "url",
"in": "query",
"description": "URL of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "token",
"in": "query",
"description": "Token of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"sharedSecret"
],
"properties": {
"sharedSecret": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"403": {
"description": "Getting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
},
"post": {
"operationId": "ocs_authapi-request-shared-secret",
"summary": "Request received to ask remote server for a shared secret",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"url",
"token"
],
"properties": {
"url": {
"type": "string",
"description": "URL of the server"
},
"token": {
"type": "string",
"description": "Token of the server"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret requested successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Requesting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
}
},
"tags": [
{
"name": "ocs_authapi",
"description": "Class OCSAuthAPI\nOCS API end-points to exchange shared secret between two connected Nextclouds"
}
]
}

View file

@ -0,0 +1,2 @@
SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
SPDX-License-Identifier: AGPL-3.0-or-later

View file

@ -0,0 +1,885 @@
{
"openapi": "3.0.3",
"info": {
"title": "federation-full",
"version": "0.0.1",
"description": "Federation allows you to connect with other trusted servers to exchange the account directory.",
"license": {
"name": "agpl"
}
},
"components": {
"securitySchemes": {
"basic_auth": {
"type": "http",
"scheme": "basic"
},
"bearer_auth": {
"type": "http",
"scheme": "bearer"
}
},
"schemas": {
"OCSMeta": {
"type": "object",
"required": [
"status",
"statuscode"
],
"properties": {
"status": {
"type": "string"
},
"statuscode": {
"type": "integer"
},
"message": {
"type": "string"
},
"totalitems": {
"type": "string"
},
"itemsperpage": {
"type": "string"
}
}
}
}
},
"paths": {
"/ocs/v2.php/apps/federation/api/v1/shared-secret": {
"get": {
"operationId": "ocs_authapi-get-shared-secret-legacy",
"summary": "Create shared secret and return it, for legacy end-points",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "url",
"in": "query",
"description": "URL of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "token",
"in": "query",
"description": "Token of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"sharedSecret"
],
"properties": {
"sharedSecret": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"403": {
"description": "Getting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/federation/api/v1/request-shared-secret": {
"post": {
"operationId": "ocs_authapi-request-shared-secret-legacy",
"summary": "Request received to ask remote server for a shared secret, for legacy end-points",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"url",
"token"
],
"properties": {
"url": {
"type": "string",
"description": "URL of the server"
},
"token": {
"type": "string",
"description": "Token of the server"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret requested successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Requesting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/cloud/shared-secret": {
"get": {
"operationId": "ocs_authapi-get-shared-secret",
"summary": "Create shared secret and return it",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "url",
"in": "query",
"description": "URL of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "token",
"in": "query",
"description": "Token of the server",
"required": true,
"schema": {
"type": "string"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret returned",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"sharedSecret"
],
"properties": {
"sharedSecret": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"403": {
"description": "Getting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
},
"post": {
"operationId": "ocs_authapi-request-shared-secret",
"summary": "Request received to ask remote server for a shared secret",
"tags": [
"ocs_authapi"
],
"security": [
{},
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"url",
"token"
],
"properties": {
"url": {
"type": "string",
"description": "URL of the server"
},
"token": {
"type": "string",
"description": "Token of the server"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Shared secret requested successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
},
"403": {
"description": "Requesting shared secret is not allowed",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/federation/trusted-servers": {
"post": {
"operationId": "settings-add-server",
"summary": "Add server to the list of trusted Nextcloud servers",
"description": "This endpoint requires admin access",
"tags": [
"settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"requestBody": {
"required": true,
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"url"
],
"properties": {
"url": {
"type": "string",
"description": "The URL of the server to add"
}
}
}
}
}
},
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Server added successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"id",
"message",
"url"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"message": {
"type": "string"
},
"url": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Server not found at the given URL",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"message"
],
"properties": {
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
},
"409": {
"description": "Server is already in the list of trusted servers",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"message"
],
"properties": {
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
},
"get": {
"operationId": "settings-get-servers",
"summary": "List all trusted servers",
"description": "This endpoint requires admin access",
"tags": [
"settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "List of trusted servers",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "array",
"items": {
"type": "object",
"required": [
"id",
"status",
"url"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
},
"status": {
"type": "integer",
"format": "int64"
},
"url": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"/ocs/v2.php/apps/federation/trusted-servers/{id}": {
"delete": {
"operationId": "settings-remove-server",
"summary": "Add server to the list of trusted Nextcloud servers",
"description": "This endpoint requires admin access",
"tags": [
"settings"
],
"security": [
{
"bearer_auth": []
},
{
"basic_auth": []
}
],
"parameters": [
{
"name": "id",
"in": "path",
"description": "The ID of the trusted server to remove",
"required": true,
"schema": {
"type": "integer",
"format": "int64"
}
},
{
"name": "OCS-APIRequest",
"in": "header",
"description": "Required to be true for the API request to pass",
"required": true,
"schema": {
"type": "boolean",
"default": true
}
}
],
"responses": {
"200": {
"description": "Server removed successfully",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"id"
],
"properties": {
"id": {
"type": "integer",
"format": "int64"
}
}
}
}
}
}
}
}
}
},
"404": {
"description": "Server not found at the given ID",
"content": {
"application/json": {
"schema": {
"type": "object",
"required": [
"ocs"
],
"properties": {
"ocs": {
"type": "object",
"required": [
"meta",
"data"
],
"properties": {
"meta": {
"$ref": "#/components/schemas/OCSMeta"
},
"data": {
"type": "object",
"required": [
"message"
],
"properties": {
"message": {
"type": "string"
}
}
}
}
}
}
}
}
}
}
}
}
}
},
"tags": [
{
"name": "ocs_authapi",
"description": "Class OCSAuthAPI\nOCS API end-points to exchange shared secret between two connected Nextclouds"
}
]
}

View file

@ -0,0 +1,2 @@
SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
SPDX-License-Identifier: AGPL-3.0-or-later

View file

@ -1,20 +1,34 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2015-2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
/** @var array $_ */
use OCA\Federation\TrustedServers;
/** @var \OCP\IL10N $l */
script('federation', 'settings-admin');
style('federation', 'settings-admin')
use OCA\Federation\TrustedServers;
use OCP\IL10N;
use OCP\IURLGenerator;
use OCP\Server;
use OCP\Util;
/** @var IL10N $l */
Util::addScript('federation', 'settings-admin');
Util::addStyle('federation', 'settings-admin');
$urlGenerator = Server::get(IURLGenerator::class);
$documentationLink = $urlGenerator->linkToDocs('admin-sharing-federated') . '#configuring-trusted-nextcloud-servers';
$documentationLabel = $l->t('External documentation for Federated Cloud Sharing');
?>
<div id="ocFederationSettings" class="section">
<h2><?php p($l->t('Trusted servers')); ?></h2>
<h2>
<?php p($l->t('Trusted servers')); ?>
<a target="_blank" rel="noreferrer noopener" class="icon-info"
title="<?php p($documentationLabel);?>"
href="<?php p($documentationLink); ?>"></a>
</h2>
<p class="settings-hint"><?php p($l->t('Federation allows you to connect with other trusted servers to exchange the account directory. For example this will be used to auto-complete external accounts for federated sharing. It is not necessary to add a server as trusted server in order to create a federated share.')); ?></p>
<p class="settings-hint"><?php p($l->t('Each server must validate the other. This process may require a few cron cycles.')); ?></p>
<ul id="listOfTrustedServers">
<?php foreach ($_['trustedServers'] as $trustedServer) { ?>
@ -35,8 +49,9 @@ style('federation', 'settings-admin')
</li>
<?php } ?>
</ul>
<p id="ocFederationAddServer">
<button id="ocFederationAddServerButton" class=""><?php p($l->t('+ Add trusted server')); ?></button>
<div id="ocFederationAddServer">
<button id="ocFederationAddServerButton"><?php p($l->t('+ Add trusted server')); ?></button>
<div class="serverUrl hidden">
<div class="serverUrl-block">
<label for="serverUrl"><?php p($l->t('Trusted server')); ?></label>
@ -45,6 +60,5 @@ style('federation', 'settings-admin')
</div>
<span class="msg"></span>
</div>
</p>
</div>
</div>

View file

@ -17,8 +17,10 @@ use OCP\BackgroundJob\IJobList;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\OCS\IDiscoveryService;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
/**
@ -30,32 +32,16 @@ use Psr\Log\LoggerInterface;
*/
class GetSharedSecretTest extends TestCase {
/** @var \PHPUnit\Framework\MockObject\MockObject|IClient */
private $httpClient;
/** @var \PHPUnit\Framework\MockObject\MockObject|IClientService */
private $httpClientService;
/** @var \PHPUnit\Framework\MockObject\MockObject|IJobList */
private $jobList;
/** @var \PHPUnit\Framework\MockObject\MockObject|IURLGenerator */
private $urlGenerator;
/** @var \PHPUnit\Framework\MockObject\MockObject|TrustedServers */
private $trustedServers;
/** @var \PHPUnit\Framework\MockObject\MockObject|LoggerInterface */
private $logger;
/** @var \PHPUnit\Framework\MockObject\MockObject|IResponse */
private $response;
/** @var \PHPUnit\Framework\MockObject\MockObject|IDiscoveryService */
private $discoverService;
/** @var \PHPUnit\Framework\MockObject\MockObject|ITimeFactory */
private $timeFactory;
private MockObject&IClient $httpClient;
private MockObject&IClientService $httpClientService;
private MockObject&IJobList $jobList;
private MockObject&IURLGenerator $urlGenerator;
private MockObject&TrustedServers $trustedServers;
private MockObject&LoggerInterface $logger;
private MockObject&IResponse $response;
private MockObject&IDiscoveryService $discoverService;
private MockObject&ITimeFactory $timeFactory;
private MockObject&IConfig $config;
private GetSharedSecret $getSharedSecret;
@ -72,6 +58,7 @@ class GetSharedSecretTest extends TestCase {
$this->response = $this->getMockBuilder(IResponse::class)->getMock();
$this->discoverService = $this->getMockBuilder(IDiscoveryService::class)->getMock();
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->config = $this->createMock(IConfig::class);
$this->discoverService->expects($this->any())->method('discover')->willReturn([]);
$this->httpClientService->expects($this->any())->method('newClient')->willReturn($this->httpClient);
@ -83,7 +70,8 @@ class GetSharedSecretTest extends TestCase {
$this->trustedServers,
$this->logger,
$this->discoverService,
$this->timeFactory
$this->timeFactory,
$this->config,
);
}
@ -104,7 +92,8 @@ class GetSharedSecretTest extends TestCase {
$this->trustedServers,
$this->logger,
$this->discoverService,
$this->timeFactory
$this->timeFactory,
$this->config,
]
)->setMethods(['parentStart'])->getMock();
$this->invokePrivate($getSharedSecret, 'argument', [['url' => 'url', 'token' => 'token']]);
@ -176,6 +165,7 @@ class GetSharedSecretTest extends TestCase {
],
'timeout' => 3,
'connect_timeout' => 3,
'verify' => true,
]
)->willReturn($this->response);
@ -267,6 +257,7 @@ class GetSharedSecretTest extends TestCase {
],
'timeout' => 3,
'connect_timeout' => 3,
'verify' => true,
]
)->willThrowException($this->createMock(ConnectException::class));

View file

@ -16,6 +16,7 @@ use OCP\BackgroundJob\IJobList;
use OCP\Http\Client\IClient;
use OCP\Http\Client\IClientService;
use OCP\Http\Client\IResponse;
use OCP\IConfig;
use OCP\IURLGenerator;
use OCP\OCS\IDiscoveryService;
use PHPUnit\Framework\MockObject\MockObject;
@ -50,6 +51,9 @@ class RequestSharedSecretTest extends TestCase {
/** @var MockObject|ITimeFactory */
private $timeFactory;
/** @var MockObject|IConfig */
private $config;
/** @var RequestSharedSecret */
private $requestSharedSecret;
@ -66,6 +70,7 @@ class RequestSharedSecretTest extends TestCase {
$this->discoveryService = $this->getMockBuilder(IDiscoveryService::class)->getMock();
$this->logger = $this->createMock(LoggerInterface::class);
$this->timeFactory = $this->createMock(ITimeFactory::class);
$this->config = $this->createMock(IConfig::class);
$this->discoveryService->expects($this->any())->method('discover')->willReturn([]);
$this->httpClientService->expects($this->any())->method('newClient')->willReturn($this->httpClient);
@ -77,7 +82,8 @@ class RequestSharedSecretTest extends TestCase {
$this->trustedServers,
$this->discoveryService,
$this->logger,
$this->timeFactory
$this->timeFactory,
$this->config,
);
}
@ -98,7 +104,8 @@ class RequestSharedSecretTest extends TestCase {
$this->trustedServers,
$this->discoveryService,
$this->logger,
$this->timeFactory
$this->timeFactory,
$this->config,
]
)->setMethods(['parentStart'])->getMock();
$this->invokePrivate($requestSharedSecret, 'argument', [['url' => 'url', 'token' => 'token']]);
@ -170,6 +177,7 @@ class RequestSharedSecretTest extends TestCase {
],
'timeout' => 3,
'connect_timeout' => 3,
'verify' => true,
]
)->willReturn($this->response);
@ -255,6 +263,7 @@ class RequestSharedSecretTest extends TestCase {
],
'timeout' => 3,
'connect_timeout' => 3,
'verify' => true,
]
)->willThrowException($this->createMock(ConnectException::class));

View file

@ -1,5 +1,4 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@ -10,22 +9,21 @@ namespace OCA\Federation\Tests\Controller;
use OCA\Federation\Controller\SettingsController;
use OCA\Federation\TrustedServers;
use OCP\AppFramework\Http\DataResponse;
use OCP\HintException;
use OCP\AppFramework\OCS\OCSException;
use OCP\AppFramework\OCS\OCSNotFoundException;
use OCP\IL10N;
use OCP\IRequest;
use PHPUnit\Framework\MockObject\MockObject;
use Psr\Log\LoggerInterface;
use Test\TestCase;
class SettingsControllerTest extends TestCase {
private SettingsController $controller;
/** @var \PHPUnit\Framework\MockObject\MockObject|IRequest */
private $request;
/** @var \PHPUnit\Framework\MockObject\MockObject|IL10N */
private $l10n;
/** @var \PHPUnit\Framework\MockObject\MockObject|TrustedServers */
private $trustedServers;
private MockObject&IRequest $request;
private MockObject&IL10N $l10n;
private MockObject&TrustedServers $trustedServers;
private MockObject&LoggerInterface $logger;
protected function setUp(): void {
parent::setUp();
@ -34,12 +32,14 @@ class SettingsControllerTest extends TestCase {
$this->l10n = $this->getMockBuilder(IL10N::class)->getMock();
$this->trustedServers = $this->getMockBuilder(TrustedServers::class)
->disableOriginalConstructor()->getMock();
$this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$this->controller = new SettingsController(
'SettingsControllerTest',
$this->request,
$this->l10n,
$this->trustedServers
$this->trustedServers,
$this->logger,
);
}
@ -68,8 +68,6 @@ class SettingsControllerTest extends TestCase {
* @dataProvider checkServerFails
*/
public function testAddServerFail(bool $isTrustedServer, bool $isNextcloud): void {
$this->expectException(HintException::class);
$this->trustedServers
->expects($this->any())
->method('isTrustedServer')
@ -81,6 +79,12 @@ class SettingsControllerTest extends TestCase {
->with('url')
->willReturn($isNextcloud);
if ($isTrustedServer) {
$this->expectException(OCSException::class);
} else {
$this->expectException(OCSNotFoundException::class);
}
$this->controller->addServer('url');
}
@ -105,7 +109,7 @@ class SettingsControllerTest extends TestCase {
->with('url')
->willReturn(true);
$this->assertTrue(
$this->assertNull(
$this->invokePrivate($this->controller, 'checkServer', ['url'])
);
}
@ -114,8 +118,6 @@ class SettingsControllerTest extends TestCase {
* @dataProvider checkServerFails
*/
public function testCheckServerFail(bool $isTrustedServer, bool $isNextcloud): void {
$this->expectException(HintException::class);
$this->trustedServers
->expects($this->any())
->method('isTrustedServer')
@ -127,6 +129,12 @@ class SettingsControllerTest extends TestCase {
->with('url')
->willReturn($isNextcloud);
if ($isTrustedServer) {
$this->expectException(OCSException::class);
} else {
$this->expectException(OCSNotFoundException::class);
}
$this->assertTrue(
$this->invokePrivate($this->controller, 'checkServer', ['url'])
);

View file

@ -1,81 +0,0 @@
<?php
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
* SPDX-License-Identifier: AGPL-3.0-only
*/
namespace OCA\Federation\Tests\Middleware;
use OCA\Federation\Controller\SettingsController;
use OCA\Federation\Middleware\AddServerMiddleware;
use OCP\AppFramework\Http;
use OCP\HintException;
use OCP\IL10N;
use Psr\Log\LoggerInterface;
use Test\TestCase;
class AddServerMiddlewareTest extends TestCase {
/** @var \PHPUnit\Framework\MockObject\MockObject | LoggerInterface */
private $logger;
/** @var \PHPUnit\Framework\MockObject\MockObject|IL10N */
private $l10n;
private AddServerMiddleware $middleware;
/** @var \PHPUnit\Framework\MockObject\MockObject | SettingsController */
private $controller;
protected function setUp(): void {
parent::setUp();
$this->logger = $this->getMockBuilder(LoggerInterface::class)->getMock();
$this->l10n = $this->getMockBuilder(IL10N::class)->getMock();
$this->controller = $this->getMockBuilder(SettingsController::class)
->disableOriginalConstructor()->getMock();
$this->middleware = new AddServerMiddleware(
'AddServerMiddlewareTest',
$this->l10n,
$this->logger
);
}
/**
* @dataProvider dataTestAfterException
*
* @param \Exception $exception
* @param string $hint
*/
public function testAfterException($exception, $hint): void {
$this->logger->expects($this->once())->method('error');
$this->l10n->expects($this->any())->method('t')
->willReturnCallback(
function (string $message): string {
return $message;
}
);
$result = $this->middleware->afterException($this->controller, 'method', $exception);
$this->assertSame(Http::STATUS_BAD_REQUEST,
$result->getStatus()
);
$data = $result->getData();
$this->assertSame($hint,
$data['message']
);
}
public function dataTestAfterException() {
return [
[new HintException('message', 'hint'), 'hint'],
[new \Exception('message'), 'message'],
];
}
}

View file

@ -129,7 +129,7 @@ class Manager {
'mountpoint' => $mountPoint,
'owner' => $owner
];
return $this->mountShare($options);
return $this->mountShare($options, $user);
}
/**
@ -214,11 +214,12 @@ class Manager {
* @param int $id share id
* @return mixed share of false
*/
public function getShare($id) {
public function getShare(int $id, ?string $user = null): array|false {
$user = $user ?? $this->uid;
$share = $this->fetchShare($id);
// check if the user is allowed to access it
if ($this->canAccessShare($share)) {
if ($this->canAccessShare($share, $user)) {
return $share;
}
@ -243,7 +244,7 @@ class Manager {
return $share;
}
private function canAccessShare(array $share): bool {
private function canAccessShare(array $share, string $user): bool {
$validShare = isset($share['share_type']) && isset($share['user']);
if (!$validShare) {
@ -252,7 +253,7 @@ class Manager {
// If the share is a user share, check if the user is the recipient
if ((int)$share['share_type'] === IShare::TYPE_USER
&& $share['user'] === $this->uid) {
&& $share['user'] === $user) {
return true;
}
@ -266,7 +267,7 @@ class Manager {
$groupShare = $share;
}
$user = $this->userManager->get($this->uid);
$user = $this->userManager->get($user);
if ($this->groupManager->get($groupShare['user'])->inGroup($user)) {
return true;
}
@ -295,13 +296,22 @@ class Manager {
* @param int $id
* @return bool True if the share could be accepted, false otherwise
*/
public function acceptShare($id) {
$share = $this->getShare($id);
public function acceptShare(int $id, ?string $user = null) {
// If we're auto-accepting a share, we need to know the user id
// as there is no session available while processing the share
// from the remote server request.
$user = $user ?? $this->uid;
if ($user === null) {
$this->logger->error('No user specified for accepting share');
return false;
}
$share = $this->getShare($id, $user);
$result = false;
if ($share) {
\OC_Util::setupFS($this->uid);
$shareFolder = Helper::getShareFolder(null, $this->uid);
\OC_Util::setupFS($user);
$shareFolder = Helper::getShareFolder(null, $user);
$mountPoint = Files::buildNotExistingFileName($shareFolder, $share['name']);
$mountPoint = Filesystem::normalizePath($mountPoint);
$hash = md5($mountPoint);
@ -314,14 +324,14 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
$userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $this->uid]);
$userShareAccepted = $acceptShare->execute([1, $mountPoint, $hash, $id, $user]);
} else {
$parentId = (int)$share['parent'];
if ($parentId !== -1) {
// this is the sub-share
$subshare = $share;
} else {
$subshare = $this->fetchUserShare($id, $this->uid);
$subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@ -332,7 +342,7 @@ class Manager {
`mountpoint` = ?,
`mountpoint_hash` = ?
WHERE `id` = ? AND `user` = ?');
$acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $this->uid]);
$acceptShare->execute([1, $mountPoint, $hash, $subshare['id'], $user]);
$result = true;
} catch (Exception $e) {
$this->logger->emergency('Could not update share', ['exception' => $e]);
@ -346,7 +356,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
$this->uid,
$user,
$mountPoint, $hash, 1,
$share['remote_id'],
$id,
@ -358,17 +368,18 @@ class Manager {
}
}
}
if ($userShareAccepted !== false) {
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'accept');
$event = new FederatedShareAddedEvent($share['remote']);
$this->eventDispatcher->dispatchTyped($event);
$this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($this->uid)));
$this->eventDispatcher->dispatchTyped(new InvalidateMountCacheEvent($this->userManager->get($user)));
$result = true;
}
}
// Make sure the user has no notification for something that does not exist anymore.
$this->processNotification($id);
$this->processNotification($id, $user);
return $result;
}
@ -379,17 +390,23 @@ class Manager {
* @param int $id
* @return bool True if the share could be declined, false otherwise
*/
public function declineShare($id) {
$share = $this->getShare($id);
public function declineShare(int $id, ?string $user = null) {
$user = $user ?? $this->uid;
if ($user === null) {
$this->logger->error('No user specified for declining share');
return false;
}
$share = $this->getShare($id, $user);
$result = false;
if ($share && (int)$share['share_type'] === IShare::TYPE_USER) {
$removeShare = $this->connection->prepare('
DELETE FROM `*PREFIX*share_external` WHERE `id` = ? AND `user` = ?');
$removeShare->execute([$id, $this->uid]);
$removeShare->execute([$id, $user]);
$this->sendFeedbackToRemote($share['remote'], $share['share_token'], $share['remote_id'], 'decline');
$this->processNotification($id);
$this->processNotification($id, $user);
$result = true;
} elseif ($share && (int)$share['share_type'] === IShare::TYPE_GROUP) {
$parentId = (int)$share['parent'];
@ -397,7 +414,7 @@ class Manager {
// this is the sub-share
$subshare = $share;
} else {
$subshare = $this->fetchUserShare($id, $this->uid);
$subshare = $this->fetchUserShare($id, $user);
}
if ($subshare !== null) {
@ -416,7 +433,7 @@ class Manager {
$share['password'],
$share['name'],
$share['owner'],
$this->uid,
$user,
$share['mountpoint'],
$share['mountpoint_hash'],
0,
@ -429,16 +446,27 @@ class Manager {
$result = false;
}
}
$this->processNotification($id);
$this->processNotification($id, $user);
}
return $result;
}
public function processNotification(int $remoteShare): void {
public function processNotification(int $remoteShare, ?string $user = null): void {
$user = $user ?? $this->uid;
if ($user === null) {
$this->logger->error('No user specified for processing notification');
return;
}
$share = $this->fetchShare($remoteShare);
if ($share === false) {
return;
}
$filter = $this->notificationManager->createNotification();
$filter->setApp('files_sharing')
->setUser($this->uid)
->setUser($user)
->setObject('remote_share', (string)$remoteShare);
$this->notificationManager->markProcessed($filter);
}
@ -538,9 +566,10 @@ class Manager {
return rtrim(substr($path, strlen($prefix)), '/');
}
public function getMount($data) {
public function getMount($data, ?string $user = null) {
$user = $user ?? $this->uid;
$data['manager'] = $this;
$mountPoint = '/' . $this->uid . '/files' . $data['mountpoint'];
$mountPoint = '/' . $user . '/files' . $data['mountpoint'];
$data['mountpoint'] = $mountPoint;
$data['certificateManager'] = \OC::$server->getCertificateManager();
return new Mount(self::STORAGE, $mountPoint, $data, $this, $this->storageLoader);
@ -550,8 +579,8 @@ class Manager {
* @param array $data
* @return Mount
*/
protected function mountShare($data) {
$mount = $this->getMount($data);
protected function mountShare($data, ?string $user = null) {
$mount = $this->getMount($data, $user);
$this->mountManager->addMount($mount);
return $mount;
}
@ -768,6 +797,8 @@ class Manager {
* @return list<Files_SharingRemoteShare> list of open server-to-server shares
*/
private function getShares($accepted) {
// Not allowing providing a user here,
// as we only want to retrieve shares for the current user.
$user = $this->userManager->get($this->uid);
$groups = $this->groupManager->getUserGroups($user);
$userGroups = [];

View file

@ -294,6 +294,20 @@ describe('SharingService share to Node mapping', () => {
accepted: true,
}
const tempExternalFile = {
id: 65,
share_type: 0,
parent: -1,
remote: 'http://nextcloud1.local/',
remote_id: '71',
share_token: '9GpiAmTIjayclrE',
name: '/test.md',
owner: 'owner-uid',
user: 'sharee-uid',
mountpoint: '{{TemporaryMountPointName#/test.md}}',
accepted: 0,
}
beforeEach(() => { vi.resetAllMocks() })
test('File', async () => {
@ -384,6 +398,35 @@ describe('SharingService share to Node mapping', () => {
expect(file.attributes.favorite).toBe(0)
})
test('External temp file', async () => {
axios.get.mockReturnValueOnce(Promise.resolve({
data: {
ocs: {
data: [tempExternalFile],
},
},
}))
const shares = await getContents(false, true, false, false)
expect(axios.get).toHaveBeenCalledTimes(1)
expect(shares.contents).toHaveLength(1)
const file = shares.contents[0] as File
expect(file).toBeInstanceOf(File)
expect(file.fileid).toBe(65)
expect(file.source).toBe('http://nextcloud.local/remote.php/dav/files/test/test.md')
expect(file.owner).toBe('owner-uid')
expect(file.mime).toBe('text/markdown')
expect(file.mtime?.getTime()).toBe(undefined)
// not available for remote shares
expect(file.size).toBe(undefined)
expect(file.permissions).toBe(0)
expect(file.root).toBe('/files/test')
expect(file.attributes).toBeInstanceOf(Object)
expect(file.attributes.favorite).toBe(0)
})
test('Empty', async () => {
vi.spyOn(logger, 'error').mockImplementationOnce(() => {})
axios.get.mockReturnValueOnce(Promise.resolve({

View file

@ -36,6 +36,10 @@ const ocsEntryToNode = async function(ocsEntry: any): Promise<Folder | File | nu
ocsEntry.item_mtime = ocsEntry.mtime
ocsEntry.file_target = ocsEntry.file_target || ocsEntry.mountpoint
if (ocsEntry.file_target.includes('TemporaryMountPointName')) {
ocsEntry.file_target = ocsEntry.name
}
// Need to set permissions to NONE for federated shares
ocsEntry.item_permissions = Permission.NONE
ocsEntry.permissions = Permission.NONE

View file

@ -645,10 +645,10 @@ class ManagerTest extends TestCase {
'user' => 'user2',
'remoteId' => '2342'
];
$this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
$user2Shares = $manager2->getOpenShares();
$this->assertCount(2, $user2Shares);
$this->assertCount(1, $manager2->getOpenShares());
$this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
$this->assertCount(2, $manager2->getOpenShares());
$this->manager->expects($this->once())->method('tryOCMEndPoint')->with('http://localhost', 'token1', '2342', 'decline')->willReturn([]);
$this->manager->removeUserShares($this->uid);
@ -690,10 +690,10 @@ class ManagerTest extends TestCase {
'user' => 'user2',
'remoteId' => '2342'
];
$this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
$user2Shares = $manager2->getOpenShares();
$this->assertCount(2, $user2Shares);
$this->assertCount(1, $manager2->getOpenShares());
$this->assertSame(null, call_user_func_array([$manager2, 'addShare'], $shareData2));
$this->assertCount(2, $manager2->getOpenShares());
$this->manager->expects($this->never())->method('tryOCMEndPoint');
$this->manager->removeGroupShares('group1');

File diff suppressed because one or more lines are too long

View file

@ -1 +1 @@
{"version":3,"sourceRoot":"","sources":["settings.scss","../../../core/css/variables.scss","../../../core/css/functions.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GCEA;AAAA;AAAA;AAAA,GCFA;AAAA;AAAA;AAAA,GFUC,0BACC,WAKF,OACC,WAID,4BE2BC,2CFvBD,mBEuBC,kDFnBD,qBEmBC,yCFfD,0BEeC,wCFXD,oEEWC,2CFPD,oCACC,oBACA,0BACA,+BACA,mBAGD,4BACC,oBACA,kCAGD,yBACC,WAIA,wCACC,kBACA,yDACC,gBAIA,mOACC,WAKH,uCACC,aAGD,sCACC,WAED,uDACC,WAKD,gBACC,WAIF,mBACC,aACA,aACA,iBACA,uEACA,qBAEA,4BACC,kBACA,SAEA,+BACC,mBAIA,qCACC,iBAKH,kCACC,sBACA,mBACA,gBAGD,mGACC,4BACA,0BACA,WAMF,oBACC,kBACA,wCACC,0BAIF,aACC,oBACA,4CACA,kFACA,8CACA,wCACA,2CACA,8CACA,6CACA,mBACA,yCAEA,sCAEC,oDAGD,+CAEC,6DACA,oDAGD,6BACC,qCACA,WACA,YAIF,6BACC,oBACA,kCAEA,mCACC,WAIA,oCACC,kBACA,oBACA,iBACA,2BACA,WACA,mBACA,QAEA,0CACC,mBACA,uBACA,gBAKD,gIACC,kBACA,qBACA,UACA,oBACA,YAKH,qCACC,kBACA,wBACA,MACA,SAEA,yCACC,qBAIF,4CACC,eAGD,4CACC,sBACA,WACA,YACA,YAMF,qBACC,aACA,sBACA,SACA,YAEA,uBACC,aAGD,uCACC,sBACA,cACA,yBAIF,iBACC,kBACA,eACA,oCACA,qCACA,6CACA,SACA,gBACA,YAEA,8CAEC,+CACA,2CAEA,0FACC,WAIF,uCACC,0BACA,qBACA,gCACA,WACA,eAEA,wDACC,qBACA,sBACA,eAIF,sCACC,kCAGC,4DAEC,iBACA,kBAEA,kFACC,YAGD,mEACC,oDAEA,kFACC,iBAIF,qEACC,WAEA,eAEA,uEACC,eAQN,gBACC,YAIA,2BACC,kCAGD,mBACC,YAIF,sCAEC,aAGD,eACC,WAGD,YACC,qBAIA,aACC,WACA,yBACA,YAGD,WACC,WACA,yBACA,YAMD,oBACC,sBAGD,iBACC,eAKD,iCACC,aACA,eACA,sBACA,SACA,gDACC,aACA,eACA,sBACA,sDACC,oBAIF,kGACC,cACA,YACA,gBAKA,iEACC,kBACA,UAED,+EACC,oBACA,oBACA,wBACA,qBAIF,wCACC,WAGD,iDACC,qBAGD,sDACC,kBACA,qBACA,WACA,0BACA,eACA,gBACA,WAQF,oBACC,gBAGD,wBACC,yBAGD,oDACC,WACA,YACA,wCAOD,oBACC,UACA,cACA,gBACA,uBAGD,2BACC,UAKD,oCAEC,cAKD,wEAEC,aAIF,gBACC,kBACA,QACA,mBAEA,sBACC,YAGD,sBACC,iBAKF,WACC,WAEA,cACC,WACA,UACA,uBACA,4CACA,iBACA,mBAGD,cACC,4CACA,UACA,uBACA,iBACA,mBAKD,gBACC,0BACA,cACA,eACA,uBACA,gBAGD,wBACC,0BAEA,gCACC,kBAIF,sCACC,kBAGD,sDAEC,cACA,eACA,eAEA,0EACC,UACA,qBACA,uBACA,gBAIF,8BACC,eAGD,kCACC,wBACA,cAIF,2BACC,mBAMA,oBACC,mBACA,sBACA,WAGD,gCACC,0BAIA,gGACC,cAMH,SACC,gBAEA,0BACC,4CAID,YACC,mBAEA,uBACC,iBACA,2BACA,qBAMH,KACC,mBACA,mBAGD,SACC,aAGD,mBACC,mBAGD,eACC,gBAOA,+IACC,sBAEA,+KACC,aAGD,mKACC,WACA,YACA,kCACA,qBACA,kBAGD,mOACC,sCAGD,mNACC,sCAGD,mNACC,oCAMF,sBACC,aAGD,YACC,oBAGD,kBACC,0BAGD,yBACC,0BAGD,sBACC,0BAGD,oCACC,uBAIF,yCACC,uBAGD,wBACC,qBAGD,2BACC,wBAEA,gBACA,aACA,yBACA,sBAKD,WACC,kBACA,2BACA,WAGD,2DAGC,qBAIA,mCACC,qBACA,YACA,eAGD,+EAEC,YAIF,yBACC,mCACC,YACA,iBACA,cACA,iDAIF,eACC,WAGD,SACC,yBAGD,QACC,qBACA,YACA,WACA,2BAEA,gBACC,kBAIF,qBACC,sBACA,qBACA,YACA,iBAGD,kBACC,qBACA,gBAIA,aACC,sCACA,mCAGD,WACC,oCAGD,mBACC,sCACA,oBAMF,8CACC,yBACA,YAGD,wBACC,WACA,YACA,mBACA,kBACA,+DAIA,oBACC,yBACA,gBAEA,uBACC,cAGD,uBACC,kBAIF,0BACC,YACA,gCAGD,oDACC,yBAGD,wDACC,2BAGD,uBACC,cAKD,oBACC,0BAGD,oCACC,gBAIF,2BACC,aACA,eACA,mDAEA,8BACC,kBAGD,6BACC,WAIF,eACC,mBAEA,iBACC,qBACA,cAIF,SACC,UAGD,eACC,iBACA,mBACA,WAGD,UACI,+CAGJ,2BACE,GACE,YAGJ,mCACE,GACE","file":"settings.css"}
{"version":3,"sourceRoot":"","sources":["settings.scss","../../../core/css/variables.scss","../../../core/css/functions.scss"],"names":[],"mappings":"AAAA;AAAA;AAAA;AAAA;AAAA,GCEA;AAAA;AAAA;AAAA,GCFA;AAAA;AAAA;AAAA,GFUC,0BACC,WAKF,OACC,WAID,4BE2BC,2CFvBD,mBEuBC,kDFnBD,qBEmBC,yCFfD,0BEeC,wCFXD,oEEWC,2CFPD,oCACC,oBACA,0BACA,+BACA,mBAGD,4BACC,oBACA,kCAGD,yBACC,WAIA,wCACC,kBACA,yDACC,gBAIA,mOACC,WAKH,uCACC,aAGD,sCACC,WAED,uDACC,WAKD,gBACC,WAIF,mBACC,aACA,aACA,iBACA,uEACA,qBAEA,4BACC,kBACA,SAEA,+BACC,mBAIA,qCACC,iBAKH,kCACC,sBACA,mBACA,gBAGD,mGACC,4BACA,0BACA,WAMF,oBACC,kBACA,wCACC,0BAIF,aACC,oBACA,4CACA,kFACA,8CACA,wCACA,2CACA,8CACA,6CACA,mBACA,yCAEA,sCAEC,oDAGD,+CAEC,6DACA,oDAGD,6BACC,qCACA,WACA,YAIF,6BACC,oBACA,kCAEA,mCACC,WAIA,oCACC,kBACA,oBACA,iBACA,2BACA,WACA,mBACA,QAEA,0CACC,mBACA,uBACA,gBAKD,gIACC,kBACA,qBACA,UACA,oBACA,YAKH,qCACC,kBACA,wBACA,MACA,SAEA,yCACC,qBAIF,4CACC,eAGD,4CACC,sBACA,WACA,YACA,YAMF,qBACC,aACA,sBACA,SACA,YAEA,uBACC,aAGD,uCACC,sBACA,cACA,yBAIF,iBACC,kBACA,eACA,oCACA,qCACA,6CACA,SACA,gBACA,YAEA,8CAEC,+CACA,2CAEA,0FACC,WAIF,uCACC,0BACA,qBACA,gCACA,WACA,eAEA,wDACC,qBACA,sBACA,eAIF,sCACC,kCAGC,4DAEC,iBACA,kBAEA,kFACC,YAGD,mEACC,oDAEA,kFACC,iBAIF,qEACC,WAEA,eAEA,uEACC,eAQN,gBACC,YAIA,2BACC,kCAGD,mBACC,YAIF,sCAEC,aAGD,eACC,WAGD,YACC,qBAIA,aACC,WACA,yBACA,YAGD,WACC,WACA,yBACA,YAMD,oBACC,sBAGD,iBACC,eAKD,iCACC,aACA,eACA,sBACA,SACA,gDACC,aACA,eACA,sBACA,sDACC,oBAIF,kGACC,cACA,YACA,gBAKA,iEACC,kBACA,UAED,+EACC,oBACA,oBACA,wBACA,qBAIF,wCACC,WAGD,iDACC,qBAGD,sDACC,kBACA,qBACA,WACA,0BACA,eACA,gBACA,WAQF,oBACC,gBAGD,wBACC,yBAGD,oDACC,WACA,YACA,wCAOD,oBACC,UACA,cACA,gBACA,uBAGD,2BACC,UAKD,oCAEC,cAKD,wEAEC,aAIF,gBACC,kBACA,QACA,mBAEA,sBACC,YAGD,sBACC,iBAKF,WACC,WAEA,cACC,WACA,UACA,uBACA,4CACA,iBACA,mBAGD,cACC,4CACA,UACA,uBACA,iBACA,mBAKD,gBACC,0BACA,cACA,eACA,uBACA,gBAGD,wBACC,0BAEA,gCACC,kBAIF,sCACC,kBAGD,sDAEC,cACA,eACA,eAEA,0EACC,UACA,qBACA,uBACA,gBAIF,8BACC,eAGD,kCACC,wBACA,cAIF,2BACC,mBAMA,oBACC,mBACA,sBACA,WAGD,gCACC,0BAIA,gGACC,cAOH,SACC,gBAEA,0BACC,4CAID,YACC,oBACA,mBACA,uBACA,eACA,iBACA,gBACA,aAEA,uBACC,aACA,mBACA,uBACA,oCACA,qCACA,yDACA,sBACA,oCAKF,WACC,kBACA,kBACA,oCACA,gBAKF,KACC,mBACA,mBAGD,SACC,aAGD,mBACC,mBAGD,eACC,gBAOA,+IACC,sBAEA,+KACC,aAGD,mKACC,WACA,YACA,kCACA,qBACA,kBAGD,mOACC,sCAGD,mNACC,sCAGD,mNACC,oCAMF,sBACC,aAGD,YACC,oBAGD,kBACC,0BAGD,yBACC,0BAGD,sBACC,0BAGD,oCACC,uBAIF,yCACC,uBAGD,wBACC,qBAGD,2BACC,wBAEA,gBACA,aACA,yBACA,sBAKD,WACC,kBACA,2BACA,WAGD,2DAGC,qBAIA,mCACC,qBACA,YACA,eAGD,+EAEC,YAIF,yBACC,mCACC,YACA,iBACA,cACA,iDAIF,eACC,WAGD,SACC,yBAGD,QACC,qBACA,YACA,WACA,2BAEA,gBACC,kBAIF,qBACC,sBACA,qBACA,YACA,iBAGD,kBACC,qBACA,gBAIA,aACC,sCACA,mCAGD,WACC,oCAGD,mBACC,sCACA,oBAMF,8CACC,yBACA,YAGD,wBACC,WACA,YACA,mBACA,kBACA,+DAIA,oBACC,yBACA,gBAEA,uBACC,cAGD,uBACC,kBAIF,0BACC,YACA,gCAGD,oDACC,yBAGD,wDACC,2BAGD,uBACC,cAKD,oBACC,0BAGD,oCACC,gBAIF,2BACC,aACA,eACA,mDAEA,8BACC,kBAGD,6BACC,WAIF,eACC,mBAEA,iBACC,qBACA,cAIF,SACC,UAGD,eACC,iBACA,mBACA,WAGD,UACI,+CAGJ,2BACE,GACE,YAGJ,mCACE,GACE","file":"settings.css"}

View file

@ -568,6 +568,7 @@ span.usersLastLoginTooltip {
}
/* SETTINGS SECTIONS */
// to match with NcSettingsSection component
.section {
margin-bottom: 0;
/* section divider lines, none needed for last one */
@ -577,13 +578,32 @@ span.usersLastLoginTooltip {
/* correctly display help icons next to headings */
h2 {
margin-bottom: 22px;
display: inline-flex;
align-items: center;
justify-content: center;
font-size: 20px;
font-weight: bold;
max-width: 900px;
margin-top: 0;
.icon-info {
padding: 6px 20px;
vertical-align: text-bottom;
display: inline-block;
.icon-info {
display: flex;
align-items: center;
justify-content: center;
width: var(--default-clickable-area);
height: var(--default-clickable-area);
margin: calc((var(--default-clickable-area) - 16px) / 2 * -1);
margin-inline-start: 0;
color: var(--color-text-maxcontrast);
}
}
p {
margin-top: -0.2em;
margin-bottom: 1em;
color: var(--color-text-maxcontrast);
max-width: 900px;
}
}

View file

@ -7,6 +7,7 @@
use Behat\Behat\Context\Context;
use Behat\Behat\Context\SnippetAcceptingContext;
use Behat\Gherkin\Node\TableNode;
use PHPUnit\Framework\Assert;
require __DIR__ . '/../../vendor/autoload.php';
@ -168,8 +169,52 @@ class FederationContext implements Context, SnippetAcceptingContext {
self::$phpFederatedServerPid = '';
}
/**
* @BeforeScenario @TrustedFederation
*/
public function theServersAreTrustingEachOther() {
$this->asAn('admin');
// Trust the remote server on the local server
$this->usingServer('LOCAL');
$this->sendRequestForJSON('POST', '/apps/federation/trusted-servers', ['url' => 'http://localhost:' . getenv('PORT')]);
Assert::assertTrue(($this->response->getStatusCode() === 200 || $this->response->getStatusCode() === 409));
// Trust the local server on the remote server
$this->usingServer('REMOTE');
$this->sendRequestForJSON('POST', '/apps/federation/trusted-servers', ['url' => 'http://localhost:' . getenv('PORT_FED')]);
// If the server is already trusted, we expect a 409
Assert::assertTrue(($this->response->getStatusCode() === 200 || $this->response->getStatusCode() === 409));
}
/**
* @AfterScenario @TrustedFederation
*/
public function theServersAreNoLongerTrustingEachOther() {
$this->asAn('admin');
// Untrust the remote servers on the local server
$this->usingServer('LOCAL');
$this->sendRequestForJSON('GET', '/apps/federation/trusted-servers');
$this->theHTTPStatusCodeShouldBe('200');
$trustedServersIDs = array_map(fn ($server) => $server->id, json_decode($this->response->getBody())->ocs->data);
foreach ($trustedServersIDs as $id) {
$this->sendRequestForJSON('DELETE', '/apps/federation/trusted-servers/' . $id);
$this->theHTTPStatusCodeShouldBe('200');
}
// Untrust the local server on the remote server
$this->usingServer('REMOTE');
$this->sendRequestForJSON('GET', '/apps/federation/trusted-servers');
$this->theHTTPStatusCodeShouldBe('200');
$trustedServersIDs = array_map(fn ($server) => $server->id, json_decode($this->response->getBody())->ocs->data);
foreach ($trustedServersIDs as $id) {
$this->sendRequestForJSON('DELETE', '/apps/federation/trusted-servers/' . $id);
$this->theHTTPStatusCodeShouldBe('200');
}
}
protected function resetAppConfigs() {
$this->deleteServerConfig('files_sharing', 'incoming_server2server_group_share_enabled');
$this->deleteServerConfig('files_sharing', 'outgoing_server2server_group_share_enabled');
$this->deleteServerConfig('files_sharing', 'federated_trusted_share_auto_accept');
}
}

View file

@ -8,7 +8,7 @@ Feature: federated
Scenario: Federate share a file with another server
Given Using server "REMOTE"
And user "user1" exists
And Using server "LOCAL"
Given Using server "LOCAL"
And user "user0" exists
When User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
Then the OCS status code should be "100"
@ -30,6 +30,12 @@ Feature: federated
| displayname_owner | user0 |
| share_with | user1@REMOTE |
| share_with_displayname | user1 |
Given Using server "REMOTE"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
Then the list of returned shares has 1 shares
Scenario: Federated group share a file with another server
Given Using server "REMOTE"
@ -40,7 +46,7 @@ Feature: federated
And As an "admin"
And Add user "gs-user1" to the group "group1"
And Add user "gs-user2" to the group "group1"
And Using server "LOCAL"
Given Using server "LOCAL"
And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And user "gs-user0" exists
When User "gs-user0" from server "LOCAL" shares "/textfile0.txt" with group "group1" from server "REMOTE"
@ -64,11 +70,10 @@ Feature: federated
| share_with | group1@REMOTE |
| share_with_displayname | group1@REMOTE |
Scenario: Federate share a file with local server
Given Using server "LOCAL"
And user "user0" exists
And Using server "REMOTE"
Given Using server "REMOTE"
And user "user1" exists
When User "user1" from server "REMOTE" shares "/textfile0.txt" with user "user0" from server "LOCAL"
Then the OCS status code should be "100"
@ -94,10 +99,10 @@ Feature: federated
Scenario: Remote sharee can see the pending share
Given Using server "REMOTE"
And user "user1" exists
And Using server "LOCAL"
Given Using server "LOCAL"
And user "user0" exists
And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "user1"
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
Then the OCS status code should be "100"
@ -122,11 +127,11 @@ Feature: federated
And As an "admin"
And Add user "gs-user1" to the group "group1"
And Add user "gs-user2" to the group "group1"
And Using server "LOCAL"
Given Using server "LOCAL"
And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And user "gs-user0" exists
When User "gs-user0" from server "LOCAL" shares "/textfile0.txt" with group "group1" from server "REMOTE"
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "gs-user1"
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
Then the OCS status code should be "100"
@ -159,7 +164,7 @@ Feature: federated
Scenario: accept a pending remote share
Given Using server "REMOTE"
And user "user1" exists
And Using server "LOCAL"
Given Using server "LOCAL"
And user "user0" exists
And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
When User "user1" from server "REMOTE" accepts last pending share
@ -175,7 +180,7 @@ Feature: federated
And As an "admin"
And Add user "gs-user1" to the group "group1"
And Add user "gs-user2" to the group "group1"
And Using server "LOCAL"
Given Using server "LOCAL"
And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And user "gs-user0" exists
When User "gs-user0" from server "LOCAL" shares "/textfile0.txt" with group "group1" from server "REMOTE"
@ -187,45 +192,45 @@ Feature: federated
Given Using server "REMOTE"
And user "user1" exists
And user "user2" exists
And Using server "LOCAL"
Given Using server "LOCAL"
And user "user0" exists
And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
And User "user1" from server "REMOTE" accepts last pending share
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "user1"
When creating a share with
| path | /textfile0 (2).txt |
| shareType | 0 |
| shareWith | user2 |
| permissions | 19 |
#Then the OCS status code should be "100"
#And the HTTP status code should be "200"
#And Share fields of last share match with
# | id | A_NUMBER |
# | item_type | file |
# | item_source | A_NUMBER |
# | share_type | 0 |
# | file_source | A_NUMBER |
# | path | /textfile0 (2).txt |
# | permissions | 19 |
# | stime | A_NUMBER |
# | storage | A_NUMBER |
# | mail_send | 1 |
# | uid_owner | user1 |
# | file_parent | A_NUMBER |
# | displayname_owner | user1 |
# | share_with | user2 |
# | share_with_displayname | user2 |
# Then the OCS status code should be "100"
# And the HTTP status code should be "200"
# And Share fields of last share match with
# | id | A_NUMBER |
# | item_type | file |
# | item_source | A_NUMBER |
# | share_type | 0 |
# | file_source | A_NUMBER |
# | path | /textfile0 (2).txt |
# | permissions | 19 |
# | stime | A_NUMBER |
# | storage | A_NUMBER |
# | mail_send | 1 |
# | uid_owner | user1 |
# | file_parent | A_NUMBER |
# | displayname_owner | user1 |
# | share_with | user2 |
# | share_with_displayname | user2 |
Scenario: Overwrite a federated shared file as recipient
Given Using server "REMOTE"
And user "user1" exists
And user "user2" exists
And Using server "LOCAL"
Given Using server "LOCAL"
And user "user0" exists
And User "user0" from server "LOCAL" shares "/textfile0.txt" with user "user1" from server "REMOTE"
And User "user1" from server "REMOTE" accepts last pending share
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "user1"
And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA"
When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/textfile0 (2).txt"
@ -236,16 +241,16 @@ Feature: federated
Given Using server "REMOTE"
And user "user1" exists
And user "user2" exists
And Using server "LOCAL"
Given Using server "LOCAL"
And user "user0" exists
And User "user0" from server "LOCAL" shares "/PARENT" with user "user1" from server "REMOTE"
And User "user1" from server "REMOTE" accepts last pending share
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "user1"
And User "user1" modifies text of "/textfile0.txt" with text "BLABLABLA"
#When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt"
#And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8"
#Then Downloaded content should be "BLABLABLA"
When User "user1" uploads file "../../data/user1/files/textfile0.txt" to "/PARENT (2)/textfile0.txt"
And Downloading file "/PARENT (2)/textfile0.txt" with range "bytes=0-8"
Then Downloaded content should be "BLABLABLA"
Scenario: List federated share from another server not accepted yet
Given Using server "LOCAL"
@ -256,7 +261,7 @@ Feature: federated
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
Given Using server "LOCAL"
When As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
Then the list of returned shares has 0 shares
@ -270,7 +275,7 @@ Feature: federated
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
When As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
@ -296,7 +301,7 @@ Feature: federated
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And remote server is stopped
When As an "user0"
@ -318,7 +323,7 @@ Feature: federated
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
# Checking that the file exists caches the file entry, which causes an
# exception to be thrown when getting the file info if the remote server is
@ -335,8 +340,6 @@ Feature: federated
| user | user0 |
| mountpoint | /remote-share.txt |
Scenario: Delete federated share with another server
Given Using server "LOCAL"
And user "user0" exists
@ -349,13 +352,13 @@ Feature: federated
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 1 shares
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
And Using server "REMOTE"
Given Using server "REMOTE"
When As an "user1"
And Deleting last share
Then the OCS status code should be "100"
@ -363,7 +366,7 @@ Feature: federated
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 0 shares
And Using server "LOCAL"
Given Using server "LOCAL"
And as "user0" the file "/remote-share.txt" does not exist
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
@ -381,7 +384,7 @@ Feature: federated
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 1 shares
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
@ -394,7 +397,7 @@ Feature: federated
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 0 shares
@ -408,7 +411,7 @@ Feature: federated
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
@ -435,7 +438,7 @@ Feature: federated
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 1 shares
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
@ -447,7 +450,7 @@ Feature: federated
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
And Using server "REMOTE"
Given Using server "REMOTE"
And As an "user1"
And sending "GET" to "/apps/files_sharing/api/v1/shares"
And the list of returned shares has 0 shares
@ -461,7 +464,7 @@ Feature: federated
# server may have its own /textfile0.txt" file)
And User "user1" copies file "/textfile0.txt" to "/remote-share.txt"
And User "user1" from server "REMOTE" shares "/remote-share.txt" with user "user0" from server "LOCAL"
And Using server "LOCAL"
Given Using server "LOCAL"
And User "user0" from server "LOCAL" accepts last pending share
And as "user0" the file "/remote-share.txt" exists
And As an "user0"
@ -474,3 +477,115 @@ Feature: federated
And As an "user0"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
Scenario: Share to a non-trusted server will NOT auto accept
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "userfed2" exists
And parameter "federated_trusted_share_auto_accept" of app "files_sharing" is set to "yes"
When As an "user0"
When User "user0" from server "LOCAL" shares "/textfile0.txt" with user "userfed2" from server "REMOTE"
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=false"
And the list of returned shares has 1 shares
Given Using server "REMOTE"
And using new dav path
And As an "userfed2"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
And the list of returned shares has 1 shares
And as "userfed2" the file "/textfile0 (2).txt" does not exist
Scenario: Share to a non-trusted server group will NOT auto accept
Given Using server "REMOTE"
And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And parameter "federated_trusted_share_auto_accept" of app "files_sharing" is set to "yes"
And user "gs-userfed3" exists
And user "gs-userfed4" exists
And group "groupfed2" exists
And As an "admin"
And Add user "gs-userfed3" to the group "groupfed2"
And Add user "gs-userfed4" to the group "groupfed2"
Given Using server "LOCAL"
And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And user "gs-user0" exists
When As an "gs-user0"
When User "gs-user0" from server "LOCAL" shares "/textfile0.txt" with group "groupfed2" from server "REMOTE"
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=false"
And the list of returned shares has 1 shares
Given Using server "REMOTE"
And using new dav path
And As an "gs-userfed3"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
And the list of returned shares has 1 shares
And as "gs-userfed3" the file "/textfile0 (2).txt" does not exist
And As an "gs-userfed4"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 0 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
And the list of returned shares has 1 shares
And as "gs-userfed4" the file "/textfile0 (2).txt" does not exist
@TrustedFederation
Scenario: Share to a trusted server auto accept
Given Using server "LOCAL"
And user "user0" exists
Given Using server "REMOTE"
And user "userfed1" exists
And parameter "federated_trusted_share_auto_accept" of app "files_sharing" is set to "yes"
When As an "user0"
When User "user0" from server "LOCAL" shares "/textfile0.txt" with user "userfed1" from server "REMOTE"
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=false"
And the list of returned shares has 1 shares
Given Using server "REMOTE"
And using new dav path
And As an "userfed1"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
And the list of returned shares has 0 shares
And as "userfed1" the file "/textfile0 (2).txt" exists
@TrustedFederation
Scenario: Share to a trusted server group auto accept
Given Using server "REMOTE"
And parameter "incoming_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And parameter "federated_trusted_share_auto_accept" of app "files_sharing" is set to "yes"
And user "gs-userfed1" exists
And user "gs-userfed2" exists
And group "groupfed1" exists
And As an "admin"
And Add user "gs-userfed1" to the group "groupfed1"
And Add user "gs-userfed2" to the group "groupfed1"
Given Using server "LOCAL"
And parameter "outgoing_server2server_group_share_enabled" of app "files_sharing" is set to "yes"
And user "gs-user0" exists
When As an "gs-user0"
When User "gs-user0" from server "LOCAL" shares "/textfile0.txt" with group "groupfed1" from server "REMOTE"
Then the OCS status code should be "100"
And the HTTP status code should be "200"
And sending "GET" to "/apps/files_sharing/api/v1/shares?shared_with_me=false"
And the list of returned shares has 1 shares
Given Using server "REMOTE"
And using new dav path
And As an "gs-userfed1"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
And the list of returned shares has 0 shares
And as "gs-userfed1" the file "/textfile0 (2).txt" exists
And As an "gs-userfed2"
And sending "GET" to "/apps/files_sharing/api/v1/remote_shares"
And the list of returned shares has 1 shares
When sending "GET" to "/apps/files_sharing/api/v1/remote_shares/pending"
And the list of returned shares has 0 shares
And as "gs-userfed2" the file "/textfile0 (2).txt" exists

View file

@ -22,6 +22,8 @@ if [ "$INSTALLED" == "true" ]; then
$OCC config:system:set auth.bruteforce.protection.enabled --value false --type bool
# Allow local remote urls otherwise we can not share
$OCC config:system:set allow_local_remote_servers --value true --type bool
# Allow self signed certificates
$OCC config:system:set sharing.federation.allowSelfSignedCertificates --value true --type bool
else
if [ "$SCENARIO_TO_RUN" != "setup_features/setup.feature" ]; then
echo "Nextcloud instance needs to be installed" >&2
@ -38,6 +40,7 @@ if [ -z "$EXECUTOR_NUMBER" ]; then
fi
PORT=$((8080 + $EXECUTOR_NUMBER))
echo $PORT
export PORT
echo "" > "${NC_DATADIR}/nextcloud.log"
echo "" > phpserver.log

View file

@ -1880,6 +1880,15 @@ $CONFIG = [
*/
'transferIncomingShares' => false,
/**
* Federated Cloud Sharing
*/
/**
* Allow self-signed certificates for federated shares
*/
'sharing.federation.allowSelfSignedCertificates' => false,
/**
* Hashing
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long