Merge security functionality
This commit is contained in:
		
							parent
							
								
									05f0619a2d
								
							
						
					
					
						commit
						5531f92c6a
					
				@ -112,8 +112,11 @@ public class DefaultMailService implements MailService {
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", enableTls);
 | 
			
		||||
        if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) {
 | 
			
		||||
            javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText());
 | 
			
		||||
        if (enableTls && jsonConfig.has("tlsVersion") && !jsonConfig.get("tlsVersion").isNull()) {
 | 
			
		||||
            String tlsVersion = jsonConfig.get("tlsVersion").asText();
 | 
			
		||||
            if (StringUtils.isNoneEmpty(tlsVersion)) {
 | 
			
		||||
                javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", tlsVersion);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return javaMailProperties;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@ -96,6 +96,10 @@ export class AlarmService {
 | 
			
		||||
    return this.http.post<void>(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public deleteAlarm(alarmId: string, config?: RequestConfig): Observable<void> {
 | 
			
		||||
    return this.http.delete<void>(`/api/alarm/${alarmId}`, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public getAlarms(query: AlarmQuery,
 | 
			
		||||
                   config?: RequestConfig): Observable<PageData<AlarmInfo>> {
 | 
			
		||||
    return this.http.get<PageData<AlarmInfo>>(`/api/alarm${query.toQuery()}`,
 | 
			
		||||
 | 
			
		||||
@ -21,6 +21,7 @@ import { Observable } from 'rxjs';
 | 
			
		||||
import { HttpClient } from '@angular/common/http';
 | 
			
		||||
import { PageLink } from '@shared/models/page/page-link';
 | 
			
		||||
import { PageData } from '@shared/models/page/page-data';
 | 
			
		||||
import { isDefined } from '@core/utils';
 | 
			
		||||
 | 
			
		||||
@Injectable({
 | 
			
		||||
  providedIn: 'root'
 | 
			
		||||
@ -67,4 +68,12 @@ export class UserService {
 | 
			
		||||
    return this.http.post(`/api/user/sendActivationMail?email=${email}`, null, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public setUserCredentialsEnabled(userId: string, userCredentialsEnabled?: boolean, config?: RequestConfig): Observable<any> {
 | 
			
		||||
    let url = `/api/user/${userId}/userCredentialsEnabled`;
 | 
			
		||||
    if (isDefined(userCredentialsEnabled)) {
 | 
			
		||||
      url += `?userCredentialsEnabled=${userCredentialsEnabled}`;
 | 
			
		||||
    }
 | 
			
		||||
    return this.http.post<User>(url, null, defaultHttpOptionsFromConfig(config));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -76,9 +76,17 @@
 | 
			
		||||
              {{ 'admin.timeout-invalid' | translate }}
 | 
			
		||||
            </mat-error>
 | 
			
		||||
          </mat-form-field>
 | 
			
		||||
          <tb-checkbox formControlName="enableTls" trueValue="true" falseValue="false">
 | 
			
		||||
          <tb-checkbox formControlName="enableTls" trueValue="true" falseValue="false" style="display: block; padding-bottom: 16px;">
 | 
			
		||||
            {{ 'admin.enable-tls' | translate }}
 | 
			
		||||
          </tb-checkbox>
 | 
			
		||||
          <mat-form-field class="mat-block" *ngIf="mailSettings.get('enableTls').value === 'true'">
 | 
			
		||||
            <mat-label translate>admin.tls-version</mat-label>
 | 
			
		||||
            <mat-select formControlName="tlsVersion">
 | 
			
		||||
              <mat-option *ngFor="let tlsVersion of tlsVersions" [value]="tlsVersion">
 | 
			
		||||
                {{ tlsVersion }}
 | 
			
		||||
              </mat-option>
 | 
			
		||||
            </mat-select>
 | 
			
		||||
          </mat-form-field>
 | 
			
		||||
          <mat-form-field class="mat-block">
 | 
			
		||||
            <mat-label translate>common.username</mat-label>
 | 
			
		||||
            <input matInput formControlName="username" placeholder="{{ 'common.enter-username' | translate }}"/>
 | 
			
		||||
 | 
			
		||||
@ -37,6 +37,8 @@ export class MailServerComponent extends PageComponent implements OnInit, HasCon
 | 
			
		||||
  adminSettings: AdminSettings<MailServerSettings>;
 | 
			
		||||
  smtpProtocols = ['smtp', 'smtps'];
 | 
			
		||||
 | 
			
		||||
  tlsVersions = ['TLSv1.0', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'];
 | 
			
		||||
 | 
			
		||||
  constructor(protected store: Store<AppState>,
 | 
			
		||||
              private router: Router,
 | 
			
		||||
              private adminService: AdminService,
 | 
			
		||||
@ -67,6 +69,7 @@ export class MailServerComponent extends PageComponent implements OnInit, HasCon
 | 
			
		||||
        Validators.pattern(/^[0-9]{1,6}$/),
 | 
			
		||||
        Validators.maxLength(6)]],
 | 
			
		||||
      enableTls: ['false'],
 | 
			
		||||
      tlsVersion: [],
 | 
			
		||||
      username: [''],
 | 
			
		||||
      password: ['']
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -30,6 +30,28 @@
 | 
			
		||||
    <mat-card-content style="padding-top: 16px;">
 | 
			
		||||
      <form #securitySettingsForm="ngForm" [formGroup]="securitySettingsFormGroup" (ngSubmit)="save()">
 | 
			
		||||
        <fieldset [disabled]="isLoading$ | async">
 | 
			
		||||
          <mat-expansion-panel [expanded]="true">
 | 
			
		||||
            <mat-expansion-panel-header>
 | 
			
		||||
              <mat-panel-title>
 | 
			
		||||
                <div class="tb-panel-title" translate>admin.general-policy</div>
 | 
			
		||||
              </mat-panel-title>
 | 
			
		||||
            </mat-expansion-panel-header>
 | 
			
		||||
            <mat-form-field class="mat-block">
 | 
			
		||||
              <mat-label translate>admin.max-failed-login-attempts</mat-label>
 | 
			
		||||
              <input matInput type="number"
 | 
			
		||||
                     formControlName="maxFailedLoginAttempts"
 | 
			
		||||
                     step="1"
 | 
			
		||||
                     min="0"/>
 | 
			
		||||
              <mat-error *ngIf="securitySettingsFormGroup.get('maxFailedLoginAttempts').hasError('min')">
 | 
			
		||||
                {{ 'admin.minimum-max-failed-login-attempts-range' | translate }}
 | 
			
		||||
              </mat-error>
 | 
			
		||||
            </mat-form-field>
 | 
			
		||||
            <mat-form-field class="mat-block">
 | 
			
		||||
              <mat-label translate>admin.user-lockout-notification-email</mat-label>
 | 
			
		||||
              <input matInput type="email"
 | 
			
		||||
                     formControlName="userLockoutNotificationEmail"/>
 | 
			
		||||
            </mat-form-field>
 | 
			
		||||
          </mat-expansion-panel>
 | 
			
		||||
          <mat-expansion-panel [expanded]="true">
 | 
			
		||||
            <mat-expansion-panel-header>
 | 
			
		||||
              <mat-panel-title>
 | 
			
		||||
@ -105,6 +127,16 @@
 | 
			
		||||
                  {{ 'admin.password-expiration-period-days-range' | translate }}
 | 
			
		||||
                </mat-error>
 | 
			
		||||
              </mat-form-field>
 | 
			
		||||
              <mat-form-field class="mat-block">
 | 
			
		||||
                <mat-label translate>admin.password-reuse-frequency-days</mat-label>
 | 
			
		||||
                <input matInput type="number"
 | 
			
		||||
                       formControlName="passwordReuseFrequencyDays"
 | 
			
		||||
                       step="1"
 | 
			
		||||
                       min="0"/>
 | 
			
		||||
                <mat-error *ngIf="securitySettingsFormGroup.get('passwordPolicy').get('passwordReuseFrequencyDays').hasError('min')">
 | 
			
		||||
                  {{ 'admin.password-reuse-frequency-days-range' | translate }}
 | 
			
		||||
                </mat-error>
 | 
			
		||||
              </mat-form-field>
 | 
			
		||||
            </section>
 | 
			
		||||
          </mat-expansion-panel>
 | 
			
		||||
          <div fxLayout="row" fxLayoutAlign="end center" style="width: 100%;" class="layout-wrap">
 | 
			
		||||
 | 
			
		||||
@ -56,6 +56,8 @@ export class SecuritySettingsComponent extends PageComponent implements OnInit,
 | 
			
		||||
 | 
			
		||||
  buildSecuritySettingsForm() {
 | 
			
		||||
    this.securitySettingsFormGroup = this.fb.group({
 | 
			
		||||
      maxFailedLoginAttempts: [null, [Validators.min(0)]],
 | 
			
		||||
      userLockoutNotificationEmail: ['', []],
 | 
			
		||||
      passwordPolicy: this.fb.group(
 | 
			
		||||
        {
 | 
			
		||||
          minimumLength: [null, [Validators.required, Validators.min(5), Validators.max(50)]],
 | 
			
		||||
@ -63,7 +65,8 @@ export class SecuritySettingsComponent extends PageComponent implements OnInit,
 | 
			
		||||
          minimumLowercaseLetters: [null, Validators.min(0)],
 | 
			
		||||
          minimumDigits: [null, Validators.min(0)],
 | 
			
		||||
          minimumSpecialCharacters: [null, Validators.min(0)],
 | 
			
		||||
          passwordExpirationPeriodDays: [null, Validators.min(0)]
 | 
			
		||||
          passwordExpirationPeriodDays: [null, Validators.min(0)],
 | 
			
		||||
          passwordReuseFrequencyDays: [null, Validators.min(0)]
 | 
			
		||||
        }
 | 
			
		||||
      )
 | 
			
		||||
    });
 | 
			
		||||
 | 
			
		||||
@ -18,11 +18,19 @@
 | 
			
		||||
<div>
 | 
			
		||||
  <mat-card class="profile-card">
 | 
			
		||||
    <mat-card-title>
 | 
			
		||||
      <div fxLayout="column">
 | 
			
		||||
        <span class="mat-headline" translate>profile.profile</span>
 | 
			
		||||
        <span class="profile-email" style='opacity: 0.7;'>{{ profile ? profile.get('email').value : '' }}</span>
 | 
			
		||||
      <div fxLayout="row">
 | 
			
		||||
        <div fxFlex fxLayout="column">
 | 
			
		||||
          <span class="mat-headline" translate>profile.profile</span>
 | 
			
		||||
          <span class="profile-email" style='opacity: 0.7;'>{{ profile ? profile.get('email').value : '' }}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div fxFlex fxLayout="column">
 | 
			
		||||
          <span class="mat-subheader" translate>profile.last-login-time</span>
 | 
			
		||||
          <span class="profile-last-login-ts" style='opacity: 0.7;'>{{ user?.additionalInfo?.lastLoginTs | date:'yyyy-MM-dd HH:mm:ss'  }}</span>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </mat-card-title>
 | 
			
		||||
    <mat-card-title>
 | 
			
		||||
    </mat-card-title>
 | 
			
		||||
    <mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
 | 
			
		||||
    </mat-progress-bar>
 | 
			
		||||
    <div style="height: 4px;" *ngIf="!(isLoading$ | async)"></div>
 | 
			
		||||
 | 
			
		||||
@ -28,5 +28,15 @@
 | 
			
		||||
      font-size: 16px;
 | 
			
		||||
      font-weight: 400;
 | 
			
		||||
    }
 | 
			
		||||
    .mat-subheader {
 | 
			
		||||
      line-height: 24px;
 | 
			
		||||
      color: rgba(0,0,0,0.54);
 | 
			
		||||
      font-size: 14px;
 | 
			
		||||
      font-weight: 400;
 | 
			
		||||
    }
 | 
			
		||||
    .profile-last-login-ts {
 | 
			
		||||
      font-size: 16px;
 | 
			
		||||
      font-weight: 400;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@ -16,6 +16,18 @@
 | 
			
		||||
 | 
			
		||||
-->
 | 
			
		||||
<div class="tb-details-buttons">
 | 
			
		||||
  <button mat-raised-button color="primary"
 | 
			
		||||
          [disabled]="(isLoading$ | async)"
 | 
			
		||||
          (click)="onEntityAction($event, 'disableAccount')"
 | 
			
		||||
          [fxShow]="!isEdit && isUserCredentialsEnabled()">
 | 
			
		||||
    {{'user.disable-account' | translate }}
 | 
			
		||||
  </button>
 | 
			
		||||
  <button mat-raised-button color="primary"
 | 
			
		||||
          [disabled]="(isLoading$ | async)"
 | 
			
		||||
          (click)="onEntityAction($event, 'enableAccount')"
 | 
			
		||||
          [fxShow]="!isEdit && !isUserCredentialsEnabled()">
 | 
			
		||||
    {{'user.enable-account' | translate }}
 | 
			
		||||
  </button>
 | 
			
		||||
  <button mat-raised-button color="primary"
 | 
			
		||||
          [disabled]="(isLoading$ | async)"
 | 
			
		||||
          (click)="onEntityAction($event, 'displayActivationLink')"
 | 
			
		||||
 | 
			
		||||
@ -23,6 +23,7 @@ import { User } from '@shared/models/user.model';
 | 
			
		||||
import { selectAuth, selectUserDetails } from '@core/auth/auth.selectors';
 | 
			
		||||
import { map } from 'rxjs/operators';
 | 
			
		||||
import { Authority } from '@shared/models/authority.enum';
 | 
			
		||||
import { isUndefined } from '@core/utils';
 | 
			
		||||
 | 
			
		||||
@Component({
 | 
			
		||||
  selector: 'tb-user',
 | 
			
		||||
@ -51,6 +52,14 @@ export class UserComponent extends EntityComponent<User> {
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  isUserCredentialsEnabled(): boolean {
 | 
			
		||||
    if (!this.entity || !this.entity.additionalInfo || isUndefined(this.entity.additionalInfo.userCredentialsEnabled)) {
 | 
			
		||||
      return true;
 | 
			
		||||
    } else {
 | 
			
		||||
      return this.entity.additionalInfo.userCredentialsEnabled === true;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  buildForm(entity: User): FormGroup {
 | 
			
		||||
    return this.fb.group(
 | 
			
		||||
      {
 | 
			
		||||
 | 
			
		||||
@ -213,6 +213,23 @@ export class UsersTableConfigResolver implements Resolve<EntityTableConfig<User>
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  setUserCredentialsEnabled($event: Event, user: User, userCredentialsEnabled: boolean) {
 | 
			
		||||
    if ($event) {
 | 
			
		||||
      $event.stopPropagation();
 | 
			
		||||
    }
 | 
			
		||||
    this.userService.setUserCredentialsEnabled(user.id.id, userCredentialsEnabled).subscribe(() => {
 | 
			
		||||
      if (!user.additionalInfo) {
 | 
			
		||||
        user.additionalInfo = {};
 | 
			
		||||
      }
 | 
			
		||||
      user.additionalInfo.userCredentialsEnabled = userCredentialsEnabled;
 | 
			
		||||
      this.store.dispatch(new ActionNotificationShow(
 | 
			
		||||
        {
 | 
			
		||||
          message: this.translate.instant(userCredentialsEnabled ? 'user.enable-account-message' : 'user.disable-account-message'),
 | 
			
		||||
          type: 'success'
 | 
			
		||||
        }));
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  onUserAction(action: EntityAction<User>): boolean {
 | 
			
		||||
    switch (action.action) {
 | 
			
		||||
      case 'loginAsUser':
 | 
			
		||||
@ -224,6 +241,12 @@ export class UsersTableConfigResolver implements Resolve<EntityTableConfig<User>
 | 
			
		||||
      case 'resendActivation':
 | 
			
		||||
        this.resendActivation(action.event, action.entity);
 | 
			
		||||
        return true;
 | 
			
		||||
      case 'disableAccount':
 | 
			
		||||
        this.setUserCredentialsEnabled(action.event, action.entity, false);
 | 
			
		||||
        return true;
 | 
			
		||||
      case 'enableAccount':
 | 
			
		||||
        this.setUserCredentialsEnabled(action.event, action.entity, true);
 | 
			
		||||
        return true;
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
@ -48,7 +48,8 @@ export enum ActionType {
 | 
			
		||||
  ALARM_ACK = 'ALARM_ACK',
 | 
			
		||||
  ALARM_CLEAR = 'ALARM_CLEAR',
 | 
			
		||||
  LOGIN = 'LOGIN',
 | 
			
		||||
  LOGOUT = 'LOGOUT'
 | 
			
		||||
  LOGOUT = 'LOGOUT',
 | 
			
		||||
  LOCKOUT = 'LOCKOUT'
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum ActionStatus {
 | 
			
		||||
@ -77,7 +78,8 @@ export const actionTypeTranslations = new Map<ActionType, string>(
 | 
			
		||||
    [ActionType.ALARM_ACK, 'audit-log.type-alarm-ack'],
 | 
			
		||||
    [ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'],
 | 
			
		||||
    [ActionType.LOGIN, 'audit-log.type-login'],
 | 
			
		||||
    [ActionType.LOGOUT, 'audit-log.type-logout']
 | 
			
		||||
    [ActionType.LOGOUT, 'audit-log.type-logout'],
 | 
			
		||||
    [ActionType.LOCKOUT, 'audit-log.type-lockout']
 | 
			
		||||
  ]
 | 
			
		||||
);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user