Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 047d6289 authored by Akhil's avatar Akhil 🙂
Browse files

Resolve merge conflicts

parents 103775a3 5d14e86b
Loading
Loading
Loading
Loading
Loading
+13 −1
Original line number Diff line number Diff line
@@ -14,6 +14,18 @@

- 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

### Captcha Configuration for user account creation

- Simple image based captcha is the default for human verification
- To change the value, set `ecloud-accounts.captcha_provider` 
  - Allowed values are `image` (default) and `hcaptcha` (https://hcaptcha.com)

#### HCaptcha Configuration

- For hcaptcha provider to work, set the following values correctly:
  - `ecloud-accounts.hcaptcha_site_key`
  - `ecloud-accounts.hcaptcha_secret`

## Drop account

- The drop account functionality plugin works in conjunction with the drop_account plugin : https://apps.nextcloud.com/apps/drop_account
@@ -25,7 +37,7 @@

Please open issues here : https://gitlab.e.foundation/e/backlog/issues

## Dependancies
## Dependencies

This plugin works in cunjunction with the drop_account plugin : https://apps.nextcloud.com/apps/drop_account

+1 −1
Original line number Diff line number Diff line
@@ -10,7 +10,7 @@
    <description><![CDATA[in /e/OS cloud, nextcloud accounts are linked to mail accounts. This app ensures both are coordinated: it sets the e-mail address, quota and storage of the user upon creation.
    It also completes the account deletion by cleaning other parts of the /e/OS 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/OS cloud setup]]></description>
    <version>6.1.1</version>
    <version>6.1.2</version>
    <licence>agpl</licence>
    <author mail="dev@murena.com" homepage="https://murena.com/">Murena SAS</author>
    <namespace>EcloudAccounts</namespace>
+78 −15
Original line number Diff line number Diff line
@@ -12,6 +12,7 @@ use OCA\EcloudAccounts\Exception\AddUsernameToCommonStoreException;
use OCA\EcloudAccounts\Exception\LDAPUserCreationException;
use OCA\EcloudAccounts\Exception\RecoveryEmailValidationException;
use OCA\EcloudAccounts\Service\CaptchaService;
use OCA\EcloudAccounts\Service\HCaptchaService;
use OCA\EcloudAccounts\Service\NewsLetterService;
use OCA\EcloudAccounts\Service\UserService;
use OCP\AppFramework\Controller;
@@ -19,6 +20,7 @@ use OCP\AppFramework\Http;
use OCP\AppFramework\Http\DataResponse;
use OCP\AppFramework\Http\RedirectResponse;
use OCP\AppFramework\Http\TemplateResponse;
use OCP\AppFramework\Services\IInitialState;
use OCP\IConfig;
use OCP\ILogger;
use OCP\IRequest;
@@ -33,14 +35,20 @@ class AccountController extends Controller {
	private $userService;
	private $newsletterService;
	private $captchaService;
	private HCaptchaService $hCaptchaService;
	protected $l10nFactory;
	private $session;
	private $userSession;
	private $urlGenerator;
	/** @var IConfig */
	private IConfig $config;
	private IInitialState $initialState;
	private const SESSION_USERNAME_CHECK = 'username_check_passed';
	private const CAPTCHA_VERIFIED_CHECK = 'captcha_verified';
	private const ALLOWED_CAPTCHA_PROVIDERS = ['image', 'hcaptcha'];
	private const DEFAULT_CAPTCHA_PROVIDER = 'image';
	private const HCAPTCHA_PROVIDER = 'hcaptcha';
	private const HCAPTCHA_DOMAINS = ['https://hcaptcha.com', 'https://*.hcaptcha.com'];

	private ILogger $logger;
	public function __construct(
@@ -49,24 +57,29 @@ class AccountController extends Controller {
		UserService $userService,
		NewsLetterService $newsletterService,
		CaptchaService $captchaService,
		HCaptchaService $hCaptchaService,
		IFactory $l10nFactory,
		IUserSession $userSession,
		IURLGenerator $urlGenerator,
		ISession $session,
		IConfig $config,
		ILogger $logger
		ILogger $logger,
		IInitialState $initialState
	) {
		parent::__construct($AppName, $request);
		$this->appName = $AppName;
		$this->userService = $userService;
		$this->newsletterService = $newsletterService;
		$this->captchaService = $captchaService;
		$this->hCaptchaService = $hCaptchaService;
		$this->l10nFactory = $l10nFactory;
		$this->session = $session;
		$this->userSession = $userSession;
		$this->config = $config;
		$this->urlGenerator = $urlGenerator;
		$this->logger = $logger;
		$this->request = $request;
		$this->initialState = $initialState;
	}

	/**
@@ -83,13 +96,31 @@ class AccountController extends Controller {
		}

		$_SERVER['HTTP_ACCEPT_LANGUAGE'] = $lang;
		$this->initialState->provideInitialState('lang', $lang);
		
		return new TemplateResponse(
		$response = new TemplateResponse(
			Application::APP_ID,
			'signup',
			['appName' => Application::APP_ID, 'lang' => $lang],
			TemplateResponse::RENDER_AS_GUEST
		);

		$captchaProvider = $this->getCaptchaProvider();
		$this->initialState->provideInitialState('captchaProvider', $captchaProvider);
		
		if ($captchaProvider === self::HCAPTCHA_PROVIDER) {
			$csp = $response->getContentSecurityPolicy();
			foreach (self::HCAPTCHA_DOMAINS as $domain) {
				$csp->addAllowedScriptDomain($domain);
				$csp->addAllowedFrameDomain($domain);
				$csp->addAllowedStyleDomain($domain);
				$csp->addAllowedConnectDomain($domain);
			}
			$response->setContentSecurityPolicy($csp);
			$hcaptchaSiteKey = $this->config->getSystemValue(Application::APP_ID . '.hcaptcha_site_key');
			$this->initialState->provideInitialState('hCaptchaSiteKey', $hcaptchaSiteKey);
		}
		return $response;
	}
	
	/**
@@ -160,8 +191,8 @@ class AccountController extends Controller {
			
			$this->session->remove(self::SESSION_USERNAME_CHECK);
			$this->session->remove(self::CAPTCHA_VERIFIED_CHECK);

			$this->userService->addUsernameToCommonDataStore($username);
			$ipAddress = $this->request->getRemoteAddress();
			$this->userService->addUsernameToCommonDataStore($username, $ipAddress, $recoveryEmail);
			$response->setStatus(200);
			$response->setData(['success' => true]);

@@ -245,10 +276,15 @@ class AccountController extends Controller {
	 * @NoCSRFRequired
	 */
	public function captcha(): Http\DataDisplayResponse {
		$captchaValue = $this->captchaService->generateCaptcha();
		// Don't allow requests to image captcha if different provider is set
		if ($this->getCaptchaProvider() !== self::DEFAULT_CAPTCHA_PROVIDER) {
			$response = new DataResponse();
			$response->setStatus(400);
			return $response;
		}

		$captchaValue = $this->captchaService->generateCaptcha();
		$response = new Http\DataDisplayResponse($captchaValue, Http::STATUS_OK, ['Content-Type' => 'image/png']);

		return $response;
	}
	/**
@@ -258,24 +294,51 @@ class AccountController extends Controller {
	 * @PublicPage
	 * @NoCSRFRequired
	 *
	 * @param string $captchaInput The user-provided human verification input.
	 * @param string $token The user-provided human verification input.
	 * @param string $bypassToken Token to bypass captcha for automation testing
	 *
	 * @return \OCP\AppFramework\Http\DataResponse
	 */
	public function verifyCaptcha(string $captchaInput = '', string $bypassToken = '') : DataResponse {
	public function verifyCaptcha(string $userToken = '', string $bypassToken = '') : DataResponse {
		$response = new DataResponse();
		$captchaToken = $this->config->getSystemValue('bypass_captcha_token', '');
		// Initialize the default status to 400 (Bad Request)
		$response->setStatus(400);
		// Check if the input matches the bypass token or the stored captcha result
		$captchaResult = (string) $this->session->get(CaptchaService::CAPTCHA_RESULT_KEY, '');
		if ((!empty($captchaToken) && $bypassToken === $captchaToken) || (!empty($captchaResult) && $captchaInput === $captchaResult)) {

		// Check if the input matches the bypass token
		$bypassTokenInConfig = $this->config->getSystemValue('bypass_captcha_token', '');
		if ((!empty($bypassTokenInConfig) && $bypassToken === $bypassTokenInConfig)) {
			$this->session->set(self::CAPTCHA_VERIFIED_CHECK, true);
			$response->setStatus(200);
		}

		$response->setStatus(400);
		$captchaProvider = $this->getCaptchaProvider();

		// Check for default captcha provider
		if ($captchaProvider === self::DEFAULT_CAPTCHA_PROVIDER && $this->verifyImageCaptcha($userToken)) {
			$this->session->set(self::CAPTCHA_VERIFIED_CHECK, true);
			$this->session->remove(CaptchaService::CAPTCHA_RESULT_KEY);
			$response->setStatus(200);
		}

		// Check for hcaptcha provider
		if ($captchaProvider === self::HCAPTCHA_PROVIDER && $this->hCaptchaService->verify($userToken)) {
			$this->session->set(self::CAPTCHA_VERIFIED_CHECK, true);
			$response->setStatus(200);
		}
		return $response;
	}

	private function verifyImageCaptcha(string $captchaInput = '') : bool {
		$captchaResult = (string) $this->session->get(CaptchaService::CAPTCHA_RESULT_KEY, '');
		return (!empty($captchaResult) && $captchaInput === $captchaResult);
	}

	private function getCaptchaProvider() : string {
		$captchaProvider = $this->config->getSystemValue('ecloud-accounts.captcha_provider', self::DEFAULT_CAPTCHA_PROVIDER);

		if (!in_array($captchaProvider, self::ALLOWED_CAPTCHA_PROVIDERS)) {
			$captchaProvider = self::DEFAULT_CAPTCHA_PROVIDER;
		}
		return $captchaProvider;
	}

}
+35 −0
Original line number Diff line number Diff line
<?php

namespace OCA\EcloudAccounts\Service;

use OCA\EcloudAccounts\AppInfo\Application;
use OCP\IConfig;
use OCP\ISession;

class HCaptchaService {
	private ISession $session;
	private IConfig $config;
	private CurlService $curl;

	private const VERIFY_URL = 'https://hcaptcha.com/siteverify';

	public function __construct(ISession $session, IConfig $config, CurlService $curlService) {
		$this->session = $session;
		$this->config = $config;
		$this->curl = $curlService;
	}

	public function verify(string $token) : bool {
		$secret = $this->config->getSystemValue(Application::APP_ID . '.hcaptcha_secret');
		$data = [
			'response' => $token,
			'secret' => $secret
		];

		$data = http_build_query($data);
		$response = $this->curl->post(self::VERIFY_URL, $data);
		$response = json_decode($response, true);
		
		return $response['success'];
	}
}
+6 −2
Original line number Diff line number Diff line
@@ -420,10 +420,12 @@ class UserService {
	 * If the operation fails, an exception will be thrown.
	 *
	 * @param string $username The username to add to the common data store.
	 * @param string $ipAddress IP Address of user
	 * @param string $recoveryEmail A recovery Email of user
	 *
	 * @throws AddUsernameToCommonStoreException If an error occurs while adding the username to the common data store.
	 */
	public function addUsernameToCommonDataStore(string $username) : void {
	public function addUsernameToCommonDataStore(string $username, string $ipAddress, string $recoveryEmail) : void {
		$commonServicesURL = $this->apiConfig['commonServicesURL'];
		$commonApiVersion = $this->apiConfig['commonApiVersion'];

@@ -434,7 +436,9 @@ class UserService {
		$url = $commonServicesURL . $endpoint ;
		
		$params = [
			'username' => $username
			'username' => $username,
			'ipAddress' => $ipAddress,
			'recoveryEmail' => $recoveryEmail
		];

		$token = $this->apiConfig['commonServicesToken'];
Loading