Files
nextcloud-xwiki/lib/Controller/SettingsController.php
Raphaël Jakse 462ac0f202 Initial commit
2023-01-04 17:43:54 +01:00

437 lines
11 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace OCA\Xwiki\Controller;
use OCA\Xwiki\Instance;
use OCP\AppFramework\Controller;
use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataDisplayResponse;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\JSONResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\NotFoundResponse;
use OCP\Notification\IManager;
use OCP\IConfig;
use OCP\IL10N;
use OCP\IRequest;
use OCP\IURLGenerator;
use OCP\IUserManager;
use OCP\PreConditionNotMetException;
use OCP\Util;
use OCP\IUser;
class SettingsController extends Controller {
private $userId;
private IL10N $l10n;
private IConfig $config;
private IURLGenerator $urlGenerator;
private IManager $manager;
private IUserManager $userManager;
public function __construct(
$appName,
$UserId,
IRequest $request,
IL10N $l10n,
IURLGenerator $urlGenerator,
IConfig $config,
IManager $manager,
IUserManager $userManager
) {
parent::__construct($appName, $request);
$this->l10n = $l10n;
$this->config = $config;
$this->request = $request;
$this->urlGenerator = $urlGenerator;
$this->userId = $UserId;
$this->userManager = $userManager;
$this->manager = $manager;
}
public function getInstances(): array {
$res = [];
foreach ($this->getFromAppJSON('instances', '[]') as $instance) {
$url = $instance['url'];
if (!empty($url)) {
$res[] = Instance::fromArray($instance, $this->getUserToken($url));
}
}
return $res;
}
/**
* @NoCSRFRequired
*/
public function addToken() {
$instanceUrl = $this->request->getParam('i');
$token = $this->request->getParam('token');
$this->setUserToken($instanceUrl, $token);
return new RedirectResponse(
$this->urlGenerator->linkToRoute(
'settings.PersonalSettings.index',
['section' => 'xwiki']
)
);
}
public function requestToken() {
$instanceUrl = $this->request->getParam('i');
$instance = $this->getInstance($instanceUrl);
if ($instance !== null) {
$state = bin2hex(random_bytes(20));
$states = $this->getFromAppJSON('oidc_states', []);
$states[$state] = [
'user' => $this->userId,
'instance' => $instance->url,
'date' => $_SERVER['REQUEST_TIME']
];
$this->saveAsAppJSON('oidc_states', $states);
// FIXME check instance
return new RedirectResponse(
$instanceUrl
. '/bin/view/Nextcloud/Tokens?' . http_build_query([
'response_type' => 'code',
'client_id' => $instance->clientId,
'state' => $state,
'redirect_uri' => $this->getRedirectURL()
])
);
} // else todo
}
public function getRedirectURL() {
return (empty($_SERVER['HTTPS']) ? 'http://' : 'https://') .
$_SERVER['SERVER_NAME'] .
$this->urlGenerator->linkToRoute('xwiki.settings.oidcRedirect');
}
/**
* @NoCSRFRequired
*/
public function oidcRedirect() {
$state = $this->request->getParam('state');
$code = $this->request->getParam('code');
$states = $this->getFromAppJSON('oidc_states', []);
if (empty($states[$state])) {
return new RedirectResponse(
$this->urlGenerator->linkToRoute(
'settings.PersonalSettings.index',
['section' => 'xwiki', 'diagnostic' => 'state not found']
)
);
}
['instance' => $instanceURL, 'user' => $user] = $states[$state];
unset($states[$state]);
$this->saveAsAppJSON('oidc_states', $states);
$instance = $this->getInstance($instanceURL);
$error = $this->request->getParam('error');
if (!empty($error)) {
return new RedirectResponse(
$this->urlGenerator->linkToRoute(
'settings.PersonalSettings.index', [
'section' => 'xwiki',
'error' => $error,
// 'error_description' => $this->request->getParam('error_description'),
'i' => $instance->url
]
)
);
}
if ($user === $this->userId) {
$answer = file_get_contents(
$instanceURL . '/bin/view/Nextcloud/Tokens/Create',
false,
stream_context_create(['http' => [
'method' => 'POST',
'header' => 'Content-type: application/x-www-form-urlencoded',
'content' => http_build_query([
'grant_type' => 'authorization_code',
'code' => $code,
'redirect_uri' => $this->getRedirectURL()
])
]])
);
$t = json_decode($answer, true);
if (empty($t) || empty($t['access_token'])) {
return new RedirectResponse(
$this->urlGenerator->linkToRoute(
'settings.PersonalSettings.index', [
'section' => 'xwiki',
'error' => 'missing_access_token',
'i' => $instance->url
]
)
);
}
$this->setUserToken($instanceURL, $t['access_token']);
}
return new RedirectResponse(
$this->urlGenerator->linkToRoute(
'settings.PersonalSettings.index',
['section' => 'xwiki']
)
);
}
public function deleteToken() {
$instanceUrl = $this->request->getParam('i');
$this->setUserToken($instanceUrl, '');
return new RedirectResponse(
$this->urlGenerator->linkToRoute(
'settings.PersonalSettings.index',
['section' => 'xwiki']
)
);
}
public function setUserValue() {
$key = $this->request->getParam('k');
$value = $this->request->getParam('v');
if (empty($key)) {
return new JSONResponse([
'error' => 'Mandatory parameter:' .
' k. Use v to give the value. ' .
'If unset, the setting will be removed'
], Http::STATUS_BAD_REQUEST);
}
if ($key !== 'integratedMode') {
return new JSONResponse(
['error' => 'Unknown key ' . $key],
Http::STATUS_BAD_REQUEST
);
}
if (empty($value)) {
$this->config->deleteUserValue($this->userId, 'xwiki', $key);
}
$this->saveAsUserString($key, $value);
return new JSONResponse(['ok' => true]);
}
public function getInstance($url): Instance | null {
foreach ($this->getFromAppJSON('instances', '[]') as $instance) {
if ($instance['url'] === $url) {
return Instance::fromArray($instance, $this->getUserToken($url));
}
}
return null;
}
public function getUserToken(string $url) {
$tokens = $this->getFromUserJSON('tokens', '{}');
if (!empty($tokens[$url])) {
return $tokens[$url];
}
return '';
}
public function setUserToken(string $url, string $token) {
$tokens = $this->getFromUserJSON('tokens', '{}');
if (empty($token)) {
unset($tokens[$url]);
} else {
$tokens[$url] = $token;
}
$this->saveAsUserJSON('tokens', $tokens);
}
public function addInstance(): JSONResponse {
$url = Instance::fixURL($this->request->getParam('url'));
$clientId = trim($this->request->getParam('clientId'));
if (empty($url)) {
return new JSONResponse(
['error' => 'Mandatory parameter: url'],
Http::STATUS_BAD_REQUEST
);
}
$instances = $this->getFromAppJSON('instances', '[]');
foreach ($instances as $instance) {
if ($instance['url'] === $url) {
return new JSONResponse(
['error' => $this->l10n->t('An instance with this URL already exists.')],
Http::STATUS_BAD_REQUEST
);
}
}
$instances[] = ['url' => $url, 'clientId' => $clientId];
$this->saveAsAppJSON('instances', $instances);
$this->maybeNotify($url);
return new JSONResponse(['ok' => true, 'url' => $url]);
}
private function maybeNotify(string $url) {
if ($this->request->getParam('notifyUsers') === 'true') {
$instance = new Instance($url);
$prettyName = $instance->getPrettyName();
$notification = $this->manager->createNotification();
$notification->setApp('xwiki')
->setDateTime(new \DateTime())
->setObject('new_wiki_added', $instance->url)
->setSubject('new_wiki_added', [$prettyName]);
$this->userManager->callForAllUsers(function (IUser $user) use (&$notification) {
$notification->setUser($user->getUID());
$this->manager->notify($notification);
});
}
}
public function pingInstance(): JSONResponse {
$url = Instance::fixURL($this->request->getParam('url'));
if (empty($url)) {
return new JSONResponse(
['error' => 'Mandatory parameter: url'],
Http::STATUS_BAD_REQUEST
);
}
$restURL = $url . '/rest';
$q = file_get_contents($restURL);
if (empty($q)) {
return new JSONResponse(
['error' => $this->l10n->t('We did not get a successful reply from the instance (URL: %s)', [$restURL])],
Http::STATUS_BAD_REQUEST
);
}
$matches = null;
preg_match('#<version(?:[\s][^>]*)?>([^<]+)</version>#', $q, $matches);
$version = $matches[1];
if (empty($version)) {
return new JSONResponse(
['error' => $this->l10n->t('We did not understand the instances version')],
Http::STATUS_BAD_REQUEST
);
}
return new JSONResponse([
'ok' => true,
'version' => $version,
'url' => $url,
'hasNextcloudApplication' => Instance::hasNextcloudApplication($url)
]);
}
public function replaceInstance(): JSONResponse {
$oldUrl = $this->request->getParam('oldUrl');
$newUrl = $this->request->getParam('newUrl');
$clientId = trim($this->request->getParam('clientId'));
if (empty($oldUrl) || empty($newUrl)) {
return new JSONResponse(
['error' => 'Mandatory parameters: oldUrl and newUrl'],
Http::STATUS_BAD_REQUEST
);
}
$found = null;
$instances = $this->getFromAppJSON('instances', '[]');
foreach ($instances as &$instance) {
if ($instance['url'] === $oldUrl) {
$instance['url'] = Instance::fixURL($newUrl);
$instance['clientId'] = $clientId;
$found = $instance;
} else if ($instance['url'] === $newUrl) {
return new JSONResponse(
['error' => $this->l10n->t('An instance with this URL already exists')],
Http::STATUS_BAD_REQUEST
);
}
}
if (!$found) {
return new JSONResponse(['error' => $this->l10n->t('Could not find the instance you are trying to replace. This should not happen, please report a bug.')]);
}
$this->saveAsAppJSON('instances', $instances);
$this->maybeNotify($newUrl);
return new JSONResponse(['ok' => true, 'url' => $found['url']]);
}
private function saveAsAppJSON($key, $value): void {
$this->config->setAppValue(
'xwiki',
$key,
json_encode(
$value,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK
)
);
}
private function saveAsUserJSON(string $key, mixed $value): void {
$this->saveAsUserString(
$key,
json_encode(
$value,
JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_NUMERIC_CHECK
)
);
}
private function saveAsUserString(string $key, string $value): void {
$this->config->setUserValue($this->userId, 'xwiki', $key, $value);
}
public function getFromAppJSON($key, $defaultJSONString): mixed {
return $this->getFromJSON(
$this->config->getAppValue('xwiki', $key, $defaultJSONString),
$defaultJSONString
);
}
public function getFromUserJSON($key, $defaultJSONString): mixed {
return $this->getFromJSON(
$this->config->getUserValue($this->userId, 'xwiki', $key, $defaultJSONString),
$defaultJSONString
);
}
private function getFromJSON($configVal, $defaultJSONString): mixed {
$configVal = empty($configVal) ? '' : json_decode($configVal, true);
if (($defaultJSONString === '[]' || is_array($defaultJSONString)) && !is_array($configVal)) {
return [];
}
return $configVal;
}
public function deleteInstance(): JSONResponse {
$url = $this->request->getParam('url');
$this->saveAsAppJSON('instances', array_filter(
$this->getFromAppJSON('instances', '[]'),
function ($instance) use ($url) {
return !empty($instance['url']) && $instance['url'] !== $url;
}
));
// TODO: cleanup token from all user's config
return new JSONResponse(['ok' => true]);
}
}