diff --git a/src/app/enums/nfs-acl.enum.ts b/src/app/enums/nfs-acl.enum.ts index 0c8b5626a69..6b74c9d5171 100644 --- a/src/app/enums/nfs-acl.enum.ts +++ b/src/app/enums/nfs-acl.enum.ts @@ -6,6 +6,7 @@ export enum NfsAclTag { Everyone = 'everyone@', User = 'USER', UserGroup = 'GROUP', + Both = 'BOTH', } export const nfsAclTagLabels = new Map([ diff --git a/src/app/interfaces/smb-share.interface.ts b/src/app/interfaces/smb-share.interface.ts index 2077bcf21b5..d73f688434c 100644 --- a/src/app/interfaces/smb-share.interface.ts +++ b/src/app/interfaces/smb-share.interface.ts @@ -63,7 +63,7 @@ export interface SmbSharesecAce { ae_perm: SmbSharesecPermission; ae_type: SmbSharesecType; ae_who_id: { - id_type: NfsAclTag.Everyone | NfsAclTag.UserGroup | NfsAclTag.User | null; + id_type: NfsAclTag.Everyone | NfsAclTag.UserGroup | NfsAclTag.User | NfsAclTag.Both | null; id: number; }; ae_who_sid?: string; diff --git a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.html b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.html index 22c1b8991b1..804cb2f4b01 100644 --- a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.html +++ b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.html @@ -39,6 +39,7 @@ formControlName="user" [label]="'User' | translate" [provider]="userProvider" + [allowCustomValue]="true" [required]="true" > @@ -47,6 +48,7 @@ formControlName="group" [label]="'Group' | translate" [provider]="groupProvider" + [allowCustomValue]="true" [required]="true" > diff --git a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.spec.ts b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.spec.ts index 6e9157a56fa..5038b29ba92 100644 --- a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.spec.ts +++ b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.spec.ts @@ -109,26 +109,80 @@ describe('SmbAclComponent', () => { expect(title).toHaveText('Share ACL for myshare'); }); - it('shows user combobox when Who is user', async () => { - await entriesList.pressAddButton(); - const newListItem = await entriesList.getLastListItem(); - await newListItem.fillForm({ - Who: 'User', + describe('user ace', () => { + it('shows user combobox when Who is user', async () => { + await entriesList.pressAddButton(); + const newListItem = await entriesList.getLastListItem(); + await newListItem.fillForm({ + Who: 'User', + }); + + const userSelect = await loader.getHarness(IxComboboxHarness.with({ label: 'User' })); + expect(userSelect).toExist(); + + const entries = spectator.component.form.value.entries; + expect(entries[entries.length - 1]).toEqual( + expect.not.objectContaining({ user: 0 }), + ); }); - const userSelect = await loader.getHarness(IxComboboxHarness.with({ label: 'User' })); - expect(userSelect).toExist(); + it('allows custom values in User combobox', async () => { + const newListItem = await entriesList.getLastListItem(); + await newListItem.fillForm({ + Who: 'User', + }); + + const fields = await newListItem.getControlHarnessesDict(); + + const userCombobox = fields['User'] as IxComboboxHarness; + await userCombobox.writeCustomValue('root'); + + const userSelect = await loader.getHarness(IxComboboxHarness.with({ label: 'User' })); + expect(userSelect).toExist(); + + const entries = spectator.component.form.value.entries; + expect(entries[entries.length - 1]).toEqual( + expect.objectContaining({ user: 0 }), + ); + }); }); - it('shows group combobox when Who is group', async () => { - await entriesList.pressAddButton(); - const newListItem = await entriesList.getLastListItem(); - await newListItem.fillForm({ - Who: 'Group', + describe('group ace', () => { + it('shows group combobox when Who is group', async () => { + await entriesList.pressAddButton(); + const newListItem = await entriesList.getLastListItem(); + await newListItem.fillForm({ + Who: 'Group', + }); + + const groupSelect = await loader.getHarness(IxComboboxHarness.with({ label: 'Group' })); + expect(groupSelect).toExist(); + + const entries = spectator.component.form.value.entries; + expect(entries[entries.length - 1]).toEqual( + expect.not.objectContaining({ group: 1 }), + ); }); - const groupSelect = await loader.getHarness(IxComboboxHarness.with({ label: 'Group' })); - expect(groupSelect).toExist(); + it('allows custom values in Group combobox', async () => { + const newListItem = await entriesList.getLastListItem(); + await newListItem.fillForm({ + Who: 'Group', + }); + + const fields = await newListItem.getControlHarnessesDict(); + + const groupCombobox = fields['Group'] as IxComboboxHarness; + await groupCombobox.writeCustomValue('wheel'); + + const groupSelect = await loader.getHarness(IxComboboxHarness.with({ label: 'Group' })); + expect(groupSelect).toExist(); + + const entries = spectator.component.form.value.entries; + expect(entries[entries.length - 1]).toEqual( + expect.objectContaining({ group: 1 }), + ); + }); }); it('loads and shows current acl for a share', async () => { diff --git a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts index 2ce2f26eacf..f27fee260ca 100644 --- a/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts +++ b/src/app/pages/sharing/smb/smb-acl/smb-acl.component.ts @@ -5,7 +5,7 @@ import { FormBuilder } from '@ngneat/reactive-forms'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService } from '@ngx-translate/core'; import { - concatMap, forkJoin, from, Observable, of, + concatMap, forkJoin, from, map, Observable, of, } from 'rxjs'; import { NfsAclTag, smbAclTagLabels } from 'app/enums/nfs-acl.enum'; import { Role } from 'app/enums/role.enum'; @@ -141,16 +141,26 @@ export class SmbAclComponent implements OnInit { private loadSmbAcl(shareName: string): void { this.isLoading = true; - this.ws.call('sharing.smb.getacl', [{ share_name: shareName }]) - .pipe(untilDestroyed(this)) + forkJoin([ + this.ws.call('sharing.smb.getacl', [{ share_name: shareName }]), + this.userService.smbUserQueryDsCache().pipe(map((users) => users.map((user) => user.uid))), + ]).pipe(untilDestroyed(this)) .subscribe({ - next: (shareAcl) => { + next: ([shareAcl, userIds]) => { this.shareAclName = shareAcl.share_name; shareAcl.share_acl.forEach((ace, i) => { this.addAce(); + + let aeWho: FormAclEntry['ae_who']; + if (ace.ae_who_id?.id_type === NfsAclTag.Both) { + aeWho = userIds.includes(ace.ae_who_id.id) ? NfsAclTag.User : NfsAclTag.UserGroup; + } else { + aeWho = ace.ae_who_id?.id_type || ace.ae_who_str as NfsAclTag.Everyone; + } + this.form.controls.entries.at(i).patchValue({ ae_who_sid: ace.ae_who_sid, - ae_who: ace.ae_who_id?.id_type || ace.ae_who_str as NfsAclTag.Everyone, + ae_who: aeWho, ae_perm: ace.ae_perm, ae_type: ace.ae_type, group: ace.ae_who_id?.id_type !== NfsAclTag.Everyone ? ace.ae_who_id?.id : null,