Skip to content

Commit

Permalink
Add "string not blank when trimmed" validator (#217)
Browse files Browse the repository at this point in the history
  • Loading branch information
illfixit authored Jun 5, 2024
1 parent 76119f1 commit 3ba2895
Show file tree
Hide file tree
Showing 17 changed files with 166 additions and 66 deletions.
16 changes: 11 additions & 5 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ please see [changelog_updates.md](docs/dev/changelog_updates.md).

#### Patch

- Input fields containing only whitespaces are now properly validated and an appropriate error message is shown [#193](https://github.com/sovity/authority-portal/issues/193)
- Component uptime now displays up to '30+ days' [#211](https://github.com/sovity/authority-portal/issues/211)

### Known issues
Expand Down Expand Up @@ -102,29 +103,31 @@ This release addresses several security issues and adds minor improvements to th
- Change SSL settings
- Realm settings > General > Require SSL: `All requests`
- Caddy

- Headers to improve security are now set
- Modified Caddyfile:

```
# UI Requests: Internet -> Caddy 8080 -> Frontend
# Backend Requests: Internet -> Caddy 8080 -> Auth Proxy -> Caddy 8081 -> Backend

:8080 {
map {path} {target_host} {target_port} {
~^/api/.* {$AUTH_PROXY_UPSTREAM_HOST} 8080
~^/oauth2/.* {$AUTH_PROXY_UPSTREAM_HOST} 8080
default {$FRONTEND_UPSTREAM_HOST} 8080
}

reverse_proxy {target_host}:{target_port} {
header_down -Gap-Auth
}

# Set security headers for UI responses
header {
X-Frame-Options "DENY"
+Content-Security-Policy "frame-ancestors 'none'"
}

# Set security headers for API responses
header /api/* {
X-Content-Type-Options nosniff
Expand All @@ -137,7 +140,7 @@ This release addresses several security issues and adds minor improvements to th
+Cache-Control "public, max-age=2592000, immutable"
}
}

# Caddy 8081 -> Backend
# We need this second block because the auth proxy
# does not pass the token on the right header due to
Expand Down Expand Up @@ -452,11 +455,14 @@ Major release, containing a UI rework and several new features.
### Deployment Migration Notes

- Keycloak

- Replace [MDS theme](authority-portal-keycloak/mds-theme) with the new version
- Keycloak IAM needs to be upgraded to version 23.0.4

- Portal Backend

- Added environment variables

```yaml
# CaaS Portal API Client Auth
# will be provided by sovity
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
* sovity GmbH - initial implementation
*/
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {certificateValidator} from '../../../../core/utils/validators/certificate-validator';
import {passwordEntropyValidator} from '../../../../core/utils/validators/password-entropy-validator';
import {passwordMatchValidator} from '../../../../core/utils/validators/password-match-validator';
Expand All @@ -32,21 +33,28 @@ export const buildCertificateInputForm = (
],
organizationalUnit: [
initial.organizationalUnit,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
email: [
initial.email,
[Validators.required, Validators.email, Validators.maxLength(128)],
],
state: [initial.state, [Validators.required, Validators.maxLength(128)]],
city: [initial.city, [Validators.required, Validators.maxLength(128)]],
state: [
initial.state,
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
city: [
initial.city,
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
password: [
initial.password,
[
Validators.required,
Validators.minLength(8),
Validators.maxLength(128),
passwordEntropyValidator,
notBlankValidator(),
],
],
confirmPassword: [initial.confirmPassword, [Validators.required]],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,10 @@
At most {{ ctrl.errors!.maxlength.requiredLength }} characters
allowed.</mat-error
>

<mat-error
*ngIf="ctrl.touched && ctrl.hasError('trimmedBlank')"
class="text-xs">
The field cannot be blank.
</mat-error>
</mat-form-field>
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,12 @@
https://www.example.com</mat-error
>

<mat-error
*ngIf="ctrl.touched && ctrl.hasError('connector-url')"
class="text-xs">
Invalid URL format. Please use the following format: https://www.example.com
</mat-error>

<mat-error *ngIf="ctrl.touched && ctrl.hasError('zipCode')" class="text-xs">
Invalid Zip Code. Please use 5 or more letters, numbers and '-'.</mat-error
>
Expand All @@ -49,4 +55,10 @@
class="text-xs">
Invalid phone number. Only digits, whitespaces and '+' allowed</mat-error
>

<mat-error
*ngIf="ctrl.touched && ctrl.hasError('trimmedBlank')"
class="text-xs">
The field cannot be blank.
</mat-error>
</mat-form-field>
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import {AbstractControl, ValidationErrors, ValidatorFn} from '@angular/forms';

export function notBlankValidator(): ValidatorFn {
return (control: AbstractControl): ValidationErrors | null => {
if (!control.value) {
return null;
}
if (control.value.length && control.dirty && control.value.trim() === '') {
return {trimmedBlank: true};
}
return null;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
import {Injectable} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {buildCertificateInputForm} from '../../../../common/components/form-elements/certificate-input-form/certificate-input-form-builder';
import {certificateInputFormEnabledCtrls} from '../../../../common/components/form-elements/certificate-input-form/certificate-input-form-enabled-ctrls';
import {switchDisabledControls} from '../../../../core/utils/form-utils';
Expand Down Expand Up @@ -49,23 +50,38 @@ export class ProvideConnectorPageForm {
const connectorTab = this.formBuilder.nonNullable.group({
name: [
initial.connectorTab.name,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
location: [
initial.connectorTab.location,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
frontendUrl: [
initial.connectorTab.frontendUrl,
[Validators.required, Validators.maxLength(128), connectorUrlValidator],
[
Validators.required,
Validators.maxLength(128),
notBlankValidator(),
connectorUrlValidator,
],
],
endpointUrl: [
initial.connectorTab.endpointUrl,
[Validators.required, Validators.maxLength(128), connectorUrlValidator],
[
Validators.required,
Validators.maxLength(128),
notBlankValidator(),
connectorUrlValidator,
],
],
managementUrl: [
initial.connectorTab.managementUrl,
[Validators.required, Validators.maxLength(128), connectorUrlValidator],
[
Validators.required,
Validators.maxLength(128),
notBlankValidator(),
connectorUrlValidator,
],
],
organization: [
initial.connectorTab.organization,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
import {Injectable} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {buildCertificateInputForm} from '../../../../common/components/form-elements/certificate-input-form/certificate-input-form-builder';
import {certificateInputFormEnabledCtrls} from '../../../../common/components/form-elements/certificate-input-form/certificate-input-form-enabled-ctrls';
import {switchDisabledControls} from '../../../../core/utils/form-utils';
Expand Down Expand Up @@ -49,11 +50,11 @@ export class RegisterCentralComponentPageForm {
const componentTab = this.formBuilder.nonNullable.group({
name: [
initial.componentTab.name,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
location: [
initial.componentTab.location,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
frontendUrl: [
initial.componentTab.frontendUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
import {Injectable} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {buildCertificateInputForm} from '../../../../common/components/form-elements/certificate-input-form/certificate-input-form-builder';
import {certificateInputFormEnabledCtrls} from '../../../../common/components/form-elements/certificate-input-form/certificate-input-form-enabled-ctrls';
import {switchDisabledControls} from '../../../../core/utils/form-utils';
Expand Down Expand Up @@ -49,23 +50,38 @@ export class RegisterConnectorPageForm {
const connectorTab = this.formBuilder.nonNullable.group({
name: [
initial.connectorTab.name,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
location: [
initial.connectorTab.location,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
frontendUrl: [
initial.connectorTab.frontendUrl,
[Validators.required, Validators.maxLength(128), connectorUrlValidator],
[
Validators.required,
Validators.maxLength(128),
notBlankValidator(),
connectorUrlValidator,
],
],
endpointUrl: [
initial.connectorTab.endpointUrl,
[Validators.required, Validators.maxLength(128), connectorUrlValidator],
[
Validators.required,
Validators.maxLength(128),
notBlankValidator(),
connectorUrlValidator,
],
],
managementUrl: [
initial.connectorTab.managementUrl,
[Validators.required, Validators.maxLength(128), connectorUrlValidator],
[
Validators.required,
Validators.maxLength(128),
notBlankValidator(),
connectorUrlValidator,
],
],
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
*/
import {Injectable} from '@angular/core';
import {FormBuilder, FormGroup, Validators} from '@angular/forms';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {subdomainValidators} from 'src/app/core/utils/validators/subdomain-validator';
import {
DEFAULT_REQUEST_CONNECTOR_PAGE_FORM_VALUE,
Expand All @@ -31,7 +32,7 @@ export class RequestConnectorPageForm {
return this.formBuilder.nonNullable.group({
connectorTitle: [
initial.connectorTitle,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
connectorSubdomain: [
initial.connectorSubdomain,
Expand All @@ -43,7 +44,7 @@ export class RequestConnectorPageForm {
],
connectorDescription: [
initial.connectorDescription,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import {MatDialogRef} from '@angular/material/dialog';
import {Subject, takeUntil} from 'rxjs';
import {Store} from '@ngxs/store';
import {InviteOrganizationRequest} from '@sovity.de/authority-portal-client';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {phoneNumberValidator} from 'src/app/core/utils/validators/phone-number-validator';
import {InviteNewOrganization} from '../state/authority-invite-new-organization-page-actions';
import {
Expand Down Expand Up @@ -62,29 +63,24 @@ export class AuthorityInviteNewOrganizationComponent {
return this.formBuilder.nonNullable.group({
userFirstName: [
initial.userFirstName,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
userLastName: [
initial.userLastName,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
userEmail: [
initial.userEmail,
[Validators.required, Validators.maxLength(128), Validators.email],
],
orgName: [
initial.orgName,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
userJobTitle: [initial.userJobTitle],
userPhoneNumber: [
initial.userPhoneNumber,
[
Validators.required,
phoneNumberValidator,
Validators.minLength(5),
Validators.maxLength(28),
],
[phoneNumberValidator, Validators.maxLength(28)],
],
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
getParticipantRoles,
mapRolesToReadableFormat,
} from 'src/app/core/utils/user-role-utils';
import {notBlankValidator} from 'src/app/core/utils/validators/not-blank-validator';
import {InviteNewUser} from '../state/participant-invite-new-user-page-actions';
import {
DEFAULT_PARTICIPANT_INVITE_NEW_USER_PAGE_STATE,
Expand Down Expand Up @@ -65,17 +66,20 @@ export class ParticipantInviteNewUserComponent {
return this.formBuilder.nonNullable.group({
firstName: [
initial.firstName,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
lastName: [
initial.lastName,
[Validators.required, Validators.maxLength(128)],
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
email: [
initial.email,
[Validators.required, Validators.maxLength(128), Validators.email],
],
role: [initial.role, [Validators.required, Validators.maxLength(128)]],
role: [
initial.role,
[Validators.required, Validators.maxLength(128), notBlankValidator()],
],
});
}

Expand Down
Loading

0 comments on commit 3ba2895

Please sign in to comment.