Browse Source

UI: 2fa improvement mobile view; remove required maxVerificationFailuresBeforeUserLockout

pull/6235/head
Vladyslav_Prykhodko 4 years ago
parent
commit
67e969fd75
  1. 5
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html
  2. 9
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss
  3. 1
      ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts
  4. 20
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss
  5. 23
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html
  6. 23
      ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html
  7. 7
      ui-ngx/src/app/modules/home/pages/security/security.component.html
  8. 6
      ui-ngx/src/app/modules/home/pages/security/security.component.scss
  9. 22
      ui-ngx/src/app/modules/home/pages/security/security.component.ts
  10. 14
      ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss
  11. 15
      ui-ngx/src/app/shared/models/two-factor-auth.models.ts
  12. 7
      ui-ngx/src/assets/locale/locale.constant-en_US.json

5
ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.html

@ -140,10 +140,7 @@
</mat-form-field> </mat-form-field>
<mat-form-field fxFlex class="mat-block"> <mat-form-field fxFlex class="mat-block">
<mat-label translate>admin.2fa.max-verification-failures-before-user-lockout</mat-label> <mat-label translate>admin.2fa.max-verification-failures-before-user-lockout</mat-label>
<input matInput required formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535"> <input matInput formControlName="maxVerificationFailuresBeforeUserLockout" type="number" step="1" min="0" max="65535">
<mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('required')">
{{ 'admin.2fa.max-verification-failures-before-user-lockout-required' | translate }}
</mat-error>
<mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('pattern') <mat-error *ngIf="twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('pattern')
|| twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('min') || twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('min')
|| twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('max')"> || twoFaFormGroup.get('maxVerificationFailuresBeforeUserLockout').hasError('max')">

9
ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.scss

@ -16,8 +16,7 @@
@import "../../../../../scss/constants"; @import "../../../../../scss/constants";
:host{ :host {
mat-card.settings-card { mat-card.settings-card {
@media #{$mat-md} { @media #{$mat-md} {
width: 90%; width: 90%;
@ -48,17 +47,22 @@
.mat-expansion-panel { .mat-expansion-panel {
box-shadow: none; box-shadow: none;
margin: 1px 0 0; margin: 1px 0 0;
&.provider { &.provider {
overflow: inherit; overflow: inherit;
.mat-expansion-panel-header { .mat-expansion-panel-header {
padding: 0 24px 0 8px; padding: 0 24px 0 8px;
&.mat-expanded { &.mat-expanded {
height: 48px; height: 48px;
} }
.mat-slide-toggle { .mat-slide-toggle {
margin-right: 8px; margin-right: 8px;
} }
} }
.mat-expansion-panel-header-title { .mat-expansion-panel-header-title {
height: 40px; height: 40px;
} }
@ -73,6 +77,7 @@
.mat-expansion-panel-header > .mat-content { .mat-expansion-panel-header > .mat-content {
overflow: inherit; overflow: inherit;
} }
.mat-expansion-panel-body { .mat-expansion-panel-body {
padding: 0 16px 8px 8px; padding: 0 16px 8px 8px;
} }

1
ui-ngx/src/app/modules/home/pages/admin/two-factor-auth-settings.component.ts

@ -114,7 +114,6 @@ export class TwoFactorAuthSettingsComponent extends PageComponent implements OnI
private build2faSettingsForm(): void { private build2faSettingsForm(): void {
this.twoFaFormGroup = this.fb.group({ this.twoFaFormGroup = this.fb.group({
maxVerificationFailuresBeforeUserLockout: [30, [ maxVerificationFailuresBeforeUserLockout: [30, [
Validators.required,
Validators.pattern(/^\d*$/), Validators.pattern(/^\d*$/),
Validators.min(0), Validators.min(0),
Validators.max(65535) Validators.max(65535)

20
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/authentication-dialog.component.scss

@ -13,7 +13,9 @@
* See the License for the specific language governing permissions and * See the License for the specific language governing permissions and
* limitations under the License. * limitations under the License.
*/ */
:host{ @import "../../../../../../scss/constants";
:host {
.mat-toolbar > h2 { .mat-toolbar > h2 {
font-weight: 400; font-weight: 400;
letter-spacing: 0.25px; letter-spacing: 0.25px;
@ -30,9 +32,11 @@
&:not(:first-of-type) { &:not(:first-of-type) {
margin: 0 0 8px; margin: 0 0 8px;
} }
&.description { &.description {
margin: 0; margin: 0;
color: rgba(0, 0, 0, 0.54); color: rgba(0, 0, 0, 0.54);
&:last-of-type { &:last-of-type {
margin-bottom: 24px; margin-bottom: 24px;
} }
@ -45,6 +49,13 @@
.code-container { .code-container {
max-width: 170px; max-width: 170px;
&.full-width-xs {
@media #{$mat-xs} {
max-width: 100%;
width: 100%;
}
}
} }
.result-title { .result-title {
@ -63,6 +74,7 @@
.step-description { .step-description {
max-width: 450px; max-width: 450px;
&.input { &.input {
margin: 12px 0 0; margin: 12px 0 0;
} }
@ -78,26 +90,30 @@
.backup-code { .backup-code {
max-width: 500px; max-width: 500px;
.container { .container {
max-width: 500px; max-width: 500px;
margin: 40px 0 8px; margin: 40px 0 8px;
.code { .code {
letter-spacing: 0.25px; letter-spacing: 0.25px;
padding: 0 24px; padding: 0 24px;
margin-bottom: 16px; margin-bottom: 16px;
font-family: Roboto Mono, "Helvetica Neue", monospace; font-family: Roboto Mono, "Helvetica Neue", monospace;
&.even { &.even {
text-align: right; text-align: right;
} }
} }
} }
.action-buttons { .action-buttons {
margin-bottom: 40px; margin-bottom: 40px;
} }
} }
& ::ng-deep { & ::ng-deep {
.mat-horizontal-stepper-header{ .mat-horizontal-stepper-header {
pointer-events: none !important; pointer-events: none !important;
} }
} }

23
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.html

@ -64,8 +64,8 @@
<p class="mat-body step-description input"> <p class="mat-body step-description input">
{{ 'security.2fa.dialog.verification-step-description' | translate : {address: emailConfigForm.get('email').value} }} {{ 'security.2fa.dialog.verification-step-description' | translate : {address: emailConfigForm.get('email').value} }}
</p> </p>
<div fxLayout="row" fxLayoutAlign="space-between center"> <div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start end">
<mat-form-field fxFlex class="mat-block code-container" hideRequiredMarker floatLabel="always"> <mat-form-field fxFlex class="mat-block code-container full-width-xs" hideRequiredMarker floatLabel="always">
<mat-label></mat-label> <mat-label></mat-label>
<input matInput formControlName="verificationCode" <input matInput formControlName="verificationCode"
maxlength="6" type="text" required maxlength="6" type="text" required
@ -76,12 +76,19 @@
{{ 'security.2fa.dialog.verification-code-invalid' | translate }} {{ 'security.2fa.dialog.verification-code-invalid' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<button mat-raised-button <div fxLayoutGap="8px">
type="submit" <button mat-button
color="primary" matStepperPrevious
[disabled]="(isLoading$ | async) || emailVerificationForm.invalid"> [disabled]="(isLoading$ | async)">
{{ 'action.activate' | translate }} {{ 'action.back' | translate }}
</button> </button>
<button mat-raised-button
type="submit"
color="primary"
[disabled]="(isLoading$ | async) || emailVerificationForm.invalid">
{{ 'action.activate' | translate }}
</button>
</div>
</div> </div>
</form> </form>
</mat-step> </mat-step>

23
ui-ngx/src/app/modules/home/pages/security/authentication-dialog/sms-auth-dialog.component.html

@ -66,8 +66,8 @@
<p class="mat-body step-description input"> <p class="mat-body step-description input">
{{ 'security.2fa.dialog.verification-step-description' | translate : {address: smsConfigForm.get('phone').value} }} {{ 'security.2fa.dialog.verification-step-description' | translate : {address: smsConfigForm.get('phone').value} }}
</p> </p>
<div fxLayout="row" fxLayoutAlign="space-between center" fxLayoutGap="8px"> <div fxLayout="row" fxLayout.xs="column" fxLayoutAlign="space-between center" fxLayoutAlign.xs="start end">
<mat-form-field fxFlex class="mat-block code-container" floatLabel="always" hideRequiredMarker> <mat-form-field fxFlex class="mat-block code-container full-width-xs" floatLabel="always" hideRequiredMarker>
<mat-label></mat-label> <mat-label></mat-label>
<input matInput formControlName="verificationCode" <input matInput formControlName="verificationCode"
maxlength="6" type="text" required maxlength="6" type="text" required
@ -78,12 +78,19 @@
{{ 'security.2fa.dialog.verification-code-invalid' | translate }} {{ 'security.2fa.dialog.verification-code-invalid' | translate }}
</mat-error> </mat-error>
</mat-form-field> </mat-form-field>
<button mat-raised-button <div fxLayoutGap="8px">
type="submit" <button mat-button
color="primary" matStepperPrevious
[disabled]="(isLoading$ | async) || smsVerificationForm.invalid"> [disabled]="(isLoading$ | async)">
{{ 'action.activate' | translate }} {{ 'action.back' | translate }}
</button> </button>
<button mat-raised-button
type="submit"
color="primary"
[disabled]="(isLoading$ | async) || smsVerificationForm.invalid">
{{ 'action.activate' | translate }}
</button>
</div>
</div> </div>
</form> </form>
</mat-step> </mat-step>

7
ui-ngx/src/app/modules/home/pages/security/security.component.html

@ -56,9 +56,14 @@
<div class="provider"> <div class="provider">
<h4 class="provider-title">{{ providersData.get(provider).name | translate }}</h4> <h4 class="provider-title">{{ providersData.get(provider).name | translate }}</h4>
<div fxLayout="row" fxLayoutAlign="space-between start"> <div fxLayout="row" fxLayoutAlign="space-between start">
<div class="mat-body-1 description"> <div class="mat-body-1 description" *ngIf="!twoFactorAuth.get(provider).value; else providerInfo">
{{ providersData.get(provider).description | translate }} {{ providersData.get(provider).description | translate }}
</div> </div>
<ng-template #providerInfo>
<div class="mat-body-1 description">
{{ providersData.get(provider).activatedHint | translate: providerDataInfo(provider) }}
</div>
</ng-template>
<mat-slide-toggle [formControlName]="provider" <mat-slide-toggle [formControlName]="provider"
(click)="confirm2FAChange($event, provider)"> (click)="confirm2FAChange($event, provider)">
</mat-slide-toggle> </mat-slide-toggle>

6
ui-ngx/src/app/modules/home/pages/security/security.component.scss

@ -19,6 +19,7 @@
.profile-container { .profile-container {
padding: 8px; padding: 8px;
} }
mat-card.profile-card { mat-card.profile-card {
@media #{$mat-gt-sm} { @media #{$mat-gt-sm} {
width: 70%; width: 70%;
@ -29,16 +30,19 @@
@media #{$mat-gt-xl} { @media #{$mat-gt-xl} {
width: 45%; width: 45%;
} }
.mat-subheader { .mat-subheader {
line-height: 24px; line-height: 24px;
color: rgba(0,0,0,0.54); color: rgba(0, 0, 0, 0.54);
font-size: 14px; font-size: 14px;
font-weight: 400; font-weight: 400;
} }
.profile-last-login-ts { .profile-last-login-ts {
font-size: 16px; font-size: 16px;
font-weight: 400; font-weight: 400;
} }
.profile-btn-subtext { .profile-btn-subtext {
font: 400 14px / 16px Roboto, "Helvetica Neue", sans-serif; font: 400 14px / 16px Roboto, "Helvetica Neue", sans-serif;
letter-spacing: 0.25px; letter-spacing: 0.25px;

22
ui-ngx/src/app/modules/home/pages/security/security.component.ts

@ -30,6 +30,9 @@ import { ClipboardService } from 'ngx-clipboard';
import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service'; import { TwoFactorAuthenticationService } from '@core/http/two-factor-authentication.service';
import { import {
AccountTwoFaSettings, AccountTwoFaSettings,
BackupCodeTwoFactorAuthAccountConfig,
EmailTwoFactorAuthAccountConfig,
SmsTwoFactorAuthAccountConfig,
twoFactorAuthProvidersData, twoFactorAuthProvidersData,
TwoFactorAuthProviderType TwoFactorAuthProviderType
} from '@shared/models/two-factor-auth.models'; } from '@shared/models/two-factor-auth.models';
@ -231,4 +234,23 @@ export class SecurityComponent extends PageComponent implements OnInit, OnDestro
} }
}); });
} }
providerDataInfo(provider: TwoFactorAuthProviderType) {
const info = {info: null};
const providerConfig = this.accountConfig.configs[provider];
if (isDefinedAndNotNull(providerConfig)) {
switch (provider) {
case TwoFactorAuthProviderType.EMAIL:
info.info = (providerConfig as EmailTwoFactorAuthAccountConfig).email;
break;
case TwoFactorAuthProviderType.SMS:
info.info = (providerConfig as SmsTwoFactorAuthAccountConfig).phoneNumber;
break;
case TwoFactorAuthProviderType.BACKUP_CODE:
info.info = (providerConfig as BackupCodeTwoFactorAuthAccountConfig).codesLeft;
break;
}
}
return info;
}
} }

14
ui-ngx/src/app/modules/login/pages/login/two-factor-auth-login.component.scss

@ -18,8 +18,10 @@
:host { :host {
display: flex; display: flex;
flex: 1 1 0; flex: 1 1 0;
.tb-two-factor-auth-login-content { .tb-two-factor-auth-login-content {
background-color: #eee; background-color: #eee;
.tb-two-factor-auth-login-card { .tb-two-factor-auth-login-card {
padding: 48px 48px 48px 16px; padding: 48px 48px 48px 16px;
@ -27,7 +29,7 @@
width: 450px !important; width: 450px !important;
} }
.mat-card-title{ .mat-card-title {
font: 400 28px / 36px Roboto, "Helvetica Neue", sans-serif; font: 400 28px / 36px Roboto, "Helvetica Neue", sans-serif;
} }
@ -46,7 +48,7 @@
margin-top: 16px; margin-top: 16px;
} }
.providers-container{ .providers-container {
padding: 0; padding: 0;
.mat-body { .mat-body {
@ -61,19 +63,23 @@
} }
} }
} }
::ng-deep{
::ng-deep {
button.provider { button.provider {
text-align: start; text-align: start;
font-weight: 400; font-weight: 400;
&:not(.mat-button-disabled) { &:not(.mat-button-disabled) {
border-color: rgba(255, 255, 255, .8); border-color: rgba(255, 255, 255, .8);
} }
.icon{
.icon {
height: 18px; height: 18px;
width: 18px; width: 18px;
vertical-align: sub; vertical-align: sub;
} }
} }
.mat-form-field-invalid .mat-hint { .mat-form-field-invalid .mat-hint {
margin-top: 20px; margin-top: 20px;
} }

15
ui-ngx/src/app/shared/models/two-factor-auth.models.ts

@ -103,9 +103,10 @@ export interface TwoFaProviderInfo {
export interface TwoFactorAuthProviderData { export interface TwoFactorAuthProviderData {
name: string; name: string;
description: string; description: string;
activatedHint: string;
} }
export interface TwoFactorAuthProviderLoginData extends TwoFactorAuthProviderData { export interface TwoFactorAuthProviderLoginData extends Omit<TwoFactorAuthProviderData, 'activatedHint'> {
icon: string; icon: string;
placeholder: string; placeholder: string;
} }
@ -115,25 +116,29 @@ export const twoFactorAuthProvidersData = new Map<TwoFactorAuthProviderType, Two
[ [
TwoFactorAuthProviderType.TOTP, { TwoFactorAuthProviderType.TOTP, {
name: 'security.2fa.provider.totp', name: 'security.2fa.provider.totp',
description: 'security.2fa.provider.totp-description' description: 'security.2fa.provider.totp-description',
activatedHint: 'security.2fa.provider.totp-hint'
} }
], ],
[ [
TwoFactorAuthProviderType.SMS, { TwoFactorAuthProviderType.SMS, {
name: 'security.2fa.provider.sms', name: 'security.2fa.provider.sms',
description: 'security.2fa.provider.sms-description' description: 'security.2fa.provider.sms-description',
activatedHint: 'security.2fa.provider.sms-hint'
} }
], ],
[ [
TwoFactorAuthProviderType.EMAIL, { TwoFactorAuthProviderType.EMAIL, {
name: 'security.2fa.provider.email', name: 'security.2fa.provider.email',
description: 'security.2fa.provider.email-description' description: 'security.2fa.provider.email-description',
activatedHint: 'security.2fa.provider.email-hint'
} }
], ],
[ [
TwoFactorAuthProviderType.BACKUP_CODE, { TwoFactorAuthProviderType.BACKUP_CODE, {
name: 'security.2fa.provider.backup_code', name: 'security.2fa.provider.backup_code',
description: 'security.2fa.provider.backup-code-description' description: 'security.2fa.provider.backup-code-description',
activatedHint: 'security.2fa.provider.backup-code-hint'
} }
] ]
] ]

7
ui-ngx/src/assets/locale/locale.constant-en_US.json

@ -321,7 +321,6 @@
"issuer-name-required": "Issuer name is required.", "issuer-name-required": "Issuer name is required.",
"max-verification-failures-before-user-lockout": "Max verification failures before user lockout", "max-verification-failures-before-user-lockout": "Max verification failures before user lockout",
"max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.", "max-verification-failures-before-user-lockout-pattern": "Max verification failures must be a positive integer.",
"max-verification-failures-before-user-lockout-required": "Max verification failures is required.",
"number-of-checking-attempts": "Number of checking attempts", "number-of-checking-attempts": "Number of checking attempts",
"number-of-checking-attempts-pattern": "Number of checking attempts must be a positive integer.", "number-of-checking-attempts-pattern": "Number of checking attempts must be a positive integer.",
"number-of-checking-attempts-required": "Number of checking attempts is required.", "number-of-checking-attempts-required": "Number of checking attempts is required.",
@ -2624,12 +2623,16 @@
"provider": { "provider": {
"email": "Email", "email": "Email",
"email-description": "Use a security code sent to your email address to authenticate.", "email-description": "Use a security code sent to your email address to authenticate.",
"email-hint": "Authentication codes are sent via email to {{ info }}",
"sms": "SMS", "sms": "SMS",
"sms-description": "Use your phone to authenticate. We'll send you a security code via SMS message when you log in.", "sms-description": "Use your phone to authenticate. We'll send you a security code via SMS message when you log in.",
"sms-hint": "Authentication codes are sent by text message to {{ info }}",
"totp": "Authenticator app", "totp": "Authenticator app",
"totp-description": "Use apps like Google Authenticator, Authy, or Duo on your phone to authenticate. It will generate a security code for logging in.", "totp-description": "Use apps like Google Authenticator, Authy, or Duo on your phone to authenticate. It will generate a security code for logging in.",
"totp-hint": "Authenticator app is set up for your account",
"backup_code": "Backup code", "backup_code": "Backup code",
"backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling." "backup-code-description": "These printable one-time passcodes allow you to sign in when away from your phone, like when you’re traveling.",
"backup-code-hint": "{{ info }} single-use codes are active at this time"
} }
} }
}, },

Loading…
Cancel
Save