mirror of
https://github.com/SuiteCRM/SuiteCRM-Core.git
synced 2025-09-01 08:00:47 +08:00
Add Two Factor Authentication Popup
This commit is contained in:
parent
e99d51b4e7
commit
f96cb90da8
9 changed files with 496 additions and 32 deletions
|
@ -725,3 +725,6 @@ export * from './views/2fa/components/2fa/2fa.component';
|
||||||
export * from './views/2fa/components/2fa/2fa.module';
|
export * from './views/2fa/components/2fa/2fa.module';
|
||||||
export * from './views/2fa/components/2fa-check/2fa-check.component';
|
export * from './views/2fa/components/2fa-check/2fa-check.component';
|
||||||
export * from './views/2fa/components/2fa-check/2fa-check.module';
|
export * from './views/2fa/components/2fa-check/2fa-check.module';
|
||||||
|
export * from './views/2fa/components/2fa-check-modal/2fa-check-modal.component';
|
||||||
|
export * from './views/2fa/components/2fa-check-modal/2fa-check-modal.module';
|
||||||
|
export * from './views/2fa/components/2fa-check-modal/2fa-check-modal.model';
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
/**
|
||||||
|
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
|
||||||
|
* Copyright (C) 2024 SalesAgility Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
||||||
|
* Free Software Foundation with the addition of the following permission added
|
||||||
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
||||||
|
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
|
||||||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||||
|
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
|
||||||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||||
|
* the words "Supercharged by SuiteCRM".
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
import {take} from 'rxjs/operators';
|
||||||
|
import {Process, ProcessService} from "../../process.service";
|
||||||
|
import {Observable} from "rxjs";
|
||||||
|
|
||||||
|
@Injectable({providedIn: 'root'})
|
||||||
|
export class CheckTwoFactorCode {
|
||||||
|
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
protected processService: ProcessService,
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check Auth Code
|
||||||
|
*/
|
||||||
|
public checkCode(auth_code): Observable<Process> {
|
||||||
|
|
||||||
|
const processType = 'check-two-factor-code';
|
||||||
|
|
||||||
|
const options = {
|
||||||
|
auth_code
|
||||||
|
};
|
||||||
|
|
||||||
|
return this.processService.submit(processType, options).pipe(take(1));
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<! --
|
||||||
|
/**
|
||||||
|
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
|
||||||
|
* Copyright (C) 2024 SalesAgility Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
||||||
|
* Free Software Foundation with the addition of the following permission added
|
||||||
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
||||||
|
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
|
||||||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||||
|
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
|
||||||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||||
|
* the words "Supercharged by SuiteCRM".
|
||||||
|
*/
|
||||||
|
-->
|
||||||
|
<scrm-modal [closable]="false"
|
||||||
|
bodyKlass="m-0 small-font"
|
||||||
|
footerKlass="border-0"
|
||||||
|
headerKlass="border-0"
|
||||||
|
klass="two-factor-popup">
|
||||||
|
|
||||||
|
<div modal-body>
|
||||||
|
<div>
|
||||||
|
<div class="d-flex flex-column pt-4 align-items-center">
|
||||||
|
<scrm-label labelKey="LBL_ENTER_AUTH_APP_2FA_CODE" class="pb-4"></scrm-label>
|
||||||
|
<input [(ngModel)]="authCode"
|
||||||
|
id="auth_code"
|
||||||
|
type="text"
|
||||||
|
name="auth_code"
|
||||||
|
autocomplete="off"
|
||||||
|
class="mb-3 auth-input"
|
||||||
|
maxlength="6"/>
|
||||||
|
|
||||||
|
<button id="submit-2fa-code"
|
||||||
|
(click)="checkCode()"
|
||||||
|
type="submit"
|
||||||
|
class="btn btn-sm btn-main mb-1">
|
||||||
|
<scrm-label labelKey="LBL_VERIFY_2FA"></scrm-label>
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div class="small mt-2 text-muted">
|
||||||
|
<scrm-label labelKey="LBL_PROBLEMS_GENERATING_CODE"></scrm-label>
|
||||||
|
</div>
|
||||||
|
<div class="small mb-2 text-muted">
|
||||||
|
<scrm-label labelKey="LBL_BACKUP_CODES_FALLBACK_INSTRUCTIONS"></scrm-label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</scrm-modal>
|
|
@ -0,0 +1,76 @@
|
||||||
|
/**
|
||||||
|
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
|
||||||
|
* Copyright (C) 2024 SalesAgility Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
||||||
|
* Free Software Foundation with the addition of the following permission added
|
||||||
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
||||||
|
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
|
||||||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||||
|
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
|
||||||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||||
|
* the words "Supercharged by SuiteCRM".
|
||||||
|
*/
|
||||||
|
import {Component, HostListener} from "@angular/core";
|
||||||
|
import {NgbActiveModal} from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
import {LanguageStore} from "../../../../store/language/language.store";
|
||||||
|
import {CheckTwoFactorCode} from "../../../../services/process/processes/check-two-factor-code/check-two-factor-code";
|
||||||
|
import {TwoFactorCheckModalResult} from "./2fa-check-modal.model";
|
||||||
|
import {MessageService} from "../../../../services/message/message.service";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'scrm-2fa-modal',
|
||||||
|
templateUrl: './2fa-check-modal.component.html',
|
||||||
|
styleUrls: [],
|
||||||
|
})
|
||||||
|
export class TwoFactorCheckModalComponent {
|
||||||
|
|
||||||
|
authCode: string;
|
||||||
|
|
||||||
|
|
||||||
|
@HostListener('keyup.control.enter')
|
||||||
|
onEnterKey() {
|
||||||
|
this.checkCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
public activeModal: NgbActiveModal,
|
||||||
|
protected language: LanguageStore,
|
||||||
|
protected message: MessageService,
|
||||||
|
protected checkTwoFactorCode: CheckTwoFactorCode
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public checkCode() {
|
||||||
|
const authCode = this.authCode;
|
||||||
|
|
||||||
|
this.checkTwoFactorCode.checkCode(authCode).subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
this.closeModal(response.data.two_factor_complete)
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.message.addDangerMessageByKey('LBL_FACTOR_AUTH_FAIL')
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public closeModal(authComplete: boolean) {
|
||||||
|
this.activeModal.close({
|
||||||
|
two_factor_complete: authComplete
|
||||||
|
} as TwoFactorCheckModalResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/**
|
||||||
|
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
|
||||||
|
* Copyright (C) 2024 SalesAgility Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
||||||
|
* Free Software Foundation with the addition of the following permission added
|
||||||
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
||||||
|
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
|
||||||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||||
|
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
|
||||||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||||
|
* the words "Supercharged by SuiteCRM".
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface TwoFactorCheckModalResult {
|
||||||
|
[key: string]: any;
|
||||||
|
|
||||||
|
two_factor_complete: boolean
|
||||||
|
}
|
|
@ -0,0 +1,48 @@
|
||||||
|
/**
|
||||||
|
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
|
||||||
|
* Copyright (C) 2024 SalesAgility Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
||||||
|
* Free Software Foundation with the addition of the following permission added
|
||||||
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
||||||
|
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
|
||||||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||||
|
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
|
||||||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||||
|
* the words "Supercharged by SuiteCRM".
|
||||||
|
*/
|
||||||
|
|
||||||
|
import {NgModule} from "@angular/core";
|
||||||
|
import {CommonModule} from "@angular/common";
|
||||||
|
import {ModalModule} from "../../../../components/modal/components/modal/modal.module";
|
||||||
|
import {TwoFactorCheckModalComponent} from "./2fa-check-modal.component";
|
||||||
|
import {TwoFactorCheckModule} from "../2fa-check/2fa-check.module";
|
||||||
|
import {FormsModule} from "@angular/forms";
|
||||||
|
import {LabelModule} from "../../../../components/label/label.module";
|
||||||
|
import {TrustHtmlModule} from "../../../../pipes/trust-html/trust-html.module";
|
||||||
|
|
||||||
|
@NgModule({
|
||||||
|
declarations: [TwoFactorCheckModalComponent],
|
||||||
|
imports: [
|
||||||
|
CommonModule,
|
||||||
|
ModalModule,
|
||||||
|
TwoFactorCheckModule,
|
||||||
|
FormsModule,
|
||||||
|
LabelModule,
|
||||||
|
TrustHtmlModule,
|
||||||
|
]
|
||||||
|
})
|
||||||
|
export class TwoFactorCheckModalModule {
|
||||||
|
}
|
|
@ -34,6 +34,8 @@ import {ButtonCallback, ButtonInterface} from "../../../../common/components/but
|
||||||
import {UserPreferenceStore} from "../../../../store/user-preference/user-preference.store";
|
import {UserPreferenceStore} from "../../../../store/user-preference/user-preference.store";
|
||||||
import {Clipboard} from '@angular/cdk/clipboard';
|
import {Clipboard} from '@angular/cdk/clipboard';
|
||||||
import {GenerateBackupCodes} from "../../../../services/process/processes/generate-backup-codes/generate-backup-codes";
|
import {GenerateBackupCodes} from "../../../../services/process/processes/generate-backup-codes/generate-backup-codes";
|
||||||
|
import {NgbModal} from "@ng-bootstrap/ng-bootstrap";
|
||||||
|
import {TwoFactorCheckModalComponent} from "../2fa-check-modal/2fa-check-modal.component";
|
||||||
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
|
@ -70,6 +72,7 @@ export class TwoFactorComponent implements OnInit {
|
||||||
protected message: MessageService,
|
protected message: MessageService,
|
||||||
protected language: LanguageStore,
|
protected language: LanguageStore,
|
||||||
protected userPreference: UserPreferenceStore,
|
protected userPreference: UserPreferenceStore,
|
||||||
|
protected modalService: NgbModal,
|
||||||
protected clipboard: Clipboard,
|
protected clipboard: Clipboard,
|
||||||
protected generateBackupCodesService: GenerateBackupCodes,
|
protected generateBackupCodesService: GenerateBackupCodes,
|
||||||
) {
|
) {
|
||||||
|
@ -117,7 +120,7 @@ export class TwoFactorComponent implements OnInit {
|
||||||
onClick: ((): void => {
|
onClick: ((): void => {
|
||||||
this.generateBackupCodes();
|
this.generateBackupCodes();
|
||||||
}) as ButtonCallback,
|
}) as ButtonCallback,
|
||||||
labelKey: 'LBL_REGENERATE_BACKUP_CODES',
|
labelKey: 'LBL_REGENERATE_CODES',
|
||||||
titleKey: ''
|
titleKey: ''
|
||||||
} as ButtonInterface;
|
} as ButtonInterface;
|
||||||
}
|
}
|
||||||
|
@ -140,35 +143,46 @@ export class TwoFactorComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
public disable2FactorAuth(): void {
|
public disable2FactorAuth(): void {
|
||||||
this.authService.disable2fa().subscribe({
|
const modal = this.modalService.open(TwoFactorCheckModalComponent, {size: 'lg'});
|
||||||
next: (response) => {
|
|
||||||
if (isTrue(response?.two_factor_disabled)) {
|
|
||||||
|
|
||||||
this.isAppMethodEnabled.set(false);
|
|
||||||
this.areRecoveryCodesGenerated.set(false);
|
|
||||||
this.isQrCodeGenerated.set(false);
|
|
||||||
|
|
||||||
this.message.addSuccessMessageByKey('LBL_FACTOR_AUTH_DISABLE');
|
modal.result.then((result) => {
|
||||||
}
|
if (!result.two_factor_complete){
|
||||||
},
|
this.message.addDangerMessageByKey('LBL_FACTOR_AUTH_FAIL');
|
||||||
error: () => {
|
return;
|
||||||
this.isAppMethodEnabled.set(true);
|
|
||||||
this.areRecoveryCodesGenerated.set(true);
|
|
||||||
}
|
}
|
||||||
});
|
|
||||||
return;
|
this.authService.disable2fa().subscribe({
|
||||||
|
next: (response) => {
|
||||||
|
if (isTrue(response?.two_factor_disabled)) {
|
||||||
|
|
||||||
|
this.isAppMethodEnabled.set(false);
|
||||||
|
this.areRecoveryCodesGenerated.set(false);
|
||||||
|
this.isQrCodeGenerated.set(false);
|
||||||
|
|
||||||
|
this.message.addSuccessMessageByKey('LBL_FACTOR_AUTH_DISABLE');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
error: () => {
|
||||||
|
this.isAppMethodEnabled.set(true);
|
||||||
|
this.areRecoveryCodesGenerated.set(true);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}).catch();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getTitle(): string {
|
getTitle(): string {
|
||||||
return this.title;
|
return this.title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public finalize2fa() {
|
public finalize2fa(): void {
|
||||||
this.authService.finalize2fa(this.authCode).subscribe(response => {
|
this.authService.finalize2fa(this.authCode).subscribe(response => {
|
||||||
const verified = response?.two_factor_setup_complete ?? false;
|
const verified = response?.two_factor_setup_complete ?? false;
|
||||||
|
|
||||||
if (isTrue(verified)) {
|
if (isTrue(verified)) {
|
||||||
this.generateBackupCodes();
|
this.generateCodes();
|
||||||
this.message.addSuccessMessageByKey('LBL_FACTOR_AUTH_SUCCESS');
|
this.message.addSuccessMessageByKey('LBL_FACTOR_AUTH_SUCCESS');
|
||||||
|
|
||||||
this.isAppMethodEnabled.set(true);
|
this.isAppMethodEnabled.set(true);
|
||||||
|
@ -182,24 +196,36 @@ export class TwoFactorComponent implements OnInit {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
public copyBackupCodes() {
|
public copyBackupCodes(): void {
|
||||||
this.clipboard.copy(this.backupCodes);
|
this.clipboard.copy(this.backupCodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public generateCodes(): void {
|
||||||
public generateBackupCodes(){
|
|
||||||
this.areRecoveryCodesGenerated.set(false)
|
|
||||||
this.generateBackupCodesService.generate().subscribe({
|
this.generateBackupCodesService.generate().subscribe({
|
||||||
next: (response) => {
|
next: (response) => {
|
||||||
console.log(response);
|
|
||||||
console.log('inside next');
|
|
||||||
this.backupCodes = response?.data.backupCodes;
|
this.backupCodes = response?.data.backupCodes;
|
||||||
this.areRecoveryCodesGenerated.set(true)
|
this.areRecoveryCodesGenerated.set(true)
|
||||||
},
|
},
|
||||||
error: () => {
|
error: () => {
|
||||||
console.log('inside eror');
|
|
||||||
this.areRecoveryCodesGenerated.set(false)
|
this.areRecoveryCodesGenerated.set(false)
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public generateBackupCodes(): void {
|
||||||
|
|
||||||
|
const modal = this.modalService.open(TwoFactorCheckModalComponent, {size: 'lg'});
|
||||||
|
|
||||||
|
modal.result.then((result) => {
|
||||||
|
if (!result.two_factor_complete){
|
||||||
|
this.message.addDangerMessageByKey('LBL_FACTOR_AUTH_FAIL');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.areRecoveryCodesGenerated.set(false)
|
||||||
|
this.generateCodes()
|
||||||
|
}).catch();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,30 @@
|
||||||
|
.two-factor-popup {
|
||||||
|
.auth-input {
|
||||||
|
border: .03em solid $nepal-grey;
|
||||||
|
padding: .45em;
|
||||||
|
margin: 0 1em 0 0;
|
||||||
|
color: #666;
|
||||||
|
font-size: .8em;
|
||||||
|
background-color: #fff;
|
||||||
|
width: 30%;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 991px) {
|
||||||
|
.backup-codes-container {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(5, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
|
.backup-codes {
|
||||||
|
padding: 10px;
|
||||||
|
border: 1px solid lightgrey;
|
||||||
|
border-radius: .25rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 768px) {
|
@media (min-width: 768px) {
|
||||||
#two-factor {
|
#two-factor {
|
||||||
|
|
||||||
|
@ -8,6 +35,7 @@
|
||||||
width: fit-content;
|
width: fit-content;
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code-container {
|
.qr-code-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
padding: .25rem 1.5rem .5rem 1.5rem;
|
padding: .25rem 1.5rem .5rem 1.5rem;
|
||||||
|
@ -39,15 +67,17 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.backup-codes-container {
|
@media (max-width: 991px) {
|
||||||
display: grid;
|
.backup-codes-container {
|
||||||
grid-template-columns: repeat(5, 1fr);
|
display: grid;
|
||||||
gap: 10px;
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
gap: 10px;
|
||||||
|
|
||||||
.backup-codes {
|
.backup-codes {
|
||||||
padding: 10px;
|
padding: 10px;
|
||||||
border: 1px solid lightgrey;
|
border: 1px solid lightgrey;
|
||||||
border-radius: .25rem;
|
border-radius: .25rem;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -68,6 +98,7 @@
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code-container {
|
.qr-code-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -122,6 +153,7 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media(max-width: 374px) {
|
@media(max-width: 374px) {
|
||||||
#two-factor {
|
#two-factor {
|
||||||
|
|
||||||
|
@ -132,6 +164,7 @@
|
||||||
margin-left: 1.5rem;
|
margin-left: 1.5rem;
|
||||||
margin-right: 2rem;
|
margin-right: 2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.qr-code-container {
|
.qr-code-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|
130
core/backend/Process/LegacyHandler/CheckTwoFactorCodeHandler.php
Normal file
130
core/backend/Process/LegacyHandler/CheckTwoFactorCodeHandler.php
Normal file
|
@ -0,0 +1,130 @@
|
||||||
|
<?php
|
||||||
|
/**
|
||||||
|
* SuiteCRM is a customer relationship management program developed by SalesAgility Ltd.
|
||||||
|
* Copyright (C) 2024 SalesAgility Ltd.
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under
|
||||||
|
* the terms of the GNU Affero General Public License version 3 as published by the
|
||||||
|
* Free Software Foundation with the addition of the following permission added
|
||||||
|
* to Section 15 as permitted in Section 7(a): FOR ANY PART OF THE COVERED WORK
|
||||||
|
* IN WHICH THE COPYRIGHT IS OWNED BY SALESAGILITY, SALESAGILITY DISCLAIMS THE
|
||||||
|
* WARRANTY OF NON INFRINGEMENT OF THIRD PARTY RIGHTS.
|
||||||
|
*
|
||||||
|
* 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/>.
|
||||||
|
*
|
||||||
|
* In accordance with Section 7(b) of the GNU Affero General Public License
|
||||||
|
* version 3, these Appropriate Legal Notices must retain the display of the
|
||||||
|
* "Supercharged by SuiteCRM" logo. If the display of the logos is not reasonably
|
||||||
|
* feasible for technical reasons, the Appropriate Legal Notices must display
|
||||||
|
* the words "Supercharged by SuiteCRM".
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Process\LegacyHandler;
|
||||||
|
|
||||||
|
use ApiPlatform\Exception\InvalidArgumentException;
|
||||||
|
use App\Engine\LegacyHandler\LegacyHandler;
|
||||||
|
use App\Engine\LegacyHandler\LegacyScopeState;
|
||||||
|
use App\Process\Entity\Process;
|
||||||
|
use App\Process\Service\ProcessHandlerInterface;
|
||||||
|
use Scheb\TwoFactorBundle\Security\TwoFactor\Provider\Totp\TotpAuthenticatorInterface;
|
||||||
|
use Symfony\Bundle\SecurityBundle\Security;
|
||||||
|
use Symfony\Component\HttpFoundation\RequestStack;
|
||||||
|
|
||||||
|
class CheckTwoFactorCodeHandler extends LegacyHandler implements ProcessHandlerInterface
|
||||||
|
{
|
||||||
|
protected const MSG_OPTIONS_NOT_FOUND = 'Process options is not defined';
|
||||||
|
protected const PROCESS_TYPE = 'check-two-factor-code';
|
||||||
|
|
||||||
|
|
||||||
|
protected TotpAuthenticatorInterface $totpAuthenticator;
|
||||||
|
protected Security $security;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(
|
||||||
|
string $projectDir,
|
||||||
|
string $legacyDir,
|
||||||
|
string $legacySessionName,
|
||||||
|
string $defaultSessionName,
|
||||||
|
LegacyScopeState $legacyScopeState,
|
||||||
|
RequestStack $requestStack,
|
||||||
|
Security $security,
|
||||||
|
TotpAuthenticatorInterface $totpAuthenticator,
|
||||||
|
) {
|
||||||
|
parent::__construct(
|
||||||
|
$projectDir,
|
||||||
|
$legacyDir,
|
||||||
|
$legacySessionName,
|
||||||
|
$defaultSessionName,
|
||||||
|
$legacyScopeState,
|
||||||
|
$requestStack
|
||||||
|
);
|
||||||
|
$this->security = $security;
|
||||||
|
$this->totpAuthenticator = $totpAuthenticator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHandlerKey(): string
|
||||||
|
{
|
||||||
|
return self::PROCESS_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProcessType(): string
|
||||||
|
{
|
||||||
|
return self::PROCESS_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function requiredAuthRole(): string
|
||||||
|
{
|
||||||
|
return 'ROLE_USER';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRequiredACLs(Process $process): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function configure(Process $process): void
|
||||||
|
{
|
||||||
|
$process->setId(self::PROCESS_TYPE);
|
||||||
|
$process->setAsync(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validate(Process $process): void
|
||||||
|
{
|
||||||
|
if (empty($process->getOptions())) {
|
||||||
|
throw new InvalidArgumentException(self::MSG_OPTIONS_NOT_FOUND);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function run(Process $process): void
|
||||||
|
{
|
||||||
|
$options = $process->getOptions();
|
||||||
|
|
||||||
|
$authCode = $options['auth_code'] ?? false;
|
||||||
|
|
||||||
|
$user = $this->security->getToken()->getUser();
|
||||||
|
|
||||||
|
if (!$options['auth_code']) {
|
||||||
|
$process->setStatus('error');
|
||||||
|
$process->setMessages(['LBL_ACTION_ERROR']);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$correctCode = $this->totpAuthenticator->checkCode($user, $authCode);
|
||||||
|
|
||||||
|
if (!$correctCode){
|
||||||
|
$correctCode = $user->isBackupCode($authCode);
|
||||||
|
}
|
||||||
|
|
||||||
|
$response = ['two_factor_complete' => $correctCode];
|
||||||
|
|
||||||
|
$process->setData($response);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue