Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,6 @@ The SharePoint Backend allows administrators to add SharePoint document librarie

![screenshot](screenshots/configuration.png)

Supports SharePoint 2013, 2016 and SharePoint Online (Office 365). Nextcloud accesses SharePoint through the SharePoint REST API and uses SAML Token authentication, with a fallback to NTLM auth. Nextcloud respects file access permissions associated with its configured user credentials. Versioning and sharing are handled by Nextcloud.
Supports SharePoint 2013, 2016, 2019 (experimental) and SharePoint Online (Office 365). Nextcloud accesses SharePoint through the SharePoint REST API and uses SAML Token authentication, with a fallback to NTLM auth. Nextcloud respects file access permissions associated with its configured user credentials. Versioning and sharing are handled by Nextcloud.

Learn more about External Storage and SharePoint on [https://nextcloud.com/storage/](https://nextcloud.com/storage/)
12 changes: 2 additions & 10 deletions composer.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
{
"require": {
"vgrem/php-spo": "^2.2",
"cweagans/composer-patches": "^1.7"
"vgrem/php-spo": "2.4.*"
},
"scripts": {
"cs:check": "php-cs-fixer fix --dry-run --diff",
Expand All @@ -10,12 +9,5 @@
},
"require-dev": {
"nextcloud/coding-standard": "^0.5.0"
},
"extra": {
"patches": {
"vgrem/php-spo": {
"Lowercase the Accept header value": "https://github.com/blizzz/phpSPO/commit/6025be531c764ec0d4d0545fc0293b6f91d72726.patch"
}
}
}
}
}
81 changes: 24 additions & 57 deletions composer.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 28 additions & 43 deletions lib/Client.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,20 +24,21 @@
namespace OCA\SharePoint;

use Exception;
use Office365\Runtime\ClientObject;
use Office365\Runtime\ClientObjectCollection;
use Office365\Runtime\Http\RequestOptions;
use Office365\Runtime\Http\Response;
use Office365\SharePoint\BasePermissions;
use Office365\SharePoint\ClientContext;
use Office365\SharePoint\Field;
use Office365\SharePoint\File;
use Office365\SharePoint\FileCreationInformation;
use Office365\SharePoint\Folder;
use Office365\SharePoint\SPList;
use Psr\Log\LoggerInterface;
use function explode;
use function json_decode;
use OCA\SharePoint\Helper\RequestsWrapper;
use OCA\SharePoint\Storage\Storage;
use Office365\PHP\Client\Runtime\Auth\AuthenticationContext;
use Office365\PHP\Client\Runtime\ClientObject;
use Office365\PHP\Client\Runtime\ClientObjectCollection;
use Office365\PHP\Client\Runtime\Utilities\RequestOptions;
use Office365\PHP\Client\SharePoint\BasePermissions;
use Office365\PHP\Client\SharePoint\ClientContext;
use Office365\PHP\Client\SharePoint\File;
use Office365\PHP\Client\SharePoint\FileCreationInformation;
use Office365\PHP\Client\SharePoint\Folder;
use Office365\PHP\Client\SharePoint\SPList;

class Client {
public const DEFAULT_PROPERTIES = [
Expand All @@ -49,11 +50,8 @@ class Client {
/** @var ClientContext */
protected $context;

/** @var AuthenticationContext */
protected $authContext;
/** @var array */
protected $options;

/** @var ContextsFactory */
private $contextsFactory;

Expand All @@ -66,12 +64,11 @@ class Client {
/** @var string[] */
private $knownSP2013SystemFolders = ['Forms', 'Item', 'Attachments'];

/** @var RequestsWrapper */
private $requestsWrapper;
/** @var array */
protected $lastResponse;

public function __construct(
ContextsFactory $contextsFactory,
RequestsWrapper $requestsWrapper,
string $sharePointUrl,
array $credentials,
array $options
Expand All @@ -80,7 +77,6 @@ public function __construct(
$this->sharePointUrl = $sharePointUrl;
$this->credentials = $credentials;
$this->options = $options;
$this->requestsWrapper = $requestsWrapper;
}

/**
Expand Down Expand Up @@ -129,19 +125,11 @@ private function findSharePointErrorCode(Exception $e): ?int {
if ($trace['function'] === 'validateResponse' && isset($trace['args'][0])) {
$responseCodeJson = json_decode($trace['args'][0], true)['error'];
} else {
$lastRequest = $this->getLastRequestData();
if (!isset($lastRequest['response'])) {
if (!isset($this->lastResponse) || !isset($this->lastResponse['error'])) {
return null;
}

$responseData = $lastRequest['response']
? json_decode($lastRequest['response'], true)
: [];

if (!isset($responseData['error'])) {
return null;
}
$responseCodeJson = $responseData['error'];
$responseCodeJson = $this->lastResponse['error'];
}

return (int)explode(',', $responseCodeJson['code'])[0];
Expand Down Expand Up @@ -312,11 +300,6 @@ private function renameFile(File $file, $newPath) {
$this->context->executeQuery();
}

private function getLastRequestData(): array {
$requestHistory = $this->requestsWrapper->getHistory();
return array_pop($requestHistory);
}

/**
* deletes a provided File or Folder
*
Expand Down Expand Up @@ -382,7 +365,7 @@ public function fetchFolderContents(Folder $folder) {
public function isHidden(ClientObject $file) {
// ClientObject itself does not have getListItemAllFields but is
// the common denominator of File and Folder
if (!$file instanceof File && !$file instanceof Folder) {
if (!$file instanceof File && !$file instanceof Folder && !$file instanceof Field) {
throw new \InvalidArgumentException('File or Folder expected');
}
if ($file instanceof File) {
Expand Down Expand Up @@ -459,6 +442,10 @@ public function getDocumentLibrary(string $documentLibrary): SPList {
*/
public function loadAndExecute(ClientObject $object, array $properties = null) {
$this->context->load($object, $properties);
$r = $this->context->getPendingRequest();
$r->afterExecuteRequest(function (Response $response) {
$this->lastResponse = $response->getContent();
});
$this->context->executeQuery();
}

Expand All @@ -483,23 +470,21 @@ private function ensureConnection() {
if ($this->options['forceNtlm'] ?? false) {
throw new Exception('enforced NTLM auth');
}
$this->authContext = $this->contextsFactory->getTokenAuthContext($this->sharePointUrl);
$this->authContext->acquireTokenForUser($this->credentials['user'], $this->credentials['password']);
$this->context = $this->contextsFactory->getClientContext($this->sharePointUrl, $this->credentials['user'], $this->credentials['password']);
} catch (Exception $e) {
\OC::$server->getLogger()->logException($e,
/** @var LoggerInterface $logger */
$logger = \OC::$server->get(LoggerInterface::class); // FIXME: DI
$logger->debug(
'Failed to acquire token for user, fall back to NTLM auth',
[
'message' => 'Failed to acquire token for user, fall back to NTLM auth',
'app' => 'sharepoint',
'level' => ILogger::DEBUG
'exception' => $e,
]
);
// fall back to NTLM
$this->authContext = $this->contextsFactory->getCredentialsAuthContext($this->credentials['user'], $this->credentials['password']);
$this->authContext->AuthType = CURLAUTH_NTLM;
$this->context = $this->contextsFactory->getClientContext($this->sharePointUrl, $this->credentials['user'], $this->credentials['password'], true);
// Auth is not triggered yet with NTLM. This will happen when
// something is requested from SharePoint (on demand)
}

$this->context = $this->contextsFactory->getClientContext($this->sharePointUrl, $this->authContext);
}
}
10 changes: 0 additions & 10 deletions lib/ClientFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,7 @@

namespace OCA\SharePoint;

use OCA\SharePoint\Helper\RequestsWrapper;

class ClientFactory {
/** @var RequestsWrapper */
private $requestsWrapper;

public function __construct(RequestsWrapper $requestsWrapper) {
$this->requestsWrapper = $requestsWrapper;
}

public function getClient(
ContextsFactory $contextsFactory,
string $sharePointUrl,
Expand All @@ -41,7 +32,6 @@ public function getClient(
): Client {
return new Client(
$contextsFactory,
$this->requestsWrapper,
$sharePointUrl,
$credentials,
$options
Expand Down
Loading