@@ -3,7 +3,7 @@ import {I18nService} from '@app/services/i18n';
33import { HttpService } from '@app/services/http' ;
44import { FaceService } from '@app/services/face' ;
55import { HttpErrorResponse } from '@angular/common/http' ;
6- import { Component , Inject , OnInit } from '@angular/core' ;
6+ import { Component , ElementRef , Inject , OnInit , ViewChild } from '@angular/core' ;
77import { Asset , ConnectData , ConnectionToken } from '@app/model' ;
88import { NzNotificationService } from 'ng-zorro-antd/notification' ;
99import { DomSanitizer , SafeResourceUrl } from '@angular/platform-browser' ;
@@ -32,12 +32,16 @@ interface DialogContent {
3232 styleUrls : [ 'acl-dialog.component.scss' ]
3333} )
3434export class ElementACLDialogComponent implements OnInit {
35+ @ViewChild ( 'otpCodeInput' , { static : false } ) otpCodeInput : ElementRef ;
3536 public asset : Asset ;
3637 public connectInfo : ConnectData ;
3738 public code : string ;
3839 public connectionToken : ConnectionToken = null ;
3940 public error : HttpErrorResponse ;
4041 public otherError : string ;
42+ public otpCode : string = '' ;
43+ public otpCodeError : string = '' ;
44+ public otpCodeSubmitting = false ;
4145 public ticketAssignees : string = '-' ;
4246 // Token 的行为,创建或者兑换 Token, create, exchange
4347 public tokenAction : string = 'create' ;
@@ -82,15 +86,43 @@ export class ElementACLDialogComponent implements OnInit {
8286 }
8387
8488 ngOnInit ( ) {
85- // 创建 Token 的时候,需要传入 Asset 和 ConnectInfo
86- this . content = this . getDialogContent ( this . data . code ) ;
8789 this . asset = this . data . asset ;
8890 this . connectInfo = this . data . connectInfo ;
89- this . code = this . data . code ;
91+ this . error = this . data . error ;
9092 // 兑换 Token 的时候,需要传入 Token ID
9193 this . tokenID = this . data . tokenID ;
9294 // 控制 token 的行为, 创建还是兑换
9395 this . tokenAction = this . data . tokenAction ;
96+ this . code = this . resolveDialogCode ( this . data . code , this . error ) ;
97+ this . content = this . getDialogContent ( this . code ) ;
98+ if ( this . code === 'otp_code_verify' ) {
99+ this . focusOTPCodeInput ( ) ;
100+ }
101+ }
102+
103+ onOTPCodeInput ( ) {
104+ this . otpCodeError = '' ;
105+ }
106+
107+ onConfirmOTPCode ( ) {
108+ const otpCode = ( this . otpCode || '' ) . trim ( ) ;
109+ if ( ! otpCode ) {
110+ this . otpCodeError = this . _i18n . instant ( 'Please input OTP code' ) ;
111+ this . focusOTPCodeInput ( ) ;
112+ return ;
113+ }
114+ this . otpCodeError = '' ;
115+ this . otpCodeSubmitting = true ;
116+ this . requestTokenWithOTPCode ( otpCode ) . subscribe (
117+ ( connToken : ConnectionToken ) => {
118+ this . otpCodeSubmitting = false ;
119+ this . dialogRef . close ( connToken ) ;
120+ } ,
121+ ( error : HttpErrorResponse ) => {
122+ this . otpCodeSubmitting = false ;
123+ this . applyDialogError ( error , true ) ;
124+ }
125+ ) ;
94126 }
95127
96128 async onCopySuccess ( evt ) {
@@ -272,6 +304,91 @@ export class ElementACLDialogComponent implements OnInit {
272304 this . dialogRef . close ( null ) ;
273305 }
274306
307+ private requestTokenWithOTPCode ( otpCode : string ) {
308+ if ( this . tokenAction === 'exchange' ) {
309+ return this . _http . exchangeConnectToken ( this . tokenID , false , false , undefined , otpCode ) ;
310+ }
311+ if ( this . data . connectData && this . data . connectData . direct ) {
312+ return this . _http . adminConnectToken (
313+ this . asset ,
314+ this . data . connectData ,
315+ false ,
316+ false ,
317+ undefined ,
318+ otpCode
319+ ) ;
320+ }
321+ return this . _http . createConnectToken ( this . asset , this . connectInfo , false , false , undefined , otpCode ) ;
322+ }
323+
324+ private resolveDialogCode ( code : string , error ?: HttpErrorResponse ) : string {
325+ if ( this . hasOTPCodeError ( error ) ) {
326+ return 'otp_code_verify' ;
327+ }
328+ return code || 'other' ;
329+ }
330+
331+ private hasOTPCodeError ( error ?: HttpErrorResponse ) : boolean {
332+ return ! ! ( error && error . error && typeof error . error === 'object' && error . error . otp_code ) ;
333+ }
334+
335+ private getFieldErrorMessage ( error : HttpErrorResponse , field : string ) : string {
336+ if ( ! error || ! error . error || typeof error . error !== 'object' ) {
337+ return '' ;
338+ }
339+ const value = error . error [ field ] ;
340+ if ( Array . isArray ( value ) ) {
341+ return value . join ( ' ' ) ;
342+ }
343+ return value || '' ;
344+ }
345+
346+ private getOtherErrorMessage ( error : HttpErrorResponse ) : string {
347+ if ( ! error ) {
348+ return '' ;
349+ }
350+ let value : any = error . error ;
351+ if ( ! value ) {
352+ return error . message ;
353+ }
354+ if ( typeof value === 'string' ) {
355+ return value ;
356+ }
357+ if ( Array . isArray ( value ) ) {
358+ return value . join ( ' ' ) ;
359+ }
360+ if ( value . detail ) {
361+ return value . detail ;
362+ }
363+ return JSON . stringify ( value ) ;
364+ }
365+
366+ private applyDialogError ( error : HttpErrorResponse , keepOTPCode = false ) {
367+ this . error = error ;
368+ this . data . error = error ;
369+ this . code = this . resolveDialogCode ( error ?. error ?. code , error ) ;
370+ if ( this . code === 'otp_code_verify' ) {
371+ this . otpCodeError = this . getFieldErrorMessage ( error , 'otp_code' ) ;
372+ if ( ! keepOTPCode ) {
373+ this . otpCode = '' ;
374+ }
375+ this . content = this . getDialogContent ( this . code ) ;
376+ this . focusOTPCodeInput ( ) ;
377+ return ;
378+ }
379+ this . otpCodeError = '' ;
380+ this . otherError = this . getOtherErrorMessage ( error ) ;
381+ this . content = this . getDialogContent ( this . code ) ;
382+ }
383+
384+ private focusOTPCodeInput ( ) {
385+ setTimeout ( ( ) => {
386+ if ( this . otpCodeInput && this . otpCodeInput . nativeElement ) {
387+ this . otpCodeInput . nativeElement . focus ( ) ;
388+ }
389+ } , 0 ) ;
390+ }
391+
275392 checkTicket ( ) {
276393 const checkMethod = this . connectionToken . from_ticket_info . check_ticket_api . method . toLowerCase ( ) ;
277394 const checkURL = this . connectionToken . from_ticket_info . check_ticket_api . url ;
@@ -343,6 +460,24 @@ export class ElementACLDialogComponent implements OnInit {
343460 }
344461 ]
345462 } ,
463+ otp_code_verify : {
464+ title : 'OTP verification' ,
465+ message : 'This account requires OTP code. Please input it to continue.' ,
466+ customContent : {
467+ type : 'otp_code'
468+ } ,
469+ actions : [
470+ {
471+ text : 'Cancel' ,
472+ callback : ( ) => vm . closeDialog ( )
473+ } ,
474+ {
475+ text : 'Confirm' ,
476+ type : 'primary' ,
477+ callback : ( ) => vm . onConfirmOTPCode ( )
478+ }
479+ ]
480+ } ,
346481 acl_face_online : {
347482 title : 'Login reminder' ,
348483 message : 'Face online required' ,
0 commit comments