mirror of
https://github.com/espocrm/espocrm.git
synced 2026-06-28 06:56:05 +00:00
sms
This commit is contained in:
80
application/Espo/Controllers/TwoFactorSms.php
Normal file
80
application/Espo/Controllers/TwoFactorSms.php
Normal file
@@ -0,0 +1,80 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Controllers;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\BadRequest;
|
||||
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
use Espo\Services\TwoFactorSms as Service;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
class TwoFactorSms
|
||||
{
|
||||
private $service;
|
||||
|
||||
public function __construct(Service $service, User $user)
|
||||
{
|
||||
$this->service = $service;
|
||||
$this->user = $user;
|
||||
|
||||
if (
|
||||
!$this->user->isAdmin() &&
|
||||
!$this->user->isRegular()
|
||||
) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
}
|
||||
public function postActionSendCode(Request $request): bool
|
||||
{
|
||||
$data = $request->getParsedBody();
|
||||
|
||||
$id = $data->id ?? null;
|
||||
$phoneNumber = $data->phoneNumber ?? null;
|
||||
|
||||
if (!$id) {
|
||||
throw new BadRequest("No 'id'.");
|
||||
}
|
||||
|
||||
if (!$phoneNumber) {
|
||||
throw new BadRequest("No 'phoneNumber'.");
|
||||
}
|
||||
|
||||
if (!$this->user->isAdmin() && $id !== $this->user->getId()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$this->service->sendCode($id, $phoneNumber);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
104
application/Espo/Core/Authentication/TwoFactor/Sms/SmsLogin.php
Normal file
104
application/Espo/Core/Authentication/TwoFactor/Sms/SmsLogin.php
Normal file
@@ -0,0 +1,104 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Authentication\TwoFactor\Sms;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\UserData;
|
||||
|
||||
use Espo\Repositories\UserData as UserDataRepository;
|
||||
|
||||
use Espo\Core\Authentication\TwoFactor\Login;
|
||||
use Espo\Core\Authentication\Result;
|
||||
use Espo\Core\Authentication\ResultData;
|
||||
use Espo\Core\Authentication\FailReason;
|
||||
use Espo\Core\Api\Request;
|
||||
|
||||
class SmsLogin implements Login
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
private $util;
|
||||
|
||||
public function __construct(EntityManager $entityManager, Util $util)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
$this->util = $util;
|
||||
}
|
||||
|
||||
public function login(Result $result, Request $request): Result
|
||||
{
|
||||
$code = $request->getHeader('Espo-Authorization-Code');
|
||||
|
||||
if (!$code) {
|
||||
$this->util->sendCode($result->getLoggedUser());
|
||||
|
||||
return Result::secondStepRequired($result->getUser(), $this->getLoginData());
|
||||
}
|
||||
|
||||
$loggedUser = $result->getLoggedUser();
|
||||
|
||||
if ($this->verifyCode($loggedUser, $code)) {
|
||||
return $result;
|
||||
}
|
||||
|
||||
return Result::fail(FailReason::CODE_NOT_VERIFIED);
|
||||
}
|
||||
|
||||
private function getLoginData(): ResultData
|
||||
{
|
||||
return ResultData::createWithMessage('enterCodeSentBySms');
|
||||
}
|
||||
|
||||
private function verifyCode(User $user, string $code): bool
|
||||
{
|
||||
$userData = $this->getUserDataRepository()->getByUserId($user->getId());
|
||||
|
||||
if (!$userData) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$userData->get('auth2FA')) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($userData->get('auth2FAMethod') !== 'Sms') {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->util->verifyCode($user, $code);
|
||||
}
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Authentication\TwoFactor\Sms;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
use Espo\Core\Authentication\TwoFactor\UserSetup;
|
||||
use Espo\Core\Exceptions\Error;
|
||||
|
||||
use stdClass;
|
||||
|
||||
class SmsUserSetup implements UserSetup
|
||||
{
|
||||
private $util;
|
||||
|
||||
public function __construct(Util $util)
|
||||
{
|
||||
$this->util = $util;
|
||||
}
|
||||
|
||||
public function getData(User $user): stdClass
|
||||
{
|
||||
return (object) [
|
||||
'phoneNumberList' => $user->getPhoneNumberGroup()->getNumberList(),
|
||||
];
|
||||
}
|
||||
|
||||
public function verifyData(User $user, stdClass $payloadData): bool
|
||||
{
|
||||
$code = $payloadData->code ?? null;
|
||||
|
||||
if ($code === null) {
|
||||
throw new Error("No code.");
|
||||
}
|
||||
|
||||
$codeModified = str_replace(' ', '', trim($code));
|
||||
|
||||
if (!$codeModified) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->util->verifyCode($user, $codeModified);
|
||||
}
|
||||
}
|
||||
329
application/Espo/Core/Authentication/TwoFactor/Sms/Util.php
Normal file
329
application/Espo/Core/Authentication/TwoFactor/Sms/Util.php
Normal file
@@ -0,0 +1,329 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Authentication\TwoFactor\Sms;
|
||||
|
||||
use Espo\Core\Exceptions\Error;
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Sms\SmsSender;
|
||||
use Espo\Core\Sms\SmsFactory;
|
||||
use Espo\Core\Utils\Language;
|
||||
use Espo\Core\Field\DateTime;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\ORM\Query\Part\Condition as Cond;
|
||||
|
||||
use Espo\Entities\User;
|
||||
use Espo\Entities\Sms;
|
||||
use Espo\Entities\TwoFactorCode;
|
||||
use Espo\Entities\UserData;
|
||||
|
||||
use Espo\Repositories\UserData as UserDataRepository;
|
||||
|
||||
use const STR_PAD_LEFT;
|
||||
|
||||
class Util
|
||||
{
|
||||
/**
|
||||
* A lifetime of a code.
|
||||
*/
|
||||
private const CODE_LIFETIME_PERIOD = '10 minutes';
|
||||
|
||||
/*
|
||||
* A max number of attempts to try a single code.
|
||||
*/
|
||||
private const CODE_ATTEMPTS_COUNT = 5;
|
||||
|
||||
/**
|
||||
* A length of a code.
|
||||
*/
|
||||
private const CODE_LENGTH = 6;
|
||||
|
||||
/**
|
||||
* A max number of codes tried by a user in a period defined by `CODE_LIMIT_PERIOD`.
|
||||
*/
|
||||
private const CODE_LIMIT = 5;
|
||||
|
||||
/**
|
||||
* A period for limiting trying to too many codes.
|
||||
*/
|
||||
private const CODE_LIMIT_PERIOD = '20 minutes';
|
||||
|
||||
private $entityManager;
|
||||
|
||||
private $config;
|
||||
|
||||
private $smsSender;
|
||||
|
||||
private $language;
|
||||
|
||||
private $smsFactory;
|
||||
|
||||
public function __construct(
|
||||
EntityManager $entityManager,
|
||||
Config $config,
|
||||
SmsSender $smsSender,
|
||||
Language $language,
|
||||
SmsFactory $smsFactory
|
||||
) {
|
||||
$this->entityManager = $entityManager;
|
||||
$this->config = $config;
|
||||
$this->smsSender = $smsSender;
|
||||
$this->language = $language;
|
||||
$this->smsFactory = $smsFactory;
|
||||
}
|
||||
|
||||
public function storePhoneNumber(User $user, string $phoneNumber): void
|
||||
{
|
||||
$this->checkPhoneNumberIsUsers($user, $phoneNumber);
|
||||
|
||||
$userData = $this->getUserDataRepository()->getByUserId($user->getId());
|
||||
|
||||
$userData->set('auth2FASmsPhoneNumber', $phoneNumber);
|
||||
|
||||
$this->entityManager->saveEntity($userData);
|
||||
}
|
||||
|
||||
public function verifyCode(User $user, string $code): bool
|
||||
{
|
||||
$codeEntity = $this->findCodeEntity($user);
|
||||
|
||||
if (!$codeEntity) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($codeEntity->getAttemptsLeft() <= 1) {
|
||||
$this->decrementAttemptsLeft($codeEntity);
|
||||
$this->inactivateExistingCodeRecords($user);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($codeEntity->getCode() !== $code) {
|
||||
$this->decrementAttemptsLeft($codeEntity);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!$this->isCodeValidByLifetime($codeEntity)) {
|
||||
$this->inactivateExistingCodeRecords($user);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->inactivateExistingCodeRecords($user);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function sendCode(User $user, ?string $phoneNumber = null): void
|
||||
{
|
||||
if ($phoneNumber === null) {
|
||||
$phoneNumber = $this->getPhoneNumber($user);
|
||||
}
|
||||
|
||||
$this->checkPhoneNumberIsUsers($user, $phoneNumber);
|
||||
$this->checkCodeLimit($user);
|
||||
|
||||
$code = $this->generateCode();
|
||||
|
||||
$this->inactivateExistingCodeRecords($user);
|
||||
$this->createCodeRecord($user, $code);
|
||||
|
||||
$sms = $this->createSms($user, $code, $phoneNumber);
|
||||
|
||||
$this->smsSender->send($sms);
|
||||
}
|
||||
|
||||
private function isCodeValidByLifetime(TwoFactorCode $codeEntity): bool
|
||||
{
|
||||
$period = $this->config->get('auth2FASmsCodeLifetimePeriod') ?? self::CODE_LIFETIME_PERIOD;
|
||||
|
||||
$validUntil = $codeEntity->getCreatedAt()->modify($period);
|
||||
|
||||
if (DateTime::createNow()->diff($validUntil)->invert) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function findCodeEntity(User $user): ?TwoFactorCode
|
||||
{
|
||||
return $this->entityManager
|
||||
->getRDBRepository(TwoFactorCode::ENTITY_TYPE)
|
||||
->where([
|
||||
'method' => 'Sms',
|
||||
'userId' => $user->getId(),
|
||||
'isActive' => true,
|
||||
])
|
||||
->findOne();
|
||||
}
|
||||
|
||||
private function getPhoneNumber(User $user): string
|
||||
{
|
||||
$userData = $this->getUserDataRepository()->getByUserId($user->getId());
|
||||
|
||||
if (!$userData) {
|
||||
throw new Error("UserData not found.");
|
||||
}
|
||||
|
||||
$phoneNumber = $userData->get('auth2FASmsPhoneNumber');
|
||||
|
||||
if ($phoneNumber) {
|
||||
return $phoneNumber;
|
||||
}
|
||||
|
||||
if ($user->getPhoneNumberGroup()->getCount() === 0) {
|
||||
throw new Error("User does not have phone number.");
|
||||
}
|
||||
|
||||
return $user->getPhoneNumberGroup()->getPrimary()->getNumber();
|
||||
}
|
||||
|
||||
private function checkPhoneNumberIsUsers(User $user, string $phoneNumber): void
|
||||
{
|
||||
$userNumberList = array_map(
|
||||
function (string $item) {
|
||||
return strtolower($item);
|
||||
},
|
||||
$user->getPhoneNumberGroup()->getNumberList()
|
||||
);
|
||||
|
||||
if (!in_array(strtolower($phoneNumber), $userNumberList)) {
|
||||
throw new Forbidden("Phone number is not one of user's.");
|
||||
}
|
||||
}
|
||||
|
||||
private function checkCodeLimit(User $user): void
|
||||
{
|
||||
$limit = $this->config->get('auth2FASmsCodeLimit') ?? self::CODE_LIMIT;
|
||||
$period = $this->config->get('auth2FASmsCodeLimitPeriod') ?? self::CODE_LIMIT_PERIOD;
|
||||
|
||||
$from = DateTime::createNow()
|
||||
->modify('-' . $period)
|
||||
->getString();
|
||||
|
||||
$count = $this->entityManager
|
||||
->getRDBRepository(TwoFactorCode::ENTITY_TYPE)
|
||||
->where(
|
||||
Cond::and(
|
||||
Cond::equal(Cond::column('method'), 'Sms'),
|
||||
Cond::equal(Cond::column('userId'), $user->getId()),
|
||||
Cond::greaterOrEqual(Cond::column('createdAt'), $from),
|
||||
Cond::lessOrEqual(Cond::column('attemptsLeft'), 0),
|
||||
)
|
||||
)
|
||||
->count();
|
||||
|
||||
if ($count >= $limit) {
|
||||
throw new Forbidden("Max code count exceeded.");
|
||||
}
|
||||
}
|
||||
|
||||
private function generateCode(): string
|
||||
{
|
||||
$codeLength = $this->config->get('auth2FASmsCodeLength') ?? self::CODE_LENGTH;
|
||||
|
||||
$max = pow(10, $codeLength) - 1;
|
||||
|
||||
return str_pad(
|
||||
(string) random_int(0, $max),
|
||||
$codeLength,
|
||||
'0',
|
||||
STR_PAD_LEFT
|
||||
);
|
||||
}
|
||||
|
||||
private function createSms(User $user, string $code, string $phoneNumber): Sms
|
||||
{
|
||||
$fromNumber = $this->config->get('outboundSmsFromNumber');
|
||||
|
||||
$bodyTpl = $this->language->translate('yourAuthenticationCode', 'messages', 'User');
|
||||
|
||||
$body = str_replace('{code}', $code, $bodyTpl);
|
||||
|
||||
$sms = $this->smsFactory->create();
|
||||
|
||||
$sms->setFromNumber($fromNumber);
|
||||
$sms->setBody($body);
|
||||
$sms->addToNumber($phoneNumber);
|
||||
|
||||
return $sms;
|
||||
}
|
||||
|
||||
private function inactivateExistingCodeRecords(User $user): void
|
||||
{
|
||||
$query = $this->entityManager
|
||||
->getQueryBuilder()
|
||||
->update()
|
||||
->in(TwoFactorCode::ENTITY_TYPE)
|
||||
->where([
|
||||
'userId' => $user->getId(),
|
||||
'method' => 'Sms',
|
||||
])
|
||||
->set([
|
||||
'isActive' => false,
|
||||
])
|
||||
->build();
|
||||
|
||||
$this->entityManager
|
||||
->getQueryExecutor()
|
||||
->execute($query);
|
||||
}
|
||||
|
||||
private function createCodeRecord(User $user, string $code): void
|
||||
{
|
||||
$this->entityManager->createEntity(TwoFactorCode::ENTITY_TYPE, [
|
||||
'code' => $code,
|
||||
'userId' => $user->getId(),
|
||||
'method' => 'Sms',
|
||||
'attemptsLeft' => $this->getCodeAttemptsCount(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function getUserDataRepository(): UserDataRepository
|
||||
{
|
||||
return $this->entityManager->getRepository(UserData::ENTITY_TYPE);
|
||||
}
|
||||
|
||||
private function decrementAttemptsLeft(TwoFactorCode $codeEntity): void
|
||||
{
|
||||
$codeEntity->decrementAttemptsLeft();
|
||||
|
||||
$this->entityManager->saveEntity($codeEntity);
|
||||
}
|
||||
|
||||
private function getCodeAttemptsCount(): int
|
||||
{
|
||||
return $this->config->get('auth2FASmsCodeAttemptsCount') ?? self::CODE_ATTEMPTS_COUNT;
|
||||
}
|
||||
}
|
||||
@@ -217,5 +217,10 @@ class DefaultBinding implements BindingProcessor
|
||||
'Espo\\Core\\WebSocket\\Sender',
|
||||
'Espo\\Core\\WebSocket\\SenderFactory'
|
||||
);
|
||||
|
||||
$binder->bindFactory(
|
||||
'Espo\\Core\\Sms\\Sender',
|
||||
'Espo\\Core\\Sms\\SenderFactory'
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Formula\Functions\ExtGroup\SmsGroup;
|
||||
|
||||
use Espo\Core\Formula\Functions\BaseFunction;
|
||||
use Espo\Core\Formula\ArgumentList;
|
||||
use Espo\Core\Sms\SmsSender;
|
||||
use Espo\Entities\Sms;
|
||||
|
||||
use Espo\Core\Di;
|
||||
|
||||
use Exception;
|
||||
|
||||
class SendType extends BaseFunction implements
|
||||
|
||||
Di\EntityManagerAware,
|
||||
Di\InjectableFactoryAware
|
||||
{
|
||||
use Di\EntityManagerSetter;
|
||||
use Di\InjectableFactorySetter;
|
||||
|
||||
public function process(ArgumentList $args)
|
||||
{
|
||||
if (count($args) < 1) {
|
||||
$this->throwTooFewArguments(1);
|
||||
}
|
||||
|
||||
$evaluatedArgs = $this->evaluate($args);
|
||||
|
||||
$id = $evaluatedArgs[0];
|
||||
|
||||
if (!$id || !is_string($id)) {
|
||||
$this->throwBadArgumentType(1, 'string');
|
||||
}
|
||||
|
||||
/** @var Sms $sms */
|
||||
$sms = $this->entityManager->getEntity(Sms::ENTITY_TYPE, $id);
|
||||
|
||||
if (!$sms) {
|
||||
$this->log("Sms '{$id}' does not exist.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($sms->getStatus() === Sms::STATUS_SENT) {
|
||||
$this->log("Can't send SMS that has 'Sent' status.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
try {
|
||||
$this->createSender()->send($sms);
|
||||
}
|
||||
catch (Exception $e) {
|
||||
$message = $e->getMessage();
|
||||
|
||||
$this->log("Error while sending SMS. Message: {$message}." , 'error');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private function createSender(): SmsSender
|
||||
{
|
||||
return $this->injectableFactory->create(SmsSender::class);
|
||||
}
|
||||
}
|
||||
38
application/Espo/Core/Sms/Sender.php
Normal file
38
application/Espo/Core/Sms/Sender.php
Normal file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Sms;
|
||||
|
||||
/**
|
||||
* An SMS sender.
|
||||
*/
|
||||
interface Sender
|
||||
{
|
||||
public function send(Sms $sms): void;
|
||||
}
|
||||
74
application/Espo/Core/Sms/SenderFactory.php
Normal file
74
application/Espo/Core/Sms/SenderFactory.php
Normal file
@@ -0,0 +1,74 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Sms;
|
||||
|
||||
use Espo\Core\Binding\Factory;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Utils\Metadata;
|
||||
use Espo\Core\InjectableFactory;
|
||||
|
||||
use RuntimeException;
|
||||
|
||||
class SenderFactory implements Factory
|
||||
{
|
||||
private $config;
|
||||
|
||||
private $metadata;
|
||||
|
||||
private $injectableFactory;
|
||||
|
||||
public function __construct(
|
||||
Config $config,
|
||||
Metadata $metadata,
|
||||
InjectableFactory $injectableFactory
|
||||
) {
|
||||
$this->config = $config;
|
||||
$this->metadata = $metadata;
|
||||
$this->injectableFactory = $injectableFactory;
|
||||
}
|
||||
|
||||
public function create(): Sender
|
||||
{
|
||||
$provider = $this->config->get('smsProvider');
|
||||
|
||||
if (!$provider) {
|
||||
throw new RuntimeException("No `smsProvider` in config.");
|
||||
}
|
||||
|
||||
$className = $this->metadata->get(['app', 'smsProviders', $provider, 'senderClassName']);
|
||||
|
||||
if (!$className) {
|
||||
throw new RuntimeException("No `senderClassName` for '{$provider}' provider.");
|
||||
}
|
||||
|
||||
return $this->injectableFactory->create($className);
|
||||
}
|
||||
}
|
||||
47
application/Espo/Core/Sms/Sms.php
Normal file
47
application/Espo/Core/Sms/Sms.php
Normal file
@@ -0,0 +1,47 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Sms;
|
||||
|
||||
/**
|
||||
* An SMS message.
|
||||
*/
|
||||
interface Sms
|
||||
{
|
||||
public function getBody(): string;
|
||||
|
||||
public function getFromNumber(): ?string;
|
||||
|
||||
public function getFromName(): ?string;
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getToNumberList(): array;
|
||||
}
|
||||
54
application/Espo/Core/Sms/SmsFactory.php
Normal file
54
application/Espo/Core/Sms/SmsFactory.php
Normal file
@@ -0,0 +1,54 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Sms;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Entities\Sms as SmsEntity;
|
||||
|
||||
/**
|
||||
* Creates SMS instances.
|
||||
*/
|
||||
class SmsFactory
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create an SMS instance.
|
||||
*/
|
||||
public function create(): SmsEntity
|
||||
{
|
||||
return $this->entityManager->getNewEntity(SmsEntity::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
60
application/Espo/Core/Sms/SmsSender.php
Normal file
60
application/Espo/Core/Sms/SmsSender.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Sms;
|
||||
|
||||
use Espo\Entities\Sms as SmsEntity;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
|
||||
class SmsSender
|
||||
{
|
||||
private $sender;
|
||||
|
||||
private $config;
|
||||
|
||||
public function __construct(Sender $sender, Config $config)
|
||||
{
|
||||
$this->sender = $sender;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function send(SmsEntity $sms): void
|
||||
{
|
||||
$systemFromNumber = $this->config->get('outboundSmsFromNumber');
|
||||
|
||||
if ($sms->getFromNumber() === null && $systemFromNumber) {
|
||||
$sms->setFromNumber($systemFromNumber);
|
||||
}
|
||||
|
||||
$this->sender->send($sms);
|
||||
|
||||
$sms->setAsSent();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Core\Utils\Database\Orm\Relations;
|
||||
|
||||
use Espo\Core\Utils\Util;
|
||||
|
||||
class SmsPhoneNumber extends HasMany
|
||||
{
|
||||
protected function load($linkName, $entityName)
|
||||
{
|
||||
$parentRelation = parent::load($linkName, $entityName);
|
||||
|
||||
$foreignEntityName = $this->getForeignEntityName();
|
||||
|
||||
$relation = [
|
||||
$entityName => [
|
||||
'relations' => [
|
||||
$linkName => [
|
||||
'midKeys' => [
|
||||
lcfirst($entityName) . 'Id',
|
||||
lcfirst($foreignEntityName) . 'Id',
|
||||
],
|
||||
],
|
||||
],
|
||||
],
|
||||
];
|
||||
|
||||
return Util::merge($parentRelation, $relation);
|
||||
}
|
||||
}
|
||||
134
application/Espo/Entities/Sms.php
Normal file
134
application/Espo/Entities/Sms.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Entities;
|
||||
|
||||
use Espo\Core\ORM\Entity;
|
||||
use Espo\Core\Sms\Sms as SmsInterface;
|
||||
use Espo\Core\Field\DateTime;
|
||||
|
||||
use Espo\Repositories\Sms as SmsRepository;
|
||||
|
||||
class Sms extends Entity implements SmsInterface
|
||||
{
|
||||
public const ENTITY_TYPE = 'Sms';
|
||||
|
||||
public const STATUS_ARCHIVED = 'Archived';
|
||||
|
||||
public const STATUS_SENT = 'Sent';
|
||||
|
||||
public const STATUS_SENDING = 'Sending';
|
||||
|
||||
public const STATUS_DRAFT = 'Draft';
|
||||
|
||||
public function getDateSent(): ?DateTime
|
||||
{
|
||||
return $this->getValueObject('dateTime');
|
||||
}
|
||||
|
||||
public function getCreatedAt(): ?DateTime
|
||||
{
|
||||
return $this->getValueObject('createdAt');
|
||||
}
|
||||
|
||||
public function getBody(): string
|
||||
{
|
||||
return $this->get('body') ?? '';
|
||||
}
|
||||
|
||||
public function getStatus(): ?string
|
||||
{
|
||||
return $this->get('status');
|
||||
}
|
||||
|
||||
public function setBody(?string $body): void
|
||||
{
|
||||
$this->set('body', $body);
|
||||
}
|
||||
|
||||
public function setFromNumber(?string $number): void
|
||||
{
|
||||
$this->set('from', $number);
|
||||
}
|
||||
|
||||
public function addToNumber(string $number): void
|
||||
{
|
||||
$list = $this->getToNumberList();
|
||||
|
||||
$list[] = $number;
|
||||
|
||||
$this->set('to', implode(';', $list));
|
||||
}
|
||||
|
||||
public function getFromNumber(): ?string
|
||||
{
|
||||
if (!$this->hasInContainer('from') && !$this->isNew()) {
|
||||
$this->getSmsRepository()->loadFromField($this);
|
||||
}
|
||||
|
||||
return $this->get('from');
|
||||
}
|
||||
|
||||
public function getFromName(): ?string
|
||||
{
|
||||
return $this->get('fromName');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
public function getToNumberList(): array
|
||||
{
|
||||
if (!$this->hasInContainer('to') && !$this->isNew()) {
|
||||
$this->getSmsRepository()->loadToField($this);
|
||||
}
|
||||
|
||||
$value = $this->get('to');
|
||||
|
||||
if (!$value) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return explode(';', $value);
|
||||
}
|
||||
|
||||
public function setAsSent(): void
|
||||
{
|
||||
$this->set('status', self::STATUS_SENT);
|
||||
|
||||
if (!$this->get('dateSent')) {
|
||||
$this->set('dateSent', DateTime::createNow()->getString());
|
||||
}
|
||||
}
|
||||
|
||||
private function getSmsRepository(): SmsRepository
|
||||
{
|
||||
return $this->entityManager->getRepository(self::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
111
application/Espo/Hooks/Sms/Numbers.php
Normal file
111
application/Espo/Hooks/Sms/Numbers.php
Normal file
@@ -0,0 +1,111 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Hooks\Sms;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
use Espo\Entities\Sms;
|
||||
use Espo\Entities\PhoneNumber;
|
||||
use Espo\Repositories\PhoneNumber as PhoneNumberRepository;
|
||||
|
||||
use Espo\ORM\Entity;
|
||||
|
||||
class Numbers
|
||||
{
|
||||
private $entityManager;
|
||||
|
||||
public function __construct(EntityManager $entityManager)
|
||||
{
|
||||
$this->entityManager = $entityManager;
|
||||
}
|
||||
|
||||
public function beforeSave(Entity $entity): void
|
||||
{
|
||||
$this->processNumbers($entity);
|
||||
}
|
||||
|
||||
private function processNumbers(Sms $entity): void
|
||||
{
|
||||
if ($entity->has('from')) {
|
||||
$this->processFrom($entity);
|
||||
}
|
||||
|
||||
if ($entity->has('to')) {
|
||||
$this->processTo($entity);
|
||||
}
|
||||
}
|
||||
|
||||
private function processFrom(Sms $entity): void
|
||||
{
|
||||
$from = $entity->get('from');
|
||||
|
||||
$entity->set('fromPhoneNumberId', null);
|
||||
$entity->set('fromEmailAddressName', null);
|
||||
|
||||
if (!$from) {
|
||||
return;
|
||||
}
|
||||
|
||||
$numberIds = $this->getPhoneNumberRepository()->getIds([$from]);
|
||||
|
||||
if (!count($numberIds)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$entity->set('fromEmailAddressId', $numberIds[0]);
|
||||
$entity->set('fromEmailAddressName', $from);
|
||||
}
|
||||
|
||||
private function processTo(Sms $entity): void
|
||||
{
|
||||
$entity->setLinkMultipleIdList('toPhoneNumbers', []);
|
||||
|
||||
$to = $entity->get('to');
|
||||
|
||||
if ($to === null || !$to) {
|
||||
return;
|
||||
}
|
||||
|
||||
$numberList = array_map(
|
||||
function (string $item): string {
|
||||
return trim($item);
|
||||
},
|
||||
explode(';', $to)
|
||||
);
|
||||
|
||||
$numberIds = $this->getPhoneNumberRepository()->getIds($numberList);
|
||||
|
||||
$entity->setLinkMultipleIdList('toPhoneNumbers', $numberIds);
|
||||
}
|
||||
|
||||
private function getPhoneNumberRepository(): PhoneNumberRepository
|
||||
{
|
||||
return $this->entityManager->getRepository(PhoneNumber::ENTITY_TYPE);
|
||||
}
|
||||
}
|
||||
84
application/Espo/Repositories/Sms.php
Normal file
84
application/Espo/Repositories/Sms.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Repositories;
|
||||
|
||||
use Espo\Entities\Sms as SmsEntity;
|
||||
use Espo\Entities\PhoneNumber;
|
||||
|
||||
use Espo\Core\Repositories\Database;
|
||||
|
||||
class Sms extends Database
|
||||
{
|
||||
public function loadFromField(SmsEntity $entity): void
|
||||
{
|
||||
if ($entity->get('fromPhoneNumberName')) {
|
||||
$entity->set('from', $entity->get('fromPhoneNumberName'));
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$numberId = $entity->get('fromPhoneNumberId');
|
||||
|
||||
if ($numberId) {
|
||||
$phoneNumber = $this->entityManager
|
||||
->getRepository(PhoneNumber::ENTITY_TYPE)
|
||||
->getById($numberId);
|
||||
|
||||
if ($phoneNumber) {
|
||||
$entity->set('from', $phoneNumber->get('name'));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
$entity->set('from', null);
|
||||
}
|
||||
|
||||
public function loadToField(SmsEntity $entity): void
|
||||
{
|
||||
$entity->loadLinkMultipleField('toPhoneNumbers');
|
||||
|
||||
$names = $entity->get('toPhoneNumbersNames');
|
||||
|
||||
if (empty($names)) {
|
||||
$entity->set('to', null);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$list = [];
|
||||
|
||||
foreach ($names as $address) {
|
||||
$list[] = $address;
|
||||
}
|
||||
|
||||
$entity->set('to', implode(';', $list));
|
||||
}
|
||||
}
|
||||
@@ -205,6 +205,7 @@ return [
|
||||
'passwordRecoveryRequestDelay',
|
||||
'thumbImageCacheDisabled',
|
||||
'emailReminderPortionSize',
|
||||
'outboundSmsFromNumber',
|
||||
'latestVersion',
|
||||
],
|
||||
'superAdminItems' => [
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
"System": "System",
|
||||
"Users": "Users",
|
||||
"Email": "Email",
|
||||
"Messaging": "Messaging",
|
||||
"Data": "Data",
|
||||
"Customization": "Customization",
|
||||
"Available Fields": "Available Fields",
|
||||
@@ -278,6 +279,7 @@
|
||||
"dashboardTemplates": "Deploy dashboards to users.",
|
||||
"layoutSets": "Collections of layouts that can be assigned to teams & portals.",
|
||||
"jobsSettings": "Job processing settings. Jobs execute tasks in the background.",
|
||||
"sms": "SMS settings.",
|
||||
"pdfTemplates": "Templates for printing to PDF."
|
||||
},
|
||||
"keywords": {
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
"currencyList": "Currency List",
|
||||
"language": "Language",
|
||||
"companyLogo": "Company Logo",
|
||||
"smsProvider": "SMS Provider",
|
||||
"outboundSmsFromNumber": "SMS From Number",
|
||||
"smtpServer": "Server",
|
||||
"smtpPort": "Port",
|
||||
"smtpAuth": "Auth",
|
||||
@@ -155,7 +157,8 @@
|
||||
},
|
||||
"auth2FAMethodList": {
|
||||
"Totp": "TOTP",
|
||||
"Email": "Email"
|
||||
"Email": "Email",
|
||||
"Sms": "SMS"
|
||||
}
|
||||
},
|
||||
"tooltips": {
|
||||
|
||||
@@ -109,8 +109,11 @@
|
||||
"userNameExists": "User Name already exists",
|
||||
"wrongCode": "Wrong code",
|
||||
"codeIsRequired": "Code is required",
|
||||
"yourAuthenticationCode": "Your authentication code: {code}.",
|
||||
"choose2FaSmsPhoneNumber": "Select a phone number that will be used for 2FA.",
|
||||
"choose2FaEmailAddress": "Select an email address that will be used for 2FA. It's highly recommended to use a non-primary email address.",
|
||||
"enterCodeSentInEmail": "Enter the code sent to your email address.",
|
||||
"enterCodeSentBySms": "Enter the code sent by SMS to your phone number.",
|
||||
"enterTotpCode": "Enter a code from your authenticator app.",
|
||||
"verifyTotpCode": "Scan the QR-code with your mobile authenticator app. If you have a trouble with scanning, you can enter the secret manually. After that you will see a 6-digit code in your application. Enter this code in the field below.",
|
||||
"generateAndSendNewPassword": "A new password will be generated and sent to the user's email address.",
|
||||
|
||||
8
application/Espo/Resources/layouts/Settings/sms.json
Normal file
8
application/Espo/Resources/layouts/Settings/sms.json
Normal file
@@ -0,0 +1,8 @@
|
||||
[
|
||||
{
|
||||
"rows": [
|
||||
[{"name": "smsProvider"}, false],
|
||||
[{"name": "outboundSmsFromNumber"}, false]
|
||||
]
|
||||
}
|
||||
]
|
||||
@@ -169,7 +169,7 @@
|
||||
"order": 10
|
||||
},
|
||||
"email":{
|
||||
"label": "Email",
|
||||
"label": "Messaging",
|
||||
"itemList": [
|
||||
{
|
||||
"url": "#Admin/outboundEmails",
|
||||
@@ -208,6 +208,13 @@
|
||||
"label": "Email Templates",
|
||||
"iconClass": "fas fa-envelope-square",
|
||||
"description": "emailTemplates"
|
||||
},
|
||||
{
|
||||
"url": "#Admin/sms",
|
||||
"label": "SMS",
|
||||
"iconClass": "fas fa-paper-plane",
|
||||
"description": "sms",
|
||||
"recordView": "views/admin/sms"
|
||||
}
|
||||
],
|
||||
"order": 15
|
||||
|
||||
@@ -14,5 +14,13 @@
|
||||
"userApplyView": "views/user-security/modals/two-factor-email",
|
||||
"loginClassName": "Espo\\Core\\Authentication\\TwoFactor\\Email\\EmailLogin",
|
||||
"userSetupClassName": "Espo\\Core\\Authentication\\TwoFactor\\Email\\EmailUserSetup"
|
||||
},
|
||||
"Sms": {
|
||||
"settings": {
|
||||
"isAvailable": true
|
||||
},
|
||||
"userApplyView": "views/user-security/modals/two-factor-sms",
|
||||
"loginClassName": "Espo\\Core\\Authentication\\TwoFactor\\Sms\\SmsLogin",
|
||||
"userSetupClassName": "Espo\\Core\\Authentication\\TwoFactor\\Sms\\SmsUserSetup"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,9 @@
|
||||
"params": {
|
||||
"awsS3Storage": {
|
||||
"level": "system"
|
||||
},
|
||||
"smsProvider": {
|
||||
"level": "admin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -378,6 +378,11 @@
|
||||
"insertText": "ext\\email\\send(EMAIL_ID)",
|
||||
"returnType": "bool"
|
||||
},
|
||||
{
|
||||
"name": "ext\\sms\\send",
|
||||
"insertText": "ext\\sms\\send(SMS_ID)",
|
||||
"returnType": "bool"
|
||||
},
|
||||
{
|
||||
"name": "ext\\email\\applyTemplate",
|
||||
"insertText": "ext\\email\\applyTemplate(EMAIL_ID, EMAIL_TEMPLATE_ID)"
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
["app", "webSocket", "messagers"],
|
||||
["app", "config"],
|
||||
["app", "rebuild"],
|
||||
["app", "smsProviders", "__ANY__", "senderClassName"],
|
||||
["selectDefs"],
|
||||
["recordDefs"],
|
||||
["pdfDefs"],
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
{
|
||||
|
||||
}
|
||||
@@ -635,6 +635,14 @@
|
||||
},
|
||||
"awsS3Storage": {
|
||||
"type": "jsonObject"
|
||||
},
|
||||
"outboundSmsFromNumber": {
|
||||
"type": "varchar",
|
||||
"trim": true
|
||||
},
|
||||
"smsProvider": {
|
||||
"type": "enum",
|
||||
"view": "views/settings/fields/sms-provider"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
147
application/Espo/Resources/metadata/entityDefs/Sms.json
Normal file
147
application/Espo/Resources/metadata/entityDefs/Sms.json
Normal file
@@ -0,0 +1,147 @@
|
||||
{
|
||||
"fields": {
|
||||
"from": {
|
||||
"type": "varchar",
|
||||
"notStorable": true,
|
||||
"required": true,
|
||||
"textFilterDisabled": true
|
||||
},
|
||||
"fromName": {
|
||||
"type": "varchar"
|
||||
},
|
||||
"to": {
|
||||
"type": "varchar",
|
||||
"notStorable": true,
|
||||
"required": true,
|
||||
"textFilterDisabled": true
|
||||
},
|
||||
"fromPhoneNumber": {
|
||||
"type": "link",
|
||||
"textFilterDisabled": true
|
||||
},
|
||||
"toPhoneNumbers": {
|
||||
"type": "linkMultiple"
|
||||
},
|
||||
"body": {
|
||||
"type": "text"
|
||||
},
|
||||
"status": {
|
||||
"type": "enum",
|
||||
"options": ["Draft", "Sending", "Sent", "Archived", "Failed"],
|
||||
"default": "Archived",
|
||||
"clientReadOnly": true,
|
||||
"style": {
|
||||
"Draft": "warning",
|
||||
"Failed": "danger",
|
||||
"Sending": "warning"
|
||||
}
|
||||
},
|
||||
"parent": {
|
||||
"type": "linkParent"
|
||||
},
|
||||
"dateSent": {
|
||||
"type": "datetime"
|
||||
},
|
||||
"createdAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"modifiedAt": {
|
||||
"type": "datetime",
|
||||
"readOnly": true
|
||||
},
|
||||
"createdBy": {
|
||||
"type": "link",
|
||||
"readOnly": true,
|
||||
"view": "views/fields/user"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "link",
|
||||
"readOnly": true,
|
||||
"view": "views/fields/user"
|
||||
},
|
||||
"replied": {
|
||||
"type": "link",
|
||||
"noJoin": true,
|
||||
"readOnly": true,
|
||||
"view": "views/email/fields/replied"
|
||||
},
|
||||
"replies": {
|
||||
"type": "linkMultiple",
|
||||
"readOnly": true,
|
||||
"orderBy": "dateSent",
|
||||
"view": "views/email/fields/replies"
|
||||
},
|
||||
"teams": {
|
||||
"type": "linkMultiple",
|
||||
"view": "views/fields/teams"
|
||||
}
|
||||
},
|
||||
"links": {
|
||||
"createdBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"modifiedBy": {
|
||||
"type": "belongsTo",
|
||||
"entity": "User"
|
||||
},
|
||||
"teams": {
|
||||
"type": "hasMany",
|
||||
"entity": "Team",
|
||||
"relationName": "entityTeam"
|
||||
},
|
||||
"parent": {
|
||||
"type": "belongsToParent",
|
||||
"entityList": [
|
||||
"Account",
|
||||
"Contact",
|
||||
"Lead",
|
||||
"Opportunity"
|
||||
],
|
||||
"foreign": "emails"
|
||||
},
|
||||
"replied": {
|
||||
"type": "belongsTo",
|
||||
"entity": "Sms",
|
||||
"foreign": "replies",
|
||||
"foreignName": "id"
|
||||
},
|
||||
"replies": {
|
||||
"type": "hasMany",
|
||||
"entity": "Sms",
|
||||
"foreign": "replied"
|
||||
},
|
||||
"fromPhoneNumber": {
|
||||
"type": "belongsTo",
|
||||
"entity": "PhoneNumber"
|
||||
},
|
||||
"toPhoneNumbers": {
|
||||
"type": "hasMany",
|
||||
"entity": "PhoneNumber",
|
||||
"relationName": "smsPhoneNumber",
|
||||
"conditions": {
|
||||
"addressType": "to"
|
||||
},
|
||||
"additionalColumns": {
|
||||
"addressType": {
|
||||
"type": "varchar",
|
||||
"len": "4"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"collection": {
|
||||
"orderBy": "dateSent",
|
||||
"order": "desc",
|
||||
"textFilterFields": ["name", "body"]
|
||||
},
|
||||
"indexes": {
|
||||
"dateSent": {
|
||||
"columns": ["dateSent", "deleted"]
|
||||
},
|
||||
"dateSentStatus": {
|
||||
"columns": ["dateSent", "status", "deleted"]
|
||||
}
|
||||
}
|
||||
}
|
||||
90
application/Espo/Services/TwoFactorSms.php
Normal file
90
application/Espo/Services/TwoFactorSms.php
Normal file
@@ -0,0 +1,90 @@
|
||||
<?php
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
namespace Espo\Services;
|
||||
|
||||
use Espo\Core\Exceptions\Forbidden;
|
||||
use Espo\Core\Exceptions\NotFound;
|
||||
|
||||
use Espo\Core\Utils\Config;
|
||||
use Espo\Core\Authentication\TwoFactor\Sms\Util;
|
||||
|
||||
use Espo\ORM\EntityManager;
|
||||
|
||||
use Espo\Entities\User;
|
||||
|
||||
class TwoFactorSms
|
||||
{
|
||||
private $util;
|
||||
|
||||
private $user;
|
||||
|
||||
private $entityManager;
|
||||
|
||||
private $config;
|
||||
|
||||
public function __construct(Util $util, User $user, EntityManager $entityManager, Config $config)
|
||||
{
|
||||
$this->util = $util;
|
||||
$this->user = $user;
|
||||
$this->entityManager = $entityManager;
|
||||
$this->config = $config;
|
||||
}
|
||||
|
||||
public function sendCode(string $userId, string $phoneNumber): void
|
||||
{
|
||||
if (!$this->user->isAdmin() && $userId !== $this->user->getId()) {
|
||||
throw new Forbidden();
|
||||
}
|
||||
|
||||
$this->checkAllowed();
|
||||
|
||||
$user = $this->entityManager->getEntity(User::ENTITY_TYPE, $userId);
|
||||
|
||||
if (!$user) {
|
||||
throw new NotFound();
|
||||
}
|
||||
|
||||
$this->util->sendCode($user, $phoneNumber);
|
||||
$this->util->storePhoneNumber($user, $phoneNumber);
|
||||
}
|
||||
|
||||
private function checkAllowed(): void
|
||||
{
|
||||
if (!$this->config->get('auth2FA')) {
|
||||
throw new Forbidden("2FA is not enabled.");
|
||||
}
|
||||
|
||||
$methodList = $this->config->get('auth2FAMethodList') ?? [];
|
||||
|
||||
if (!in_array('Sms', $methodList)) {
|
||||
throw new Forbidden("Sms 2FA is not allowed.");
|
||||
}
|
||||
}
|
||||
}
|
||||
15
client/res/templates/user-security/modals/two-factor-sms.tpl
Normal file
15
client/res/templates/user-security/modals/two-factor-sms.tpl
Normal file
@@ -0,0 +1,15 @@
|
||||
<div class="panel no-side-margin">
|
||||
<div class="panel-body">
|
||||
<p class="p-info">
|
||||
{{translate 'choose2FaSmsPhoneNumber' category='messages' scope='User'}}
|
||||
</p>
|
||||
<p class="p-button">
|
||||
<button class="btn btn-default" data-action="sendCode">{{translate 'Send Code' scope='User'}}</button>
|
||||
</p>
|
||||
<p class="p-info-after hidden">
|
||||
{{translate 'enterCodeSentBySms' category='messages' scope='User'}}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="record">{{{record}}}</div>
|
||||
36
client/src/views/admin/sms.js
Normal file
36
client/src/views/admin/sms.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
define('views/admin/sms', 'views/settings/record/edit', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
layoutName: 'sms',
|
||||
|
||||
});
|
||||
});
|
||||
39
client/src/views/settings/fields/sms-provider.js
Normal file
39
client/src/views/settings/fields/sms-provider.js
Normal file
@@ -0,0 +1,39 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
define('views/settings/fields/sms-provider', 'views/fields/enum', function (Dep) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
setupOptions: function () {
|
||||
this.params.options = Object.keys(
|
||||
this.getMetadata().get(['app', 'smsProviders']) || {}
|
||||
);
|
||||
},
|
||||
});
|
||||
});
|
||||
192
client/src/views/user-security/modals/two-factor-sms.js
Normal file
192
client/src/views/user-security/modals/two-factor-sms.js
Normal file
@@ -0,0 +1,192 @@
|
||||
/************************************************************************
|
||||
* This file is part of EspoCRM.
|
||||
*
|
||||
* EspoCRM - Open Source CRM application.
|
||||
* Copyright (C) 2014-2021 Yurii Kuznietsov, Taras Machyshyn, Oleksii Avramenko
|
||||
* Website: https://www.espocrm.com
|
||||
*
|
||||
* EspoCRM is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* EspoCRM 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 General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with EspoCRM. If not, see http://www.gnu.org/licenses/.
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions
|
||||
* of this program must display Appropriate Legal Notices, as required under
|
||||
* Section 5 of the GNU General Public License version 3.
|
||||
*
|
||||
* In accordance with Section 7(b) of the GNU General Public License version 3,
|
||||
* these Appropriate Legal Notices must retain the display of the "EspoCRM" word.
|
||||
************************************************************************/
|
||||
|
||||
define('views/user-security/modals/two-factor-sms',
|
||||
['views/modal', 'model'],
|
||||
function (Dep, Model) {
|
||||
|
||||
return Dep.extend({
|
||||
|
||||
template: 'user-security/modals/two-factor-sms',
|
||||
|
||||
className: 'dialog dialog-record',
|
||||
|
||||
events: {
|
||||
'click [data-action="sendCode"]': function () {
|
||||
this.actionSendCode();
|
||||
},
|
||||
},
|
||||
|
||||
setup: function () {
|
||||
this.buttonList = [
|
||||
{
|
||||
name: 'apply',
|
||||
label: 'Apply',
|
||||
style: 'danger',
|
||||
hidden: true,
|
||||
},
|
||||
{
|
||||
name: 'cancel',
|
||||
label: 'Cancel',
|
||||
}
|
||||
];
|
||||
|
||||
this.headerHtml = false;
|
||||
|
||||
let codeLength = this.getConfig().get('auth2FASmsCodeLength') || 7;
|
||||
|
||||
let model = new Model();
|
||||
|
||||
model.name = 'UserSecurity';
|
||||
|
||||
model.set('phoneNumber', null);
|
||||
|
||||
model.setDefs({
|
||||
fields: {
|
||||
'code': {
|
||||
type: 'varchar',
|
||||
required: true,
|
||||
maxLength: codeLength,
|
||||
},
|
||||
'phoneNumber': {
|
||||
type: 'enum',
|
||||
required: true,
|
||||
},
|
||||
}
|
||||
});
|
||||
|
||||
this.internalModel = model;
|
||||
|
||||
this.wait(
|
||||
Espo.Ajax
|
||||
.postRequest('UserSecurity/action/getTwoFactorUserSetupData', {
|
||||
id: this.model.id,
|
||||
password: this.model.get('password'),
|
||||
auth2FAMethod: this.model.get('auth2FAMethod'),
|
||||
reset: this.options.reset,
|
||||
})
|
||||
.then(data => {
|
||||
this.phoneNumberList = data.phoneNumberList;
|
||||
|
||||
this.createView('record', 'views/record/edit-for-modal', {
|
||||
scope: 'None',
|
||||
el: this.getSelector() + ' .record',
|
||||
model: model,
|
||||
detailLayout: [
|
||||
{
|
||||
rows: [
|
||||
[
|
||||
{
|
||||
name: 'phoneNumber',
|
||||
labelText: this.translate('phoneNumber', 'fields', 'User'),
|
||||
},
|
||||
false
|
||||
],
|
||||
[
|
||||
{
|
||||
name: 'code',
|
||||
labelText: this.translate('Code', 'labels', 'User'),
|
||||
},
|
||||
false
|
||||
],
|
||||
]
|
||||
}
|
||||
],
|
||||
}, view => {
|
||||
view.setFieldOptionList('phoneNumber', this.phoneNumberList);
|
||||
|
||||
if (this.phoneNumberList.length) {
|
||||
model.set('phoneNumber', this.phoneNumberList[0]);
|
||||
}
|
||||
|
||||
view.hideField('code');
|
||||
});
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
afterRender: function () {
|
||||
this.$sendCode = this.$el.find('[data-action="sendCode"]');
|
||||
|
||||
this.$pInfo = this.$el.find('p.p-info');
|
||||
this.$pButton = this.$el.find('p.p-button');
|
||||
this.$pInfoAfter = this.$el.find('p.p-info-after');
|
||||
},
|
||||
|
||||
actionSendCode: function () {
|
||||
this.$sendCode.attr('disabled', 'disabled').addClass('disabled');
|
||||
|
||||
Espo.Ajax
|
||||
.postRequest('TwoFactorSms/action/sendCode', {
|
||||
id: this.model.id,
|
||||
phoneNumber: this.internalModel.get('phoneNumber'),
|
||||
})
|
||||
.then(() => {
|
||||
this.showButton('apply');
|
||||
|
||||
this.$pInfo.addClass('hidden');
|
||||
this.$pButton.addClass('hidden');
|
||||
this.$pInfoAfter.removeClass('hidden');
|
||||
|
||||
this.getView('record').setFieldReadOnly('phoneNumber');
|
||||
this.getView('record').showField('code');
|
||||
})
|
||||
.catch(() => {
|
||||
this.$sendCode.removeAttr('disabled').removeClass('disabled');
|
||||
});
|
||||
},
|
||||
|
||||
actionApply: function () {
|
||||
let data = this.getView('record').processFetch();
|
||||
|
||||
if (!data) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.model.set('code', data.code);
|
||||
|
||||
this.hideButton('apply');
|
||||
this.hideButton('cancel');
|
||||
|
||||
Espo.Ui.notify(this.translate('pleaseWait', 'messages'));
|
||||
|
||||
this.model
|
||||
.save()
|
||||
.then(() => {
|
||||
Espo.Ui.notify(false);
|
||||
|
||||
this.trigger('done');
|
||||
})
|
||||
.catch(() => {
|
||||
this.showButton('apply');
|
||||
this.showButton('cancel');
|
||||
});
|
||||
},
|
||||
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user