From c324a71e910b519d3cbc39922b03dd01d7855e86 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Fri, 15 Nov 2024 07:42:10 -0800 Subject: [PATCH 1/9] Fix: include db_index caveat in squashed migrations (#8292) --- ...dd_insensitive_to_match_squashed_0018_auto_20170715_1712.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/documents/migrations/0015_add_insensitive_to_match_squashed_0018_auto_20170715_1712.py b/src/documents/migrations/0015_add_insensitive_to_match_squashed_0018_auto_20170715_1712.py index 4463a953d..bfa897601 100644 --- a/src/documents/migrations/0015_add_insensitive_to_match_squashed_0018_auto_20170715_1712.py +++ b/src/documents/migrations/0015_add_insensitive_to_match_squashed_0018_auto_20170715_1712.py @@ -1,6 +1,7 @@ # Generated by Django 4.2.13 on 2024-06-28 17:57 import django.db.models.deletion +from django.conf import settings from django.db import migrations from django.db import models @@ -43,7 +44,7 @@ class Migration(migrations.Migration): name="content", field=models.TextField( blank=True, - db_index=True, + db_index=("mysql" not in settings.DATABASES["default"]["ENGINE"]), help_text="The raw, text-only data of the document. This field is primarily used for searching.", ), ), From 6c3d6d562d9fcc1dc04ae02d115cf8eb24f0cccc Mon Sep 17 00:00:00 2001 From: ftibi93 Date: Sat, 16 Nov 2024 05:19:18 +0100 Subject: [PATCH 2/9] Documentation: fix docker-compose.portainer.yml GID (#8273) --- docker/compose/docker-compose.portainer.yml | 26 ++++----------------- 1 file changed, 4 insertions(+), 22 deletions(-) diff --git a/docker/compose/docker-compose.portainer.yml b/docker/compose/docker-compose.portainer.yml index 621908ff8..588a93c84 100644 --- a/docker/compose/docker-compose.portainer.yml +++ b/docker/compose/docker-compose.portainer.yml @@ -19,6 +19,8 @@ # # - Open portainer Stacks list and click 'Add stack' # - Paste the contents of this file and assign a name, e.g. 'paperless' +# - Upload 'docker-compose.env' by clicking on 'Load variables from .env file' +# - Modify the environment variables as needed # - Click 'Deploy the stack' and wait for it to be deployed # - Open the list of containers, select paperless_webserver_1 # - Click 'Console' and then 'Connect' to open the command line inside the container @@ -61,28 +63,8 @@ services: environment: PAPERLESS_REDIS: redis://broker:6379 PAPERLESS_DBHOST: db -# The UID and GID of the user used to run paperless in the container. Set this -# to your UID and GID on the host so that you have write access to the -# consumption directory. - USERMAP_UID: 1000 - USERMAP_GID: 100 -# Additional languages to install for text recognition, separated by a -# whitespace. Note that this is -# different from PAPERLESS_OCR_LANGUAGE (default=eng), which defines the -# language used for OCR. -# The container installs English, German, Italian, Spanish and French by -# default. -# See https://packages.debian.org/search?keywords=tesseract-ocr-&searchon=names&suite=buster -# for available languages. - #PAPERLESS_OCR_LANGUAGES: tur ces -# Adjust this key if you plan to make paperless available publicly. It should -# be a very long sequence of random characters. You don't need to remember it. - #PAPERLESS_SECRET_KEY: change-me -# Use this variable to set a timezone for the Paperless Docker containers. If not specified, defaults to UTC. - #PAPERLESS_TIME_ZONE: America/Los_Angeles -# The default language to use for OCR. Set this to the language most of your -# documents are written in. - #PAPERLESS_OCR_LANGUAGE: eng + env_file: + - stack.env volumes: data: From e94a92ed595f5d8035c88673d083f60f875a66a7 Mon Sep 17 00:00:00 2001 From: shamoon <4887959+shamoon@users.noreply.github.com> Date: Mon, 18 Nov 2024 10:34:46 -0800 Subject: [PATCH 3/9] Feature: two-factor authentication (#8012) --- Pipfile | 2 +- Pipfile.lock | 17 +- docs/usage.md | 6 + src-ui/messages.xlf | 227 ++++++++++++++--- .../app-frame/app-frame.component.spec.ts | 1 + .../app-frame/app-frame.component.ts | 1 + .../user-edit-dialog.component.html | 14 + .../user-edit-dialog.component.spec.ts | 43 +++- .../user-edit-dialog.component.ts | 33 ++- .../profile-edit-dialog.component.html | 241 ++++++++++++------ .../profile-edit-dialog.component.spec.ts | 81 ++++++ .../profile-edit-dialog.component.ts | 93 ++++++- src-ui/src/app/data/user-profile.ts | 7 + src-ui/src/app/data/user.ts | 1 + .../app/services/permissions.service.spec.ts | 21 ++ .../src/app/services/permissions.service.ts | 4 + .../src/app/services/profile.service.spec.ts | 28 ++ src-ui/src/app/services/profile.service.ts | 27 ++ .../app/services/rest/user.service.spec.ts | 28 +- src-ui/src/app/services/rest/user.service.ts | 10 +- .../management/commands/document_exporter.py | 2 + src/documents/templates/mfa/authenticate.html | 35 +++ src/documents/tests/test_api_permissions.py | 54 ++++ src/documents/tests/test_api_profile.py | 80 ++++++ src/locale/en_US/LC_MESSAGES/django.po | 89 ++++--- src/paperless/serialisers.py | 13 + src/paperless/settings.py | 3 + src/paperless/urls.py | 45 +++- src/paperless/views.py | 97 +++++++ 29 files changed, 1128 insertions(+), 175 deletions(-) create mode 100644 src/documents/templates/mfa/authenticate.html diff --git a/Pipfile b/Pipfile index 794af014d..1e79bb604 100644 --- a/Pipfile +++ b/Pipfile @@ -8,7 +8,7 @@ dateparser = "~=1.2" # WARNING: django does not use semver. # Only patch versions are guaranteed to not introduce breaking changes. django = "~=5.1.3" -django-allauth = {extras = ["socialaccount"], version = "*"} +django-allauth = {extras = ["mfa", "socialaccount"], version = "*"} django-auditlog = "*" django-celery-results = "*" django-compression-middleware = "*" diff --git a/Pipfile.lock b/Pipfile.lock index 9377e3575..063ec2389 100644 --- a/Pipfile.lock +++ b/Pipfile.lock @@ -1,7 +1,7 @@ { "_meta": { "hash": { - "sha256": "dccf58aea1ba4c0aa4aa93c1cc13881229889db25bc6e5b2384413a7e7e85182" + "sha256": "5a7cb70103e8f3931682c73432290f2f4ec2ba06395c8ec076d2d5449c4ff0dd" }, "pipfile-spec": 6, "requires": {}, @@ -522,6 +522,7 @@ }, "django-allauth": { "extras": [ + "mfa", "socialaccount" ], "hashes": [ @@ -641,6 +642,13 @@ "markers": "python_version >= '3.7'", "version": "==1.2.2" }, + "fido2": { + "hashes": [ + "sha256:26100f226d12ced621ca6198528ce17edf67b78df4287aee1285fee3cd5aa9fc", + "sha256:6be34c0b9fe85e4911fd2d103cce7ae8ce2f064384a7a2a3bd970b3ef7702931" + ], + "version": "==1.1.3" + }, "filelock": { "hashes": [ "sha256:2082e5703d51fbf98ea75855d9d5527e33d8ff23099bec374a134febee6946b0", @@ -1776,6 +1784,13 @@ "index": "pypi", "version": "==0.1.9" }, + "qrcode": { + "hashes": [ + "sha256:025ce2b150f7fe4296d116ee9bad455a6643ab4f6e7dce541613a4758cbce347", + "sha256:9fc05f03305ad27a709eb742cf3097fa19e6f6f93bb9e2f039c0979190f6f1b1" + ], + "version": "==8.0" + }, "rapidfuzz": { "hashes": [ "sha256:00d02cbd75d283c287471b5b3738b3e05c9096150f93f2d2dfa10b3d700f2db9", diff --git a/docs/usage.md b/docs/usage.md index f853bb7f5..8f22ec3eb 100644 --- a/docs/usage.md +++ b/docs/usage.md @@ -299,6 +299,12 @@ In order to enable the password reset feature you will need to setup an SMTP bac [`PAPERLESS_EMAIL_HOST`](configuration.md#PAPERLESS_EMAIL_HOST). If your installation does not have [`PAPERLESS_URL`](configuration.md#PAPERLESS_URL) set, the reset link included in emails will use the server host. +### Two-factor authentication + +Users can enable two-factor authentication (2FA) for their accounts from the 'My Profile' dialog. Opening the dropdown reveals a QR code that can be scanned by a 2FA app (e.g. Google Authenticator) to generate a code. The code must then be entered in the dialog to enable 2FA. If the code is accepted and 2FA is enabled, the user will be shown a set of 10 recovery codes that can be used to login in the event that the 2FA device is lost or unavailable. These codes should be stored securely and cannot be retrieved again. Once enabled, users will be required to enter a code from their 2FA app when logging in. + +Should a user lose access to their 2FA device and all recovery codes, a superuser can disable 2FA for the user from the 'Users & Groups' management screen. + ## Workflows !!! note diff --git a/src-ui/messages.xlf b/src-ui/messages.xlf index b9aa4e03e..e988a39cb 100644 --- a/src-ui/messages.xlf +++ b/src-ui/messages.xlf @@ -520,6 +520,10 @@ src/app/components/admin/config/config.component.html 34 + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 124 + Discard @@ -576,7 +580,7 @@ src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html - 43 + 57 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -584,7 +588,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 99 + 184 src/app/components/document-detail/document-detail.component.html @@ -712,6 +716,14 @@ src/app/components/common/permissions-dialog/permissions-dialog.component.html 23 + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 111 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 127 + src/app/components/common/system-status-dialog/system-status-dialog.component.html 10 @@ -1095,7 +1107,7 @@ src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html - 37 + 51 src/app/components/common/input/permissions/permissions-form/permissions-form.component.html @@ -1707,7 +1719,7 @@ src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html - 42 + 56 src/app/components/common/edit-dialog/workflow-edit-dialog/workflow-edit-dialog.component.html @@ -1719,7 +1731,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 98 + 183 src/app/components/common/select-dialog/select-dialog.component.html @@ -2514,7 +2526,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 159 + 173 @@ -2917,21 +2929,21 @@ Sidebar views updated src/app/components/app-frame/app-frame.component.ts - 208 + 209 Error updating sidebar views src/app/components/app-frame/app-frame.component.ts - 211 + 212 An error occurred while saving update checking settings. src/app/components/app-frame/app-frame.component.ts - 232 + 233 @@ -3720,7 +3732,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 18 + 20 @@ -4263,7 +4275,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 8 + 10 @@ -4274,7 +4286,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 28 + 30 @@ -4285,7 +4297,7 @@ src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 29 + 31 @@ -4323,18 +4335,70 @@ 30 + + Two-factor Authentication + + src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html + 37 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 104 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 138 + + + + Disable Two-factor Authentication + + src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html + 39 + + + src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html + 41 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 169 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 171 + + Create new user account src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 44 + 49 Edit user account src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts - 48 + 53 + + + + Totp deactivated + + src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts + 109 + + + + Totp deactivation failed + + src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts + 112 + + + src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts + 117 @@ -5151,32 +5215,36 @@ Confirm Email src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 13 + 15 Confirm Password src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 23 + 25 API Auth Token src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 31 + 33 Copy src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 35 + 37 src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 42 + 44 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 156 src/app/components/common/share-links-dropdown/share-links-dropdown.component.html @@ -5207,14 +5275,18 @@ Regenerate auth token src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 45 + 47 Copied! src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 53 + 55 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 163 src/app/components/common/share-links-dropdown/share-links-dropdown.component.html @@ -5225,91 +5297,176 @@ Warning: changing the token cannot be undone src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 55 + 57 Connected social accounts src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 59 + 63 Set a password before disconnecting social account. src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 63 + 67 Disconnect src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 69 + 73 Disconnect social account src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 71 + 75 Warning: disconnecting social accounts cannot be undone src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 81 + 85 Connect new social account src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html - 86 + 90 + + + + Scan the QR code with your authenticator app and then enter the code below + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 115 + + + + Authenticator secret + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 118 + + + + You can store this secret and use it to reinstall your authenticator app at a later time. + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 119 + + + + Code + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 122 + + + + Recovery codes will not be shown again, make sure to save them. + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 141 + + + + Copy codes + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html + 159 Emails must match src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 108 + 121 Passwords must match src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 136 + 149 Profile updated successfully src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 156 + 170 Error saving profile src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 168 + 182 Error generating auth token src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 185 + 199 Error disconnecting social account src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts - 210 + 224 + + + + Error fetching TOTP settings + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 243 + + + + TOTP activated successfully + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 263 + + + + Error activating TOTP + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 265 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 271 + + + + TOTP deactivated successfully + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 287 + + + + Error deactivating TOTP + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 289 + + + src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.ts + 294 diff --git a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts index 1354a187e..f440946da 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.spec.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.spec.ts @@ -343,6 +343,7 @@ describe('AppFrameComponent', () => { component.editProfile() expect(modalSpy).toHaveBeenCalledWith(ProfileEditDialogComponent, { backdrop: 'static', + size: 'xl', }) }) diff --git a/src-ui/src/app/components/app-frame/app-frame.component.ts b/src-ui/src/app/components/app-frame/app-frame.component.ts index df6ac65a2..83d927562 100644 --- a/src-ui/src/app/components/app-frame/app-frame.component.ts +++ b/src-ui/src/app/components/app-frame/app-frame.component.ts @@ -136,6 +136,7 @@ export class AppFrameComponent editProfile() { this.modalService.open(ProfileEditDialogComponent, { backdrop: 'static', + size: 'xl', }) this.closeMenu() } diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html index ca834a3ad..a2b3db67d 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.html @@ -32,6 +32,20 @@ + + @if (object?.is_mfa_enabled && currentUserIsSuperUser) { + + + + }
diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts index 96a0044fe..5adaf3388 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.spec.ts @@ -7,7 +7,7 @@ import { } from '@angular/forms' import { NgbActiveModal, NgbModule } from '@ng-bootstrap/ng-bootstrap' import { NgSelectModule } from '@ng-select/ng-select' -import { of } from 'rxjs' +import { of, throwError } from 'rxjs' import { IfOwnerDirective } from 'src/app/directives/if-owner.directive' import { IfPermissionsDirective } from 'src/app/directives/if-permissions.directive' import { GroupService } from 'src/app/services/rest/group.service' @@ -21,10 +21,15 @@ import { EditDialogMode } from '../edit-dialog.component' import { UserEditDialogComponent } from './user-edit-dialog.component' import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http' import { NgxBootstrapIconsModule, allIcons } from 'ngx-bootstrap-icons' +import { ToastService } from 'src/app/services/toast.service' +import { UserService } from 'src/app/services/rest/user.service' +import { PermissionsService } from 'src/app/services/permissions.service' describe('UserEditDialogComponent', () => { let component: UserEditDialogComponent let settingsService: SettingsService + let permissionsService: PermissionsService + let toastService: ToastService let fixture: ComponentFixture beforeEach(async () => { @@ -71,6 +76,8 @@ describe('UserEditDialogComponent', () => { fixture = TestBed.createComponent(UserEditDialogComponent) settingsService = TestBed.inject(SettingsService) settingsService.currentUser = { id: 99, username: 'user99' } + permissionsService = TestBed.inject(PermissionsService) + toastService = TestBed.inject(ToastService) component = fixture.componentInstance fixture.detectChanges() @@ -121,4 +128,38 @@ describe('UserEditDialogComponent', () => { component.save() expect(component.passwordIsSet).toBeTruthy() }) + + it('should support deactivation of TOTP', () => { + component.object = { id: 99, username: 'user99' } + const deactivateSpy = jest.spyOn( + component['service'] as UserService, + 'deactivateTotp' + ) + const toastErrorSpy = jest.spyOn(toastService, 'showError') + const toastInfoSpy = jest.spyOn(toastService, 'showInfo') + deactivateSpy.mockReturnValueOnce(throwError(() => new Error('error'))) + component.deactivateTotp() + expect(deactivateSpy).toHaveBeenCalled() + expect(toastErrorSpy).toHaveBeenCalled() + + deactivateSpy.mockReturnValueOnce(of(false)) + component.deactivateTotp() + expect(deactivateSpy).toHaveBeenCalled() + expect(toastErrorSpy).toHaveBeenCalled() + + deactivateSpy.mockReturnValueOnce(of(true)) + component.deactivateTotp() + expect(deactivateSpy).toHaveBeenCalled() + expect(toastInfoSpy).toHaveBeenCalled() + }) + + it('should check superuser status of current user', () => { + expect(component.currentUserIsSuperUser).toBeFalsy() + permissionsService.initialize([], { + id: 99, + username: 'user99', + is_superuser: true, + }) + expect(component.currentUserIsSuperUser).toBeTruthy() + }) }) diff --git a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts index baadfa541..acd327d3a 100644 --- a/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts +++ b/src-ui/src/app/components/common/edit-dialog/user-edit-dialog/user-edit-dialog.component.ts @@ -5,9 +5,11 @@ import { first } from 'rxjs' import { EditDialogComponent } from 'src/app/components/common/edit-dialog/edit-dialog.component' import { Group } from 'src/app/data/group' import { User } from 'src/app/data/user' +import { PermissionsService } from 'src/app/services/permissions.service' import { GroupService } from 'src/app/services/rest/group.service' import { UserService } from 'src/app/services/rest/user.service' import { SettingsService } from 'src/app/services/settings.service' +import { ToastService } from 'src/app/services/toast.service' @Component({ selector: 'pngx-user-edit-dialog', @@ -20,12 +22,15 @@ export class UserEditDialogComponent { groups: Group[] passwordIsSet: boolean = false + public totpLoading: boolean = false constructor( service: UserService, activeModal: NgbActiveModal, groupsService: GroupService, - settingsService: SettingsService + settingsService: SettingsService, + private toastService: ToastService, + private permissionsService: PermissionsService ) { super(service, activeModal, service, settingsService) @@ -87,4 +92,30 @@ export class UserEditDialogComponent .length > 0 super.save() } + + get currentUserIsSuperUser(): boolean { + return this.permissionsService.isSuperUser() + } + + deactivateTotp() { + this.totpLoading = true + ;(this.service as UserService) + .deactivateTotp(this.object) + .pipe(first()) + .subscribe({ + next: (result) => { + this.totpLoading = false + if (result) { + this.toastService.showInfo($localize`Totp deactivated`) + this.object.is_mfa_enabled = false + } else { + this.toastService.showError($localize`Totp deactivation failed`) + } + }, + error: (e) => { + this.totpLoading = false + this.toastService.showError($localize`Totp deactivation failed`, e) + }, + }) + } } diff --git a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html index 713d68864..f9d57baf3 100644 --- a/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html +++ b/src-ui/src/app/components/common/profile-edit-dialog/profile-edit-dialog.component.html @@ -5,94 +5,179 @@