diff --git a/ui-ngx/src/app/core/services/utils.service.ts b/ui-ngx/src/app/core/services/utils.service.ts
index 734e503aeb..174f6f941e 100644
--- a/ui-ngx/src/app/core/services/utils.service.ts
+++ b/ui-ngx/src/app/core/services/utils.service.ts
@@ -50,6 +50,7 @@ import { entityTypeTranslations } from '@shared/models/entity-type.models';
import cssjs from '@core/css/css';
import { isNotEmptyTbFunction } from '@shared/models/js-function.models';
import { defaultFormProperties, FormProperty } from '@shared/models/dynamic-form.models';
+import { AbstractControl, ValidationErrors, Validators } from "@angular/forms";
const i18nRegExp = new RegExp(`{${i18nPrefix}:([^{}]+)}`, 'g');
@@ -477,4 +478,13 @@ export class UtilsService {
}
}
+ public validateEmail(control: AbstractControl): ValidationErrors | null {
+ const email = control.value;
+ const nativeEmailError = Validators.email(control);
+ if (nativeEmailError !== null) {
+ return nativeEmailError;
+ }
+ const passesDomainCheck = /\.[^.\s]{2,}$/.test(email);
+ return passesDomainCheck ? null : {email: true};
+ }
}
diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html
index 21cfcba869..54d7b197cc 100644
--- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html
+++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.html
@@ -45,6 +45,9 @@
admin.user-lockout-notification-email
+
+ {{ 'login.invalid-email-format' | translate }}
+
admin.user-activation-token-ttl
diff --git a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts
index ac02cf35f8..7f644b7441 100644
--- a/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/admin/security-settings.component.ts
@@ -38,6 +38,7 @@ import { DialogService } from '@core/services/dialog.service';
import { TranslateService } from '@ngx-translate/core';
import { Observable, of } from 'rxjs';
import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { UtilsService } from "@core/services/utils.service";
@Component({
selector: 'tb-security-settings',
@@ -61,6 +62,7 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi
private dialogService: DialogService,
private translate: TranslateService,
private fb: UntypedFormBuilder,
+ private utils: UtilsService,
private destroyRef: DestroyRef) {
super(store);
this.buildSecuritySettingsForm();
@@ -76,7 +78,7 @@ export class SecuritySettingsComponent extends PageComponent implements HasConfi
buildSecuritySettingsForm() {
this.securitySettingsFormGroup = this.fb.group({
maxFailedLoginAttempts: [null, [Validators.min(0)]],
- userLockoutNotificationEmail: ['', []],
+ userLockoutNotificationEmail: ['', [this.utils.validateEmail]],
userActivationTokenTtl: [24, [Validators.required, Validators.min(1), Validators.max(24)]],
passwordResetTokenTtl: [24, [Validators.required, Validators.min(1), Validators.max(24)]],
mobileSecretKeyLength: [null, [Validators.min(1)]],
diff --git a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts
index 7d1aa2aa4e..a097b470ca 100644
--- a/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/profile/profile.component.ts
@@ -33,6 +33,7 @@ import { getCurrentAuthUser } from '@core/auth/auth.selectors';
import { AuthService } from '@core/auth/auth.service';
import { UnitSystem, UnitSystems } from '@shared/models/unit.models';
import { UnitService } from '@core/services/unit.service';
+import { UtilsService } from "@core/services/utils.service";
@Component({
selector: 'tb-profile',
@@ -54,6 +55,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir
private authService: AuthService,
private translate: TranslateService,
private unitService: UnitService,
+ private utils: UtilsService,
private fb: UntypedFormBuilder) {
super(store);
this.authUser = getCurrentAuthUser(this.store);
@@ -66,7 +68,7 @@ export class ProfileComponent extends PageComponent implements OnInit, HasConfir
private buildProfileForm() {
this.profile = this.fb.group({
- email: ['', [Validators.required, Validators.email]],
+ email: ['', [Validators.required, this.utils.validateEmail]],
firstName: [''],
lastName: [''],
phone: [''],
diff --git a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts
index 601c2e9277..769463c63b 100644
--- a/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/security/authentication-dialog/email-auth-dialog.component.ts
@@ -28,6 +28,7 @@ import {
TwoFactorAuthProviderType
} from '@shared/models/two-factor-auth.models';
import { MatStepper } from '@angular/material/stepper';
+import { UtilsService } from "@core/services/utils.service";
export interface EmailAuthDialogData {
email: string;
@@ -51,13 +52,14 @@ export class EmailAuthDialogComponent extends DialogComponent,
protected router: Router,
private twoFaService: TwoFactorAuthenticationService,
+ private utils: UtilsService,
@Inject(MAT_DIALOG_DATA) public data: EmailAuthDialogData,
public dialogRef: MatDialogRef,
public fb: UntypedFormBuilder) {
super(store, router, dialogRef);
this.emailConfigForm = this.fb.group({
- email: [this.data.email, [Validators.required, Validators.email]]
+ email: [this.data.email, [Validators.required, this.utils.validateEmail]]
});
this.emailVerificationForm = this.fb.group({
diff --git a/ui-ngx/src/app/modules/home/pages/user/user.component.ts b/ui-ngx/src/app/modules/home/pages/user/user.component.ts
index da0a001b5d..0c9447e9d3 100644
--- a/ui-ngx/src/app/modules/home/pages/user/user.component.ts
+++ b/ui-ngx/src/app/modules/home/pages/user/user.component.ts
@@ -14,7 +14,7 @@
/// limitations under the License.
///
-import { ChangeDetectorRef, Component, Inject, Optional } from '@angular/core';
+import { ChangeDetectorRef, Component, Inject, OnInit, Optional } from '@angular/core';
import { select, Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { EntityComponent } from '../../components/entity/entity.component';
@@ -27,13 +27,14 @@ import { isDefinedAndNotNull } from '@core/utils';
import { EntityTableConfig } from '@home/models/entity/entities-table-config.models';
import { ActionNotificationShow } from '@app/core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
+import { UtilsService } from "@core/services/utils.service";
@Component({
selector: 'tb-user',
templateUrl: './user.component.html',
styleUrls: ['./user.component.scss']
})
-export class UserComponent extends EntityComponent {
+export class UserComponent extends EntityComponent implements OnInit{
authority = Authority;
@@ -46,11 +47,16 @@ export class UserComponent extends EntityComponent {
@Optional() @Inject('entity') protected entityValue: User,
@Optional() @Inject('entitiesTableConfig') protected entitiesTableConfigValue: EntityTableConfig,
public fb: UntypedFormBuilder,
+ private utils: UtilsService,
protected cd: ChangeDetectorRef,
protected translate: TranslateService) {
super(store, fb, entityValue, entitiesTableConfigValue, cd);
}
+ ngOnInit(): void {
+ this.entityForm.controls.email.addValidators(this.utils.validateEmail);
+ }
+
hideDelete() {
if (this.entitiesTableConfig) {
return !this.entitiesTableConfig.deleteEnabled(this.entity);
@@ -70,7 +76,7 @@ export class UserComponent extends EntityComponent {
buildForm(entity: User): UntypedFormGroup {
return this.fb.group(
{
- email: [entity ? entity.email : '', [Validators.required, Validators.email]],
+ email: [entity ? entity.email : '', [Validators.required]],
firstName: [entity ? entity.firstName : ''],
lastName: [entity ? entity.lastName : ''],
phone: [entity ? entity.phone : ''],
diff --git a/ui-ngx/src/app/modules/login/pages/login/login.component.ts b/ui-ngx/src/app/modules/login/pages/login/login.component.ts
index aa7642b654..89d5c8d8cd 100644
--- a/ui-ngx/src/app/modules/login/pages/login/login.component.ts
+++ b/ui-ngx/src/app/modules/login/pages/login/login.component.ts
@@ -19,11 +19,12 @@ import { AuthService } from '@core/auth/auth.service';
import { Store } from '@ngrx/store';
import { AppState } from '@core/core.state';
import { PageComponent } from '@shared/components/page.component';
-import { UntypedFormBuilder } from '@angular/forms';
+import { UntypedFormBuilder, Validators } from '@angular/forms';
import { HttpErrorResponse } from '@angular/common/http';
import { Constants } from '@shared/models/constants';
import { Router } from '@angular/router';
import { OAuth2ClientLoginInfo } from '@shared/models/oauth2.models';
+import { UtilsService } from "@core/services/utils.service";
@Component({
selector: 'tb-login',
@@ -35,14 +36,15 @@ export class LoginComponent extends PageComponent implements OnInit {
passwordViolation = false;
loginFormGroup = this.fb.group({
- username: '',
- password: ''
+ username: ['', [Validators.required, this.utils.validateEmail]],
+ password: ['']
});
oauth2Clients: Array = null;
constructor(protected store: Store,
private authService: AuthService,
public fb: UntypedFormBuilder,
+ private utils: UtilsService,
private router: Router) {
super(store);
}
diff --git a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts b/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts
index 98b3d97398..eaa9622eb7 100644
--- a/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts
+++ b/ui-ngx/src/app/modules/login/pages/login/reset-password-request.component.ts
@@ -22,6 +22,7 @@ import { PageComponent } from '@shared/components/page.component';
import { UntypedFormBuilder, Validators } from '@angular/forms';
import { ActionNotificationShow } from '@core/notification/notification.actions';
import { TranslateService } from '@ngx-translate/core';
+import { UtilsService } from "@core/services/utils.service";
@Component({
selector: 'tb-reset-password-request',
@@ -33,12 +34,13 @@ export class ResetPasswordRequestComponent extends PageComponent implements OnIn
clicked: boolean = false;
requestPasswordRequest = this.fb.group({
- email: ['', [Validators.email, Validators.required]]
+ email: ['', [Validators.required, this.utils.validateEmail]],
}, {updateOn: 'submit'});
constructor(protected store: Store,
private authService: AuthService,
private translate: TranslateService,
+ private utils: UtilsService,
public fb: UntypedFormBuilder) {
super(store);
}
diff --git a/ui-ngx/src/app/shared/components/contact.component.ts b/ui-ngx/src/app/shared/components/contact.component.ts
index d52e7d139b..e085a5e99a 100644
--- a/ui-ngx/src/app/shared/components/contact.component.ts
+++ b/ui-ngx/src/app/shared/components/contact.component.ts
@@ -14,14 +14,15 @@
/// limitations under the License.
///
-import { Component, Input } from '@angular/core';
+import { Component, Input, OnInit } from '@angular/core';
import { UntypedFormGroup } from '@angular/forms';
+import { UtilsService } from "@core/services/utils.service";
@Component({
selector: 'tb-contact',
templateUrl: './contact.component.html'
})
-export class ContactComponent {
+export class ContactComponent implements OnInit {
@Input()
parentForm: UntypedFormGroup;
@@ -30,7 +31,11 @@ export class ContactComponent {
phoneInputDefaultCountry = 'US';
- constructor() {
+ constructor(private utils: UtilsService) {
+ }
+
+ ngOnInit() {
+ this.parentForm.controls['email'].addValidators(this.utils.validateEmail)
}
changeCountry(countryCode: string) {