From 75bf0409c47c3c04485c0a9ba4e76394916c4108 Mon Sep 17 00:00:00 2001 From: Fabi <38692350+fgerschwiler@users.noreply.github.com> Date: Tue, 24 Nov 2020 12:06:46 +0100 Subject: [PATCH] fix: management api remove otp (#1010) * fix: management api remove otp * add postinstall * remove mgmt otp Co-authored-by: Max Peintner --- .../user-mfa/user-mfa.component.html | 10 +++++ .../user-mfa/user-mfa.component.ts | 37 ++++++++++++++++++- console/src/app/services/mgmt.service.ts | 6 +++ internal/api/grpc/management/user.go | 5 +++ .../eventsourcing/eventstore/user.go | 4 ++ internal/management/repository/user.go | 1 + pkg/grpc/management/proto/management.proto | 10 +++++ 7 files changed, 71 insertions(+), 2 deletions(-) diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html index 61c2b662c5..eb234e45d8 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.html @@ -17,6 +17,16 @@ + + {{ 'USER.MFA.TABLEACTIONS' | translate }} + + + + + diff --git a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts index 00b2e10f76..e91c170d77 100644 --- a/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts +++ b/console/src/app/pages/users/user-detail/user-detail/user-mfa/user-mfa.component.ts @@ -1,9 +1,12 @@ import { Component, Input, OnDestroy, OnInit, ViewChild } from '@angular/core'; +import { MatDialog } from '@angular/material/dialog'; import { MatSort } from '@angular/material/sort'; import { MatTable, MatTableDataSource } from '@angular/material/table'; import { BehaviorSubject, Observable } from 'rxjs'; +import { WarnDialogComponent } from 'src/app/modules/warn-dialog/warn-dialog.component'; import { MFAState, MfaType, UserMultiFactor, UserView } from 'src/app/proto/generated/management_pb'; import { ManagementService } from 'src/app/services/mgmt.service'; +import { ToastService } from 'src/app/services/toast.service'; export interface MFAItem { @@ -17,7 +20,7 @@ export interface MFAItem { styleUrls: ['./user-mfa.component.scss'], }) export class UserMfaComponent implements OnInit, OnDestroy { - public displayedColumns: string[] = ['type', 'state']; + public displayedColumns: string[] = ['type', 'state', 'actions']; @Input() private user!: UserView.AsObject; public mfaSubject: BehaviorSubject = new BehaviorSubject([]); private loadingSubject: BehaviorSubject = new BehaviorSubject(false); @@ -31,7 +34,7 @@ export class UserMfaComponent implements OnInit, OnDestroy { public MFAState: any = MFAState; public error: string = ''; - constructor(private mgmtUserService: ManagementService) { } + constructor(private mgmtUserService: ManagementService, private dialog: MatDialog, private toast: ToastService) { } public ngOnInit(): void { this.getOTP(); @@ -50,4 +53,34 @@ export class UserMfaComponent implements OnInit, OnDestroy { this.error = error.message; }); } + + public deleteMFA(type: MfaType): void { + const dialogRef = this.dialog.open(WarnDialogComponent, { + data: { + confirmKey: 'ACTIONS.DELETE', + cancelKey: 'ACTIONS.CANCEL', + titleKey: 'USER.MFA.DIALOG.OTP_DELETE_TITLE', + descriptionKey: 'USER.MFA.DIALOG.OTP_DELETE_DESCRIPTION', + }, + width: '400px', + }); + + dialogRef.afterClosed().subscribe(resp => { + if (resp) { + if (type === MfaType.MFATYPE_OTP) { + this.mgmtUserService.removeMfaOTP(this.user.id).then(() => { + this.toast.showInfo('USER.TOAST.OTPREMOVED', true); + + const index = this.dataSource.data.findIndex(mfa => mfa.type === type); + if (index > -1) { + this.dataSource.data.splice(index, 1); + } + this.getOTP(); + }).catch(error => { + this.toast.showError(error); + }); + } + } + }); + } } diff --git a/console/src/app/services/mgmt.service.ts b/console/src/app/services/mgmt.service.ts index e3e61180b4..f003e5c456 100644 --- a/console/src/app/services/mgmt.service.ts +++ b/console/src/app/services/mgmt.service.ts @@ -677,6 +677,12 @@ export class ManagementService { return this.grpcService.mgmt.getUserMfas(req); } + public removeMfaOTP(id: string): Promise { + const req = new UserID(); + req.setId(id); + return this.grpcService.mgmt.removeMfaOTP(req); + } + public SaveUserProfile( id: string, firstName?: string, diff --git a/internal/api/grpc/management/user.go b/internal/api/grpc/management/user.go index f40f86317c..bf7f0abbd8 100644 --- a/internal/api/grpc/management/user.go +++ b/internal/api/grpc/management/user.go @@ -221,6 +221,11 @@ func (s *Server) GetUserMfas(ctx context.Context, userID *management.UserID) (*m return &management.UserMultiFactors{Mfas: mfasFromModel(mfas)}, nil } +func (s *Server) RemoveMfaOTP(ctx context.Context, userID *management.UserID) (*empty.Empty, error) { + err := s.user.RemoveOTP(ctx, userID.Id) + return &empty.Empty{}, err +} + func (s *Server) SearchUserMemberships(ctx context.Context, in *management.UserMembershipSearchRequest) (*management.UserMembershipSearchResponse, error) { request := userMembershipSearchRequestsToModel(in) request.AppendUserIDQuery(in.UserId) diff --git a/internal/management/repository/eventsourcing/eventstore/user.go b/internal/management/repository/eventsourcing/eventstore/user.go index c8579062ff..3ab8118411 100644 --- a/internal/management/repository/eventsourcing/eventstore/user.go +++ b/internal/management/repository/eventsourcing/eventstore/user.go @@ -223,6 +223,10 @@ func (repo *UserRepo) UserMfas(ctx context.Context, userID string) ([]*usr_model return []*usr_model.MultiFactor{{Type: usr_model.MfaTypeOTP, State: user.OTPState}}, nil } +func (repo *UserRepo) RemoveOTP(ctx context.Context, userID string) error { + return repo.UserEvents.RemoveOTP(ctx, userID) +} + func (repo *UserRepo) SetOneTimePassword(ctx context.Context, password *usr_model.Password) (*usr_model.Password, error) { policy, err := repo.View.PasswordComplexityPolicyByAggregateID(authz.GetCtxData(ctx).OrgID) if err != nil && caos_errs.IsNotFound(err) { diff --git a/internal/management/repository/user.go b/internal/management/repository/user.go index 58f4bdeda1..ae035368e7 100644 --- a/internal/management/repository/user.go +++ b/internal/management/repository/user.go @@ -31,6 +31,7 @@ type UserRepository interface { ChangeProfile(ctx context.Context, profile *model.Profile) (*model.Profile, error) UserMfas(ctx context.Context, userID string) ([]*model.MultiFactor, error) + RemoveOTP(ctx context.Context, userID string) error SearchExternalIDPs(ctx context.Context, request *model.ExternalIDPSearchRequest) (*model.ExternalIDPSearchResponse, error) RemoveExternalIDP(ctx context.Context, externalIDP *model.ExternalIDP) error diff --git a/pkg/grpc/management/proto/management.proto b/pkg/grpc/management/proto/management.proto index ceeece9f15..52f76c0611 100644 --- a/pkg/grpc/management/proto/management.proto +++ b/pkg/grpc/management/proto/management.proto @@ -388,6 +388,16 @@ rpc GetUserByID(UserID) returns (UserView) { }; } + rpc RemoveMfaOTP(UserID) returns (google.protobuf.Empty) { + option (google.api.http) = { + delete: "/users/{id}/mfas/otp" + }; + + option (caos.zitadel.utils.v1.auth_option) = { + permission: "user.write" + }; + } + // Sends an Notification (Email/SMS) with a password reset Link rpc SendSetPasswordNotification(SetPasswordNotificationRequest) returns (google.protobuf.Empty) { option (google.api.http) = {