Commit 15f54ca7 authored by Akhil's avatar Akhil 😶
Browse files

Merge branch 'ecloud-account-creation' into 'nc21'

Rename app to ecloud accounts and add account data  endpoint

See merge request e/infra/selfhost/nextcloud-apps/ecloud-drop-account!4
parents 3d74ded5 017d055b
# Ecloud Drop Account
Allow user to delete his account by himself
This plugin works in cunjunction with the drop_account plugin : https://apps.nextcloud.com/apps/drop_account
Triggers nextcloud's postDelete User Hook to handle proper deletion of user account in /e/ ecosystem
This plugin calls the postDelete.php script in the /e/ docker-welcome container
The e_welcome_secret is loaded in nextcloud's config file during ecloud-selfhosting installation.
# Ecloud Accounts
- This app is used to integrate ecloud account creation with [welcome](https://gitlab.e.foundation/e/infra/docker-welcome)
- Options to be configured in your `config.php`:
```php
'e_welcome_secret' => 'secret', // Secret to authenticate request to the welcome server
'e_welcome_domain' => 'welcome.ecloud.global', // Domain of welcome server
'user_folder_sharding' => false, // Whether or not user folder sharding has to be enabled
'ecloud-accounts' => [
'secret' => 'ecloud-accounts-secret', // Secret for incoming requests to authenticate against
'realdatadirectory' => '/var/www/realdatadirectory' // Directory where folders for sharding are mounted
]
```
## User Account creation
- This plugin creates an endpoint `/apps/ecloud-accounts/api/set_account_data` that is to be used to set user's email, quota,recovery email and create the user's folder if necessary
## User folder sharding
- When user folder sharding is enabled, the user's folder is created in one of the folders in the specified "real" data directory and the folder is assigned to the user randomly
- Then a `symlink` is created linking the user's folder in the nextcloud data directory to the user's folder in the "real" data directory
- If the `user_folder_sharding` config key is set to `true`, ensure to set `realdatadirectory` config key in the `ecloud-accounts` configuration to the location where your folders are mounted
- In case `user_folder_sharding` is not set in your `config.php`, it defaults to `false`
## Drop account
- The drop account functionality plugin works in conjunction with the drop_account plugin : https://apps.nextcloud.com/apps/drop_account
- The app listens for user deletion event to handle proper deletion of user account in /e/ ecosystem
- This plugin calls the postDelete.php script in the /e/ docker-welcome container
- The e_welcome_secret is loaded in nextcloud's config file during ecloud-selfhosting installation.
## Support
......
<?php
/**
* @copyright Copyright (c) 2020 Florent VINCENT <e.foundation>
*
* @author Florent VINCENT <diroots@e.email>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
use OCA\EcloudDropAccount\AppInfo\Application;
$app = \OC::$server->query(Application::class);
$app->register();
\ No newline at end of file
<?xml version="1.0"?>
<info xmlns:xsi= "http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="https://apps.nextcloud.com/schema/apps/info.xsd">
<id>ecloud_drop_account</id>
<name>Ecloud Drop Account</name>
<summary>postDelete actions dedicated to /e/ cloud setup</summary>
<description><![CDATA[in /e/ cloud self-hosting setup, nextcloud accounts are linked to mail accounts and some other things. This app completes the account deletion by cleaning other parts of the /e/ cloud setup to ensure no more data is retained when a user requests an account deletion.
This app usew the UserDeletedEvent to invoke scripts in the docker-welcome container of /e/ cloud setup]]></description>
<version>0.0.2</version>
<id>ecloud-accounts</id>
<name>Ecloud Accounts</name>
<summary>
User creation actions dedicated to ecloud setup
postDelete actions dedicated to ecloud setup
</summary>
<description><![CDATA[in /e/ cloud self-hosting setup, nextcloud accounts are linked to mail accounts and some other things. This app sets the mail, quota and storage of the user upon creation.
It also completes the account deletion by cleaning other parts of the /e/ cloud setup to ensure no more data is retained when a user requests an account deletion.
This app uses the UserDeletedEvent to invoke scripts in the docker-welcome container of /e/ cloud setup]]></description>
<version>1.0.0</version>
<licence>agpl</licence>
<author mail="diroots@e.email" homepage="https://gitlab.e.foundation/">Florent VINCENT</author>
<namespace>EcloudDropAccount</namespace>
<author mail="dev@e.email" homepage="https://gitlab.e.foundation/">Akhil Potukuchi</author>
<namespace>EcloudAccounts</namespace>
<category>tools</category>
<bugs>https://gitlab.e.foundation/e/management/issues</bugs>
<dependencies>
<nextcloud min-version="17" max-version="21"/>
<nextcloud min-version="21" max-version="21"/>
</dependencies>
</info>
<?php
return ['routes' => [
['name' => 'user#set_account_data', 'url' => '/api/set_account_data', 'verb' => 'POST'],
[
'name' => 'user#preflighted_cors', 'url' => '/api/{path}',
'verb' => 'OPTIONS', 'requirements' => array('path' => '.+')
],
]];
......@@ -24,28 +24,33 @@ declare(strict_types=1);
*
*/
namespace OCA\EcloudDropAccount\AppInfo;
namespace OCA\EcloudAccounts\AppInfo;
use OCA\EcloudDropAccount\Events\UserDeletedListener;
use OCP\AppFramework\App;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\AppFramework\Bootstrap\IBootstrap;
use OCP\AppFramework\Bootstrap\IRegistrationContext;
use OCP\AppFramework\Bootstrap\IBootContext;
use OCP\User\Events\UserDeletedEvent;
use OCA\EcloudAccounts\Events\UserDeletedListener;
class Application extends App
class Application extends App implements IBootstrap
{
const APP_NAME = 'ecloud_drop_account';
const APP_ID = 'ecloud-accounts';
public function __construct()
public function __construct(array $urlParams = [])
{
parent::__construct(self::APP_NAME);
parent::__construct(self::APP_ID, $urlParams);
}
public function register()
public function register(IRegistrationContext $context): void
{
/* @var IEventDispatcher $eventDispatcher */
$eventDispatcher = $this->getContainer()->query(IEventDispatcher::class);
$eventDispatcher->addServiceListener(UserDeletedEvent::class, UserDeletedListener::class);
$context->registerEventListener(UserDeletedEvent::class, UserDeletedListener::class);
}
public function boot(IBootContext $context): void
{
}
}
<?php
declare(strict_types=1);
namespace OCA\EcloudAccounts\Controller;
use OCP\IRequest;
use OCP\AppFramework\ApiController;
use OCP\AppFramework\Http\DataResponse;
use OCA\EcloudAccounts\Service\UserService;
class UserController extends ApiController
{
/** @var UserService */
private $userService;
public function __construct($appName, IRequest $request, UserService $userService)
{
parent::__construct($appName, $request);
$this->userService = $userService;
}
/**
* @CORS
* @PublicPage
* @NoCSRFRequired
*/
public function setAccountData(string $token, string $uid, string $email, string $recoveryEmail, string $quota = '1024 MB'): DataResponse
{
$response = new DataResponse();
if (!$this->checkAppCredentials($token)) {
$response->setStatus(401);
return $response;
}
if (!$this->userService->userExists($uid)) {
$response->setStatus(404);
return $response;
}
$user = $this->userService->getUser($uid);
if (is_null($user)) {
$response->setStatus(404);
return $response;
}
$user->setEMailAddress($email);
$user->setQuota($quota);
$recoveryEmailUpdated = $this->userService->setRecoveryEmail($uid, $recoveryEmail);
if (!$recoveryEmailUpdated) {
return $this->getErrorResponse($response, 'error_setting_recovery', 400);
}
$createdFolder = $this->userService->createUserFolder($uid);
if (!$createdFolder) {
$response->setStatus(500);
}
return $response;
}
private function getErrorResponse(DataResponse $response, string $error, int $code)
{
$response->setStatus($code);
$response->setData(['error' => $error]);
return $response;
}
private function checkAppCredentials(string $token): bool
{
$ecloud_accounts_secret = $this->userService->getConfigValue('secret');
return strcmp($token, $ecloud_accounts_secret) === 0;
}
}
<?php
declare(strict_types=1);
namespace OCA\EcloudDropAccount\Events;
namespace OCA\EcloudAccounts\Events;
use Curl;
use OCA\EcloudDropAccount\AppInfo\Application;
use OCA\EcloudAccounts\AppInfo\Application;
use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventListener;
use OCP\IConfig;
......
......@@ -85,6 +85,5 @@ class Curl
curl_close($ch);
return $response;
}
}
\ No newline at end of file
}
<?php
declare(strict_types=1);
namespace OCA\EcloudAccounts\Service;
use OCP\IUserManager;
use OCP\IUser;
use OCP\IConfig;
use UnexpectedValueException;
class UserService
{
/** @var IUserManager */
private $userManager;
/** @var array */
private $appConfig;
/** @var IConfig */
private $config;
public function __construct($appName, IUserManager $userManager, IConfig $config)
{
$this->userManager = $userManager;
$this->config = $config;
$this->appConfig = $this->config->getSystemValue($appName);
}
private function isShardingEnabled(): bool
{
$shardingEnabled = $this->config->getSystemValue('user_folder_sharding', false);
return $shardingEnabled;
}
public function getConfigValue(string $key)
{
if (!empty($this->appConfig[$key])) {
return $this->appConfig[$key];
}
return false;
}
public function userExists(string $uid): bool
{
return $this->userManager->userExists($uid);
}
public function getUser(string $uid): ?IUser
{
return $this->userManager->get($uid);
}
public function setRecoveryEmail(string $uid, string $recoveryEmail): bool
{
try {
$this->config->setUserValue($uid, 'email-recovery', 'recovery-email', $recoveryEmail);
return true;
} catch (UnexpectedValueException $e) {
return false;
}
}
public function createUserFolder(string $uid): bool
{
// return true as creation can be handled at login if sharding disabled
if (!$this->isShardingEnabled()) {
return true;
}
$realDataDir = $this->getConfigValue('realdatadirectory');
$ncDataDir = $this->config->getSystemValue('datadirectory');
$ncUserFolder = $ncDataDir . '/' . $uid;
// return false if no realDataDir specified and sharding is enabled
// As user data directory can't be created in correct location
if (!$realDataDir) {
return false;
}
// Folder already exists
if (file_exists($ncUserFolder)) {
return true;
}
// Randomly assign a directory for the new user
$directories = glob($realDataDir . '/*', GLOB_ONLYDIR);
$folderIndex = random_int(0, count($directories) - 1);
$folder = $directories[$folderIndex];
$realUserFolder = $folder . '/' . $uid;
try {
$created = mkdir($realUserFolder);
if (!$created) {
$this->logger->error('Error while creating user folder for user: ' . $uid);
return false;
}
$linked = symlink($realUserFolder, $ncUserFolder);
if (!$linked) {
$this->logger->error('Error while linking user folder for user: ' . $uid);
return false;
}
return true;
} catch (Exception $e) {
$this->logger->error("Error while creating user folder and linking for user: " . $uid);
return false;
}
}
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment