Skip to content

Commit 6c9a553

Browse files
committed
mgr/dashboard: Dashboard nfs export editor rejects ipv6 addresses
Fixes https://tracker.ceph.com/issues/72660 Signed-off-by: Afreen Misbah <[email protected]>
1 parent 6ccf8a7 commit 6c9a553

File tree

4 files changed

+75
-7
lines changed

4 files changed

+75
-7
lines changed

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form-client/nfs-form-client.component.html

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,16 @@ <h6>
4141
name="addresses"
4242
id="addresses"
4343
formControlName="addresses"
44-
placeholder="192.168.0.10, 192.168.1.0/8"
44+
placeholder="e.g. 192.168.0.10, 192.168.1.0/8"
4545
[invalid]="!item.controls['addresses'].valid && (item.controls['addresses'].dirty)">
4646
</cds-text-label>
4747
<ng-template #addressesError>
4848
<span class="invalid-feedback">
4949
<span *ngIf="showError(index, 'addresses', formDir, 'required')"
5050
i18n>This field is required.</span>
5151

52-
<span *ngIf="showError(index, 'addresses', formDir, 'pattern')">
53-
<ng-container i18n>Must contain one or more comma-separated values</ng-container>
52+
<span *ngIf="showError(index, 'addresses', formDir, 'invalidAddress')">
53+
<ng-container i18n>Must contain one or more comma-separated valid addresses.</ng-container>
5454
<br>
5555
<ng-container i18n>For example:</ng-container> 192.168.0.10, 192.168.1.0/8
5656
</span>

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form-client/nfs-form-client.component.spec.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,4 +68,46 @@ describe('NfsFormClientComponent', () => {
6868
component.removeClient(0);
6969
expect(component.form.getValue('clients')).toEqual([]);
7070
});
71+
72+
describe(`test 'isValidClientAddress'`, () => {
73+
it('should return false for empty value', () => {
74+
expect(component.isValidClientAddress('')).toBeFalsy();
75+
expect(component.isValidClientAddress(null)).toBeFalsy();
76+
});
77+
78+
it('should return false for valid single IPv4 address', () => {
79+
expect(component.isValidClientAddress('192.168.1.1')).toBeFalsy();
80+
});
81+
82+
it('should return false for valid IPv6 address', () => {
83+
expect(component.isValidClientAddress('2001:db8::1')).toBeFalsy();
84+
});
85+
86+
it('should return false for valid FQDN', () => {
87+
expect(component.isValidClientAddress('nfs.example.com')).toBeFalsy();
88+
});
89+
90+
it('should return false for valid IP CIDR range', () => {
91+
expect(component.isValidClientAddress('192.168.0.0/24')).toBeFalsy();
92+
expect(component.isValidClientAddress('2001:db8::/64')).toBeFalsy();
93+
});
94+
95+
it('should return false for multiple valid addresses separated by comma', () => {
96+
const input = '192.168.1.1, 2001:db8::1, nfs.example.com, 10.0.0.0/8';
97+
expect(component.isValidClientAddress(input)).toBeFalsy();
98+
});
99+
100+
it('should return true for invalid single address', () => {
101+
expect(component.isValidClientAddress('invalid-address')).toBeTruthy();
102+
});
103+
104+
it('should return true for mixed valid and invalid addresses', () => {
105+
const input = '192.168.1.1, invalid-address';
106+
expect(component.isValidClientAddress(input)).toBeTruthy();
107+
});
108+
109+
it('should return true for URLs with protocols', () => {
110+
expect(component.isValidClientAddress('http://nfs.example.com')).toBeTruthy();
111+
});
112+
});
71113
});

src/pybind/mgr/dashboard/frontend/src/app/ceph/nfs/nfs-form-client/nfs-form-client.component.ts

Lines changed: 27 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,12 @@ import { Component, ContentChild, Input, OnInit, TemplateRef } from '@angular/co
22
import { UntypedFormArray, UntypedFormControl, NgForm, Validators } from '@angular/forms';
33

44
import _ from 'lodash';
5+
import validator from 'validator';
56

67
import { NfsService } from '~/app/shared/api/nfs.service';
78
import { Icons } from '~/app/shared/enum/icons.enum';
89
import { CdFormGroup } from '~/app/shared/forms/cd-form-group';
10+
import { CdValidators } from '~/app/shared/forms/cd-validators';
911

1012
@Component({
1113
selector: 'cd-nfs-form-client',
@@ -59,14 +61,36 @@ export class NfsFormClientComponent implements OnInit {
5961
return $localize`-- Select what kind of user id squashing is performed --`;
6062
}
6163

64+
isValidClientAddress(value: string): boolean {
65+
if (_.isEmpty(value)) {
66+
return false;
67+
}
68+
69+
const addressList = value.includes(',')
70+
? value.split(',').map((addr) => addr.trim())
71+
: [value.trim()];
72+
const invalidAddresses = addressList.filter(
73+
(address: string) =>
74+
!validator.isFQDN(address, {
75+
allow_underscores: true,
76+
require_tld: true
77+
}) &&
78+
!validator.isIP(address) &&
79+
!validator.isIPRange(address)
80+
);
81+
82+
return invalidAddresses.length > 0;
83+
}
84+
6285
addClient() {
6386
this.clientsFormArray = this.form.get('clients') as UntypedFormArray;
6487

65-
const REGEX_IP = `(([0-9]{1,3})\\.([0-9]{1,3})\\.([0-9]{1,3})\.([0-9]{1,3})([/](\\d|[1-2]\\d|3[0-2]))?)`;
66-
const REGEX_LIST_IP = `${REGEX_IP}([ ,]{1,2}${REGEX_IP})*`;
6788
const fg = new CdFormGroup({
6889
addresses: new UntypedFormControl('', {
69-
validators: [Validators.required, Validators.pattern(REGEX_LIST_IP)]
90+
validators: [
91+
Validators.required,
92+
CdValidators.custom('invalidAddress', this.isValidClientAddress)
93+
]
7094
}),
7195
access_type: new UntypedFormControl(''),
7296
squash: new UntypedFormControl('')

src/pybind/mgr/dashboard/frontend/src/app/shared/forms/cd-validators.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -696,7 +696,9 @@ export class CdValidators {
696696
return null;
697697
}
698698

699-
const urls = value.includes(',') ? value.split(',') : [value];
699+
const urls = value.includes(',')
700+
? value.split(',').map((v: string) => v.trim())
701+
: [value.trim()];
700702

701703
const invalidUrls = urls.filter(
702704
(url: string) =>

0 commit comments

Comments
 (0)