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);
|
javaMailProperties.put(MAIL_PROP + protocol + ".starttls.enable", enableTls);
|
||||||
if (enableTls && jsonConfig.has("tlsVersion") && StringUtils.isNoneEmpty(jsonConfig.get("tlsVersion").asText())) {
|
if (enableTls && jsonConfig.has("tlsVersion") && !jsonConfig.get("tlsVersion").isNull()) {
|
||||||
javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", jsonConfig.get("tlsVersion").asText());
|
String tlsVersion = jsonConfig.get("tlsVersion").asText();
|
||||||
|
if (StringUtils.isNoneEmpty(tlsVersion)) {
|
||||||
|
javaMailProperties.put(MAIL_PROP + protocol + ".ssl.protocols", tlsVersion);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return javaMailProperties;
|
return javaMailProperties;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -96,6 +96,10 @@ export class AlarmService {
|
|||||||
return this.http.post<void>(`/api/alarm/${alarmId}/clear`, null, defaultHttpOptionsFromConfig(config));
|
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,
|
public getAlarms(query: AlarmQuery,
|
||||||
config?: RequestConfig): Observable<PageData<AlarmInfo>> {
|
config?: RequestConfig): Observable<PageData<AlarmInfo>> {
|
||||||
return this.http.get<PageData<AlarmInfo>>(`/api/alarm${query.toQuery()}`,
|
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 { HttpClient } from '@angular/common/http';
|
||||||
import { PageLink } from '@shared/models/page/page-link';
|
import { PageLink } from '@shared/models/page/page-link';
|
||||||
import { PageData } from '@shared/models/page/page-data';
|
import { PageData } from '@shared/models/page/page-data';
|
||||||
|
import { isDefined } from '@core/utils';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@ -67,4 +68,12 @@ export class UserService {
|
|||||||
return this.http.post(`/api/user/sendActivationMail?email=${email}`, null, defaultHttpOptionsFromConfig(config));
|
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 }}
|
{{ 'admin.timeout-invalid' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</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 }}
|
{{ 'admin.enable-tls' | translate }}
|
||||||
</tb-checkbox>
|
</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-form-field class="mat-block">
|
||||||
<mat-label translate>common.username</mat-label>
|
<mat-label translate>common.username</mat-label>
|
||||||
<input matInput formControlName="username" placeholder="{{ 'common.enter-username' | translate }}"/>
|
<input matInput formControlName="username" placeholder="{{ 'common.enter-username' | translate }}"/>
|
||||||
|
|||||||
@ -37,6 +37,8 @@ export class MailServerComponent extends PageComponent implements OnInit, HasCon
|
|||||||
adminSettings: AdminSettings<MailServerSettings>;
|
adminSettings: AdminSettings<MailServerSettings>;
|
||||||
smtpProtocols = ['smtp', 'smtps'];
|
smtpProtocols = ['smtp', 'smtps'];
|
||||||
|
|
||||||
|
tlsVersions = ['TLSv1.0', 'TLSv1.1', 'TLSv1.2', 'TLSv1.3'];
|
||||||
|
|
||||||
constructor(protected store: Store<AppState>,
|
constructor(protected store: Store<AppState>,
|
||||||
private router: Router,
|
private router: Router,
|
||||||
private adminService: AdminService,
|
private adminService: AdminService,
|
||||||
@ -67,6 +69,7 @@ export class MailServerComponent extends PageComponent implements OnInit, HasCon
|
|||||||
Validators.pattern(/^[0-9]{1,6}$/),
|
Validators.pattern(/^[0-9]{1,6}$/),
|
||||||
Validators.maxLength(6)]],
|
Validators.maxLength(6)]],
|
||||||
enableTls: ['false'],
|
enableTls: ['false'],
|
||||||
|
tlsVersion: [],
|
||||||
username: [''],
|
username: [''],
|
||||||
password: ['']
|
password: ['']
|
||||||
});
|
});
|
||||||
|
|||||||
@ -30,6 +30,28 @@
|
|||||||
<mat-card-content style="padding-top: 16px;">
|
<mat-card-content style="padding-top: 16px;">
|
||||||
<form #securitySettingsForm="ngForm" [formGroup]="securitySettingsFormGroup" (ngSubmit)="save()">
|
<form #securitySettingsForm="ngForm" [formGroup]="securitySettingsFormGroup" (ngSubmit)="save()">
|
||||||
<fieldset [disabled]="isLoading$ | async">
|
<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 [expanded]="true">
|
||||||
<mat-expansion-panel-header>
|
<mat-expansion-panel-header>
|
||||||
<mat-panel-title>
|
<mat-panel-title>
|
||||||
@ -105,6 +127,16 @@
|
|||||||
{{ 'admin.password-expiration-period-days-range' | translate }}
|
{{ 'admin.password-expiration-period-days-range' | translate }}
|
||||||
</mat-error>
|
</mat-error>
|
||||||
</mat-form-field>
|
</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>
|
</section>
|
||||||
</mat-expansion-panel>
|
</mat-expansion-panel>
|
||||||
<div fxLayout="row" fxLayoutAlign="end center" style="width: 100%;" class="layout-wrap">
|
<div fxLayout="row" fxLayoutAlign="end center" style="width: 100%;" class="layout-wrap">
|
||||||
|
|||||||
@ -56,6 +56,8 @@ export class SecuritySettingsComponent extends PageComponent implements OnInit,
|
|||||||
|
|
||||||
buildSecuritySettingsForm() {
|
buildSecuritySettingsForm() {
|
||||||
this.securitySettingsFormGroup = this.fb.group({
|
this.securitySettingsFormGroup = this.fb.group({
|
||||||
|
maxFailedLoginAttempts: [null, [Validators.min(0)]],
|
||||||
|
userLockoutNotificationEmail: ['', []],
|
||||||
passwordPolicy: this.fb.group(
|
passwordPolicy: this.fb.group(
|
||||||
{
|
{
|
||||||
minimumLength: [null, [Validators.required, Validators.min(5), Validators.max(50)]],
|
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)],
|
minimumLowercaseLetters: [null, Validators.min(0)],
|
||||||
minimumDigits: [null, Validators.min(0)],
|
minimumDigits: [null, Validators.min(0)],
|
||||||
minimumSpecialCharacters: [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,10 +18,18 @@
|
|||||||
<div>
|
<div>
|
||||||
<mat-card class="profile-card">
|
<mat-card class="profile-card">
|
||||||
<mat-card-title>
|
<mat-card-title>
|
||||||
<div fxLayout="column">
|
<div fxLayout="row">
|
||||||
|
<div fxFlex fxLayout="column">
|
||||||
<span class="mat-headline" translate>profile.profile</span>
|
<span class="mat-headline" translate>profile.profile</span>
|
||||||
<span class="profile-email" style='opacity: 0.7;'>{{ profile ? profile.get('email').value : '' }}</span>
|
<span class="profile-email" style='opacity: 0.7;'>{{ profile ? profile.get('email').value : '' }}</span>
|
||||||
</div>
|
</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-card-title>
|
||||||
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
<mat-progress-bar color="warn" mode="indeterminate" *ngIf="isLoading$ | async">
|
||||||
</mat-progress-bar>
|
</mat-progress-bar>
|
||||||
|
|||||||
@ -28,5 +28,15 @@
|
|||||||
font-size: 16px;
|
font-size: 16px;
|
||||||
font-weight: 400;
|
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">
|
<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"
|
<button mat-raised-button color="primary"
|
||||||
[disabled]="(isLoading$ | async)"
|
[disabled]="(isLoading$ | async)"
|
||||||
(click)="onEntityAction($event, 'displayActivationLink')"
|
(click)="onEntityAction($event, 'displayActivationLink')"
|
||||||
|
|||||||
@ -23,6 +23,7 @@ import { User } from '@shared/models/user.model';
|
|||||||
import { selectAuth, selectUserDetails } from '@core/auth/auth.selectors';
|
import { selectAuth, selectUserDetails } from '@core/auth/auth.selectors';
|
||||||
import { map } from 'rxjs/operators';
|
import { map } from 'rxjs/operators';
|
||||||
import { Authority } from '@shared/models/authority.enum';
|
import { Authority } from '@shared/models/authority.enum';
|
||||||
|
import { isUndefined } from '@core/utils';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'tb-user',
|
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 {
|
buildForm(entity: User): FormGroup {
|
||||||
return this.fb.group(
|
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 {
|
onUserAction(action: EntityAction<User>): boolean {
|
||||||
switch (action.action) {
|
switch (action.action) {
|
||||||
case 'loginAsUser':
|
case 'loginAsUser':
|
||||||
@ -224,6 +241,12 @@ export class UsersTableConfigResolver implements Resolve<EntityTableConfig<User>
|
|||||||
case 'resendActivation':
|
case 'resendActivation':
|
||||||
this.resendActivation(action.event, action.entity);
|
this.resendActivation(action.event, action.entity);
|
||||||
return true;
|
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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -48,7 +48,8 @@ export enum ActionType {
|
|||||||
ALARM_ACK = 'ALARM_ACK',
|
ALARM_ACK = 'ALARM_ACK',
|
||||||
ALARM_CLEAR = 'ALARM_CLEAR',
|
ALARM_CLEAR = 'ALARM_CLEAR',
|
||||||
LOGIN = 'LOGIN',
|
LOGIN = 'LOGIN',
|
||||||
LOGOUT = 'LOGOUT'
|
LOGOUT = 'LOGOUT',
|
||||||
|
LOCKOUT = 'LOCKOUT'
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum ActionStatus {
|
export enum ActionStatus {
|
||||||
@ -77,7 +78,8 @@ export const actionTypeTranslations = new Map<ActionType, string>(
|
|||||||
[ActionType.ALARM_ACK, 'audit-log.type-alarm-ack'],
|
[ActionType.ALARM_ACK, 'audit-log.type-alarm-ack'],
|
||||||
[ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'],
|
[ActionType.ALARM_CLEAR, 'audit-log.type-alarm-clear'],
|
||||||
[ActionType.LOGIN, 'audit-log.type-login'],
|
[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