fix(settings): generate user config per userId & handle guest users

Signed-off-by: codewithvk <vivek.javiya@collabora.com>
This commit is contained in:
codewithvk 2025-01-29 15:00:01 +05:30 committed by Julius Knorr
parent 5defde2c0b
commit 969a4f90a8
5 changed files with 66 additions and 23 deletions

View file

@ -38,7 +38,7 @@ return [
['name' => 'settings#uploadFontFile', 'url' => 'settings/fonts', 'verb' => 'POST'],
[
'name' => 'settings#getSettingsFile',
'url' => 'settings/{type}/{category}/{name}',
'url' => 'settings/{type}/{token}/{category}/{name}',
'verb' => 'GET',
'requirements' => [
'type' => '[a-zA-Z0-9_\-]+',

View file

@ -7,6 +7,7 @@ namespace OCA\Richdocuments\Controller;
use OCA\Richdocuments\AppConfig;
use OCA\Richdocuments\Capabilities;
use OCA\Richdocuments\Db\WopiMapper;
use OCA\Richdocuments\Service\CapabilitiesService;
use OCA\Richdocuments\Service\ConnectivityService;
use OCA\Richdocuments\Service\DemoService;
@ -58,6 +59,7 @@ class SettingsController extends Controller {
private SettingsService $settingsService,
private LoggerInterface $logger,
private IURLGenerator $urlGenerator,
private WopiMapper $wopiMapper,
private ?string $userId,
) {
parent::__construct($appName, $request);
@ -484,8 +486,13 @@ class SettingsController extends Controller {
* @PublicPage
* @NoCSRFRequired
**/
public function getSettingsFile(string $type, string $category, string $name) {
public function getSettingsFile(string $type, string $token, string $category, string $name) {
try {
$wopi = $this->wopiMapper->getWopiForToken($token);
if ($type === 'userconfig') {
$userId = $wopi->getEditorUid() ?: $wopi->getOwnerUid();
$type = $type . '/' . $userId;
}
$systemFile = $this->settingsService->getSettingsFile($type, $category, $name);
return new DataDisplayResponse(
$systemFile->getContent(),

View file

@ -139,7 +139,9 @@ class WopiController extends Controller {
$userId = !$isPublic ? $wopi->getEditorUid() : $guestUserId;
$userSettings = $this->generateSettings($userId, 'userconfig');
if (!$isPublic) {
$userSettings = $this->generateSettings($userId, 'userconfig');
}
$sharedSettings = $this->generateSettings($userId, 'systemconfig');
$response = [
@ -174,10 +176,13 @@ class WopiController extends Controller {
'EnableRemoteAIContent' => $isTaskProcessingEnabled,
'HasContentRange' => true,
'ServerPrivateInfo' => [],
'UserSettings' => $userSettings,
'SharedSettings' => $sharedSettings,
];
if (!$isPublic) {
$response['UserSettings'] = $userSettings;
}
$enableZotero = $this->config->getAppValue(Application::APPNAME, 'zoteroEnabled', 'yes') === 'yes';
if (!$isPublic && $enableZotero) {
$zoteroAPIKey = $this->config->getUserValue($wopi->getEditorUid(), 'richdocuments', 'zoteroAPIKey', '');
@ -406,13 +411,12 @@ class WopiController extends Controller {
if ($wopi->getTokenType() !== Wopi::TOKEN_TYPE_SETTING_AUTH) {
return new JSONResponse(['error' => 'Invalid token type'], Http::STATUS_BAD_REQUEST);
}
$isPublic = empty($wopi->getEditorUid());
$guestUserId = 'Guest-' . \OC::$server->getSecureRandom()->generate(8);
$userId = !$isPublic ? $wopi->getEditorUid() : $guestUserId;
$user = $this->userManager->get($wopi->getOwnerUid());
if (!$user || !$this->groupManager->isAdmin($user->getUID())) {
return new JSONResponse(['error' => 'Access denied'], Http::STATUS_BAD_REQUEST);
}
$userConfig = $this->settingsService->generateSettingsConfig($type);
$userConfig = $this->settingsService->generateSettingsConfig($type, $userId);
return new JSONResponse($userConfig, Http::STATUS_OK);
} catch (UnknownTokenException|ExpiredTokenException $e) {
$this->logger->debug($e->getMessage(), ['exception' => $e]);
@ -431,6 +435,7 @@ class WopiController extends Controller {
try {
$wopi = $this->wopiMapper->getWopiForToken($access_token);
$userId = $wopi->getEditorUid();
// TODO: auth - for admin??
$content = fopen('php://input', 'rb');
if (!$content) {
@ -442,7 +447,7 @@ class WopiController extends Controller {
// Use the fileId as a file path URL (e.g., "/settings/systemconfig/wordbook/en_US%20(1).dic")
$settingsUrl = new SettingsUrl($fileId);
$result = $this->settingsService->uploadFile($settingsUrl, $fileContent);
$result = $this->settingsService->uploadFile($settingsUrl, $fileContent, $userId);
return new JSONResponse([
'status' => 'success',
@ -475,8 +480,9 @@ class WopiController extends Controller {
$type = $settingsUrl->getType();
$category = $settingsUrl->getCategory();
$fileName = $settingsUrl->getFileName();
$userId = $wopi->getEditorUid();
$this->settingsService->deleteSettingsFile($type, $category, $fileName);
$this->settingsService->deleteSettingsFile($type, $category, $fileName, $userId);
return new JSONResponse([
'status' => 'success',

View file

@ -72,6 +72,7 @@ class WopiMapper extends QBMapper {
$wopi = Wopi::fromParams([
'fileid' => $fileId,
'ownerUid' => $userId,
'editorUid' => $userId,
'version' => $version,
'canwrite' => true,
'serverHost' => $serverHost,

View file

@ -58,7 +58,7 @@ class SettingsService {
* @return ISimpleFolder
*/
public function ensureDirectory(SettingsUrl $settingsUrl): ISimpleFolder {
public function ensureDirectory(SettingsUrl $settingsUrl, string $userId): ISimpleFolder {
$type = $settingsUrl->getType();
$category = $settingsUrl->getCategory();
@ -68,6 +68,14 @@ class SettingsService {
$baseFolder = $this->appData->newFolder($type);
}
if ($type === 'userconfig') {
try {
$baseFolder = $baseFolder->getFolder($userId);
} catch (NotFoundException $e) {
$baseFolder = $baseFolder->newFolder($userId);
}
}
try {
$categoryFolder = $baseFolder->getFolder($category);
} catch (NotFoundException $e) {
@ -86,11 +94,11 @@ class SettingsService {
* @return array ['stamp' => string, 'uri' => string]
*/
public function uploadFile(SettingsUrl $settingsUrl, string $fileData): array {
$categoryFolder = $this->ensureDirectory($settingsUrl);
public function uploadFile(SettingsUrl $settingsUrl, string $fileData, string $userId): array {
$categoryFolder = $this->ensureDirectory($settingsUrl, $userId);
$fileName = $settingsUrl->getFileName();
$newFile = $categoryFolder->newFile($fileName, $fileData);
$fileUri = $this->generateFileUri($settingsUrl->getType(), $settingsUrl->getCategory(), $fileName);
$fileUri = $this->generateFileUri($settingsUrl->getType(), $settingsUrl->getCategory(), $fileName, $userId);
return [
'stamp' => $newFile->getETag(),
@ -105,7 +113,7 @@ class SettingsService {
* @param string $category
* @return array Each item has 'stamp' and 'uri'.
*/
public function getCategoryFileList(string $type, string $category): array {
public function getCategoryFileList(string $type, string $category, string $userId): array {
try {
$categoryFolder = $this->appData->getFolder($type . '/' . $category);
} catch (NotFoundException $e) {
@ -114,10 +122,10 @@ class SettingsService {
$files = $categoryFolder->getDirectoryListing();
return array_map(function (ISimpleFile $file) use ($type, $category) {
return array_map(function (ISimpleFile $file) use ($type, $category, $userId) {
return [
'stamp' => $file->getETag(),
'uri' => $this->generateFileUri($type, $category, $file->getName()),
'uri' => $this->generateFileUri($type, $category, $file->getName(), $userId),
];
}, $files);
}
@ -155,17 +163,21 @@ class SettingsService {
* @param string $type
* @return array
*/
public function generateSettingsConfig(string $type): array {
public function generateSettingsConfig(string $type, string $userId): array {
$kind = $type === 'userconfig' ? 'user' : 'shared';
$config = [
'kind' => $kind,
];
if ($type === 'userconfig') {
$type = $type . '/' . $userId;
}
$categories = $this->getAllCategories($type);
foreach ($categories as $category) {
$files = $this->getCategoryFileList($type, $category);
$files = $this->getCategoryFileList($type, $category, $userId);
$config[$category] = $files;
}
@ -220,11 +232,20 @@ class SettingsService {
* @param string $fileName
* @return string
*/
private function generateFileUri(string $type, string $category, string $fileName): string {
private function generateFileUri(string $type, string $category, string $fileName, string $userId): string {
// Passing userId is dangerous so we have to trim from url...
if (strpos($type, '/') !== false) {
$type = explode('/', $type)[0];
}
$token = $this->generateIframeToken($type, $userId);
return $this->urlGenerator->linkToRouteAbsolute(
'richdocuments.settings.getSettingsFile',
[
'type' => $type,
'token' => $token['token'],
'category' => $category,
'name' => $fileName,
]
@ -266,12 +287,20 @@ class SettingsService {
* @param string $category
* @param string $name
*/
public function deleteSettingsFile(string $type, string $category, string $name): void {
public function deleteSettingsFile(string $type, string $category, string $name, string $userId): void {
try {
$baseFolder = $this->appData->getFolder($type);
} catch (NotFoundException $e) {
throw new NotFoundException("Type folder '{$type}' not found.");
}
if ($type === 'userconfig') {
try {
$baseFolder = $baseFolder->getFolder($userId);
} catch (NotFoundException $e) {
throw new NotFoundException("User folder '{$userId}' not found.");
}
}
try {
$categoryFolder = $baseFolder->getFolder($category);