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,10 +18,18 @@
|
||||
<div>
|
||||
<mat-card class="profile-card">
|
||||
<mat-card-title>
|
||||
<div fxLayout="column">
|
||||
<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>
|
||||
|
||||
@ -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