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

Commit bb0e2e94 authored by Ronak Patel's avatar Ronak Patel
Browse files

Merge branch 'main' into 'dev/validation-exception'

# Conflicts:
#   appinfo/info.xml
parents 3aa370fe f5b5103f
Loading
Loading
Loading
Loading
Loading
+2 −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.0.4</version>
    <version>6.1.0</version>
    <licence>agpl</licence>
    <author mail="dev@murena.com" homepage="https://murena.com/">Murena SAS</author>
    <namespace>EcloudAccounts</namespace>
@@ -25,6 +25,7 @@
		<personal-section>OCA\EcloudAccounts\Settings\BetaSection</personal-section>
	</settings>
    <commands>
        <command>OCA\EcloudAccounts\Command\UpdateBlacklistedDomains</command>
        <command>OCA\EcloudAccounts\Command\Migrate2FASecrets</command>
        <command>OCA\EcloudAccounts\Command\MigrateWebmailAddressbooks</command>
        <command>OCA\EcloudAccounts\Command\MapActiveAttributetoLDAP</command>
+0 −35
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace OCA\EcloudAccounts\BackgroundJob;

use OCA\EcloudAccounts\Service\UserService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
use Psr\Log\LoggerInterface;

class BlacklistedDomainsJob extends TimedJob {
	private LoggerInterface $logger;
	private UserService $userService;
	private ITimeFactory $timeFactory;
	public const INTERVAL_PERIOD = 7 * 24 * 60 * 60;// Run for 7 days
	public function __construct(LoggerInterface $logger, ITimeFactory $timeFactory, UserService $userService) {
		parent::__construct($timeFactory);

		$this->setInterval(self::INTERVAL_PERIOD);
		$this->setTimeSensitivity(self::TIME_INSENSITIVE);
		$this->timeFactory = $timeFactory;
		$this->userService = $userService;
		$this->logger = $logger;
	}

	protected function run($argument): void {
		try {
			$this->userService->updateBlacklistedDomains();
		} catch (\Exception $e) {
			$this->logger->logException('Error updating blacklisted domains for account creation', ['exception' => $e]);
			return;
		}
	}
}
+15 −6
Original line number Diff line number Diff line
@@ -5,17 +5,21 @@ declare(strict_types=1);
namespace OCA\EcloudAccounts\Command;

use OCA\EcloudAccounts\AppInfo\Application;
use OCA\EcloudAccounts\Service\UserService;
use OCA\EcloudAccounts\Service\BlackListService;
use OCP\ILogger;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

class UpdateBlacklistedDomains extends Command {
	private UserService $userService;
	private BlackListService $blackListService;
	private ILogger $logger;

	public function __construct(UserService $userService) {

	public function __construct(BlackListService $blackListService, ILogger $logger) {
		parent::__construct();
		$this->userService = $userService;
		$this->blackListService = $blackListService;
		$this->logger = $logger;
	}

	protected function configure() {
@@ -23,8 +27,13 @@ class UpdateBlacklistedDomains extends Command {
	}

	protected function execute(InputInterface $input, OutputInterface $output): int {
		$this->userService->updateBlacklistedDomains();
		try {
			$this->blackListService->updateBlacklistedDomains();
			$output->writeln('Updated blacklisted domains for creation.');
		} catch (\Throwable $th) {
			$this->logger->error('Error while updating blacklisted domains. ' . $th->getMessage());
			$output->writeln('Error while updating blacklisted domains. '. $th->getMessage());
		}
		return 1;
	}
}
+118 −0
Original line number Diff line number Diff line
<?php

declare(strict_types=1);

namespace OCA\EcloudAccounts\Service;

use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFile;
use OCP\ILogger;

class BlackListService {
	private IAppData $appData;
	private ILogger $logger;
	private const BLACKLISTED_DOMAINS_FILE_NAME = 'blacklisted_domains.json';
	private const BLACKLISTED_DOMAINS_URL = 'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.json';

	public function __construct(ILogger $logger, IAppData $appData) {
		$this->appData = $appData;
		$this->logger = $logger;
	}

	/**
	 * Check if an email domain is blacklisted against a JSON list of disposable email domains.
	 *
	 * @param string $email The email address to check.
	 * @return bool True if the email domain is blacklisted, false otherwise.
	 */
	public function isBlacklistedEmail(string $email): bool {
		if (!$this->ensureDocumentsFolder()) {
			return false;
		}
		$blacklistedDomains = $this->getBlacklistedDomainData();
		if (empty($blacklistedDomains)) {
			return false;
		}
		$emailParts = explode('@', $email);
		$emailDomain = strtolower(end($emailParts));
		return in_array($emailDomain, $blacklistedDomains);
	}
	/**
	 * Update the blacklisted domains data by fetching it from a URL and saving it locally.
	 *
	 * @return void
	 */
	public function updateBlacklistedDomains(): void {
		$blacklisted_domain_url = self::BLACKLISTED_DOMAINS_URL;
		$json_data = file_get_contents($blacklisted_domain_url);
		$this->setBlacklistedDomainsData($json_data);
	}
	/**
	 * Store blacklisted domain data in a file within AppData.
	 *
	 * @param string $data The data to be stored in the file.
	 * @return void
	 */
	private function setBlacklistedDomainsData(string $data): void {
		$file = $this->getBlacklistedDomainsFile();
		$file->putContent($data);
	}
	/**
	 * Retrieve the blacklisted domain file path
	 *
	 * @return ISimpleFile
	 */
	private function getBlacklistedDomainsFile(): ISimpleFile {
		try {
			$currentFolder = $this->appData->getFolder('/');
		} catch (NotFoundException $e) {
			$currentFolder = $this->appData->newFolder('/');
		}
		$filename = self::BLACKLISTED_DOMAINS_FILE_NAME;
		if ($currentFolder->fileExists($filename)) {
			return $currentFolder->getFile($filename);
		}
		return $currentFolder->newFile($filename);
	}
	/**
	 * Retrieve the blacklisted domain data.
	 *
	 * @return array The array of blacklisted domains.
	 */
	public function getBlacklistedDomainData(): array {
		$document = self::BLACKLISTED_DOMAINS_FILE_NAME;
		$file = $this->getBlacklistedDomainsFile();
		try {
			$blacklistedDomainsInJson = $file->getContent();
			if (empty($blacklistedDomainsInJson)) {
				return [];
			}
			return json_decode($blacklistedDomainsInJson, true, 512, JSON_THROW_ON_ERROR);
		} catch (NotFoundException $e) {
			$this->logger->warning('Blacklisted domains file ' . $document . ' not found!');
			return [];
		} catch (\Throwable $e) {
			$this->logger->warning('Error decoding blacklisted domains file ' . $document . ': ' . $e->getMessage());
			return [];
		}
	}
	
	/**
	 * Ensure the specified folder exists within AppData.
	 *
	 * @return bool
	 */
	private function ensureDocumentsFolder(): bool {
		try {
			$this->appData->getFolder('/');
		} catch (NotFoundException $e) {
			$this->logger->error(Application::APP_ID . ' AppData folder not found!');
			return false;
		} catch (\RuntimeException $e) {
			$this->logger->error(Application::APP_ID . ' AppData folder not found! Runtime Error: '.$e->getMessage());
			return false;
		}
		return true;
	}
}
+4 −35
Original line number Diff line number Diff line
@@ -42,8 +42,8 @@ class UserService {
	private $apiConfig;
	/** @var LDAPConnectionService */
	private $LDAPConnectionService;

	public function __construct($appName, IUserManager $userManager, IConfig $config, CurlService $curlService, ILogger $logger, Defaults $defaults, IFactory $l10nFactory, LDAPConnectionService $LDAPConnectionService) {
	private BlackListService $blackListService;
	public function __construct($appName, IUserManager $userManager, IConfig $config, CurlService $curlService, ILogger $logger, Defaults $defaults, IFactory $l10nFactory, LDAPConnectionService $LDAPConnectionService, BlackListService $blackListService) {
		$this->userManager = $userManager;
		$this->config = $config;
		$this->appConfig = $this->config->getSystemValue($appName);
@@ -52,6 +52,7 @@ class UserService {
		$this->defaults = $defaults;
		$this->l10nFactory = $l10nFactory;
		$this->LDAPConnectionService = $LDAPConnectionService;
		$this->blackListService = $blackListService;
		$commonServiceURL = $this->config->getSystemValue('common_services_url', '');

		if (!empty($commonServiceURL)) {
@@ -275,37 +276,10 @@ class UserService {
		if ($this->isRecoveryEmailDomainDisallowed($recoveryEmail)) {
			throw new RecoveryEmailValidationException('You cannot set an email address with a Murena domain as recovery email address.');
		}
		if ($this->isBlacklistedEmail($recoveryEmail)) {
		if ($this->blackListService->isBlacklistedEmail($recoveryEmail)) {
			throw new BlacklistedEmailException('The domain of this email address is blacklisted. Please provide another recovery address.');
		}
	}
	/**
	 * Check if an email domain is blacklisted against a JSON list of disposable email domains.
	 *
	 * @param string $email The email address to check.
	 * @return bool True if the email domain is blacklisted, false otherwise.
	 */
	public function isBlacklistedEmail(string $email): bool {
		// Get the blacklisted domains from configuration
		$blacklistedDomainsInJson = $this->config->getAppValue(Application::APP_ID, 'blacklisted_domains');
		if (empty($blacklistedDomainsInJson)) {
			return false;
		}
		$blacklistedDomains = json_decode($blacklistedDomainsInJson, true);

		if (empty($blacklistedDomains)) {
			return false;
		}

		// Split the email address into parts using explode
		$emailParts = explode('@', $email);
		
		// Extract the domain part
		$emailDomain = strtolower(end($emailParts));
		
		// Check if the email domain is in the blacklisted domains array
		return in_array($emailDomain, $blacklistedDomains);
	}
	/**
	 * Add a new user to the LDAP directory.
	 *
@@ -586,9 +560,4 @@ class UserService {
	private function getDefaultQuota() {
		return $this->config->getSystemValueInt('default_quota_in_megabytes', 1024);
	}
	public function updateBlacklistedDomains() {
		$blacklisted_domain_url = 'https://raw.githubusercontent.com/disposable/disposable-email-domains/master/domains.json';
		$json_data = file_get_contents($blacklisted_domain_url);
		$this->config->setAppValue(Application::APP_ID, 'blacklisted_domains', $json_data);
	}
}