This commit is contained in:
sharevb 2024-08-25 20:25:04 +00:00 committed by GitHub
commit 93e3d32a2c
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 635 additions and 13 deletions

3
components.d.ts vendored
View file

@ -129,6 +129,7 @@ declare module '@vue/runtime-core' {
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default'] MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default'] MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default'] NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCode: typeof import('naive-ui')['NCode'] NCode: typeof import('naive-ui')['NCode']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition'] NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NConfigProvider: typeof import('naive-ui')['NConfigProvider'] NConfigProvider: typeof import('naive-ui')['NConfigProvider']
@ -143,6 +144,8 @@ declare module '@vue/runtime-core' {
NLayoutSider: typeof import('naive-ui')['NLayoutSider'] NLayoutSider: typeof import('naive-ui')['NLayoutSider']
NMenu: typeof import('naive-ui')['NMenu'] NMenu: typeof import('naive-ui')['NMenu']
NScrollbar: typeof import('naive-ui')['NScrollbar'] NScrollbar: typeof import('naive-ui')['NScrollbar']
NSpin: typeof import('naive-ui')['NSpin']
NTable: typeof import('naive-ui')['NTable']
NSlider: typeof import('naive-ui')['NSlider'] NSlider: typeof import('naive-ui')['NSlider']
NSwitch: typeof import('naive-ui')['NSwitch'] NSwitch: typeof import('naive-ui')['NSwitch']
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default'] NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']

View file

@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest'; import { describe, expect, it } from 'vitest';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation } from './chmod-calculator.service'; import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation, computePermissionsFromChmodSymbolicRepresentation, computeUmaskRepresentation } from './chmod-calculator.service';
describe('chmod-calculator', () => { describe('chmod-calculator', () => {
describe('computeChmodOctalRepresentation', () => { describe('computeChmodOctalRepresentation', () => {
@ -10,6 +10,7 @@ describe('chmod-calculator', () => {
owner: { read: true, write: true, execute: true }, owner: { read: true, write: true, execute: true },
group: { read: true, write: true, execute: true }, group: { read: true, write: true, execute: true },
public: { read: true, write: true, execute: true }, public: { read: true, write: true, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('777'); ).to.eql('777');
@ -20,6 +21,7 @@ describe('chmod-calculator', () => {
owner: { read: false, write: false, execute: false }, owner: { read: false, write: false, execute: false },
group: { read: false, write: false, execute: false }, group: { read: false, write: false, execute: false },
public: { read: false, write: false, execute: false }, public: { read: false, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('000'); ).to.eql('000');
@ -30,6 +32,7 @@ describe('chmod-calculator', () => {
owner: { read: false, write: true, execute: false }, owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: true }, group: { read: false, write: true, execute: true },
public: { read: true, write: false, execute: true }, public: { read: true, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('235'); ).to.eql('235');
@ -40,6 +43,7 @@ describe('chmod-calculator', () => {
owner: { read: true, write: false, execute: false }, owner: { read: true, write: false, execute: false },
group: { read: false, write: true, execute: false }, group: { read: false, write: true, execute: false },
public: { read: false, write: false, execute: true }, public: { read: false, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('421'); ).to.eql('421');
@ -50,6 +54,7 @@ describe('chmod-calculator', () => {
owner: { read: false, write: false, execute: true }, owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false }, group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false }, public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('124'); ).to.eql('124');
@ -60,11 +65,57 @@ describe('chmod-calculator', () => {
owner: { read: false, write: true, execute: false }, owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false }, group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false }, public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('222'); ).to.eql('222');
});
expect(
computeChmodOctalRepresentation({
permissions: {
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: true, setgid: true, stickybit: true },
},
}),
).to.eql('7222');
expect(
computeChmodOctalRepresentation({
permissions: {
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: true, setgid: false, stickybit: false },
},
}),
).to.eql('4222');
expect(
computeChmodOctalRepresentation({
permissions: {
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: true, stickybit: false },
},
}),
).to.eql('2222');
expect(
computeChmodOctalRepresentation({
permissions: {
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: true },
},
}),
).to.eql('1222');
});
});
describe('computeChmodSymbolicRepresentation', () => {
it('get the symbolic representation from permissions', () => { it('get the symbolic representation from permissions', () => {
expect( expect(
computeChmodSymbolicRepresentation({ computeChmodSymbolicRepresentation({
@ -72,6 +123,7 @@ describe('chmod-calculator', () => {
owner: { read: true, write: true, execute: true }, owner: { read: true, write: true, execute: true },
group: { read: true, write: true, execute: true }, group: { read: true, write: true, execute: true },
public: { read: true, write: true, execute: true }, public: { read: true, write: true, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('rwxrwxrwx'); ).to.eql('rwxrwxrwx');
@ -82,6 +134,7 @@ describe('chmod-calculator', () => {
owner: { read: false, write: false, execute: false }, owner: { read: false, write: false, execute: false },
group: { read: false, write: false, execute: false }, group: { read: false, write: false, execute: false },
public: { read: false, write: false, execute: false }, public: { read: false, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('---------'); ).to.eql('---------');
@ -92,6 +145,7 @@ describe('chmod-calculator', () => {
owner: { read: false, write: true, execute: false }, owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: true }, group: { read: false, write: true, execute: true },
public: { read: true, write: false, execute: true }, public: { read: true, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('-w--wxr-x'); ).to.eql('-w--wxr-x');
@ -102,6 +156,7 @@ describe('chmod-calculator', () => {
owner: { read: true, write: false, execute: false }, owner: { read: true, write: false, execute: false },
group: { read: false, write: true, execute: false }, group: { read: false, write: true, execute: false },
public: { read: false, write: false, execute: true }, public: { read: false, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('r---w---x'); ).to.eql('r---w---x');
@ -112,6 +167,7 @@ describe('chmod-calculator', () => {
owner: { read: false, write: false, execute: true }, owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false }, group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false }, public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('--x-w-r--'); ).to.eql('--x-w-r--');
@ -122,9 +178,382 @@ describe('chmod-calculator', () => {
owner: { read: false, write: true, execute: false }, owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false }, group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false }, public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}, },
}), }),
).to.eql('-w--w--w-'); ).to.eql('-w--w--w-');
expect(
computeChmodSymbolicRepresentation({
permissions: {
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: true },
},
}),
).to.eql('--x-w-r-t');
expect(
computeChmodSymbolicRepresentation({
permissions: {
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: true, stickybit: true },
},
}),
).to.eql('--x-wsr-t');
expect(
computeChmodSymbolicRepresentation({
permissions: {
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: true, setgid: true, stickybit: true },
},
}),
).to.eql('--s-wsr-t');
expect(
computeChmodSymbolicRepresentation({
permissions: {
owner: { read: true, write: false, execute: true },
group: { read: true, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.eql('r-xrw-r--');
expect(
computeChmodSymbolicRepresentation({
permissions: {
owner: { read: true, write: true, execute: true },
group: { read: true, write: true, execute: true },
public: { read: true, write: true, execute: true },
flags: { setuid: true, setgid: true, stickybit: true },
},
}),
).to.eql('rwsrwsrwt');
});
});
describe('computePermissionsFromChmodOctalRepresentation', () => {
it('throws on invalid octal values', () => {
expect(() => computePermissionsFromChmodOctalRepresentation('12')).to.throw();
expect(() => computePermissionsFromChmodOctalRepresentation('12345')).to.throw();
expect(() => computePermissionsFromChmodOctalRepresentation('999')).to.throw();
expect(() => computePermissionsFromChmodOctalRepresentation('9999')).to.throw();
});
it('get permissions from octal representation', () => {
expect(
computePermissionsFromChmodOctalRepresentation('777'),
).to.eql({
owner: { read: true, write: true, execute: true },
group: { read: true, write: true, execute: true },
public: { read: true, write: true, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('000'),
).to.eql({
owner: { read: false, write: false, execute: false },
group: { read: false, write: false, execute: false },
public: { read: false, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('235'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: true },
public: { read: true, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('421'),
).to.eql({
owner: { read: true, write: false, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('124'),
).to.eql({
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('222'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('7222'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: true, setgid: true, stickybit: true },
});
expect(
computePermissionsFromChmodOctalRepresentation('4222'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: true, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('2222'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: true, stickybit: false },
});
expect(
computePermissionsFromChmodOctalRepresentation('1222'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: true },
});
});
});
describe('computePermissionsFromChmodSymbolicRepresentation', () => {
it('throws on invalid symbolic values', () => {
expect(() => computePermissionsFromChmodSymbolicRepresentation('rr---')).to.throw();
expect(() => computePermissionsFromChmodSymbolicRepresentation('rwxrwx--w')).to.throw();
});
it('get permissions from symbolic representation', () => {
expect(
computePermissionsFromChmodSymbolicRepresentation('dr-xr-xr-x'),
).to.eql({
owner: { read: true, write: false, execute: true },
group: { read: true, write: false, execute: true },
public: { read: true, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('-rw-r--r--'),
).to.eql({
owner: { read: true, write: true, execute: false },
group: { read: true, write: false, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('rwxrwxrwx'),
).to.eql({
owner: { read: true, write: true, execute: true },
group: { read: true, write: true, execute: true },
public: { read: true, write: true, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('---------'),
).to.eql({
owner: { read: false, write: false, execute: false },
group: { read: false, write: false, execute: false },
public: { read: false, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('r---wxr-x'),
).to.eql({
owner: { read: true, write: false, execute: false },
group: { read: false, write: true, execute: true },
public: { read: true, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('r---w---x'),
).to.eql({
owner: { read: true, write: false, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('--x-w-r--'),
).to.eql({
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('-w--w--w-'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('-ws-ws-wt'),
).to.eql({
owner: { read: false, write: true, execute: true },
group: { read: false, write: true, execute: true },
public: { read: false, write: true, execute: true },
flags: { setuid: true, setgid: true, stickybit: true },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('-ws-w--w-'),
).to.eql({
owner: { read: false, write: true, execute: true },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: true, setgid: false, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('-w--ws-w-'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: true },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: true, stickybit: false },
});
expect(
computePermissionsFromChmodSymbolicRepresentation('-w--w--wt'),
).to.eql({
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: true },
flags: { setuid: false, setgid: false, stickybit: true },
});
});
});
describe('computeUmaskRepresentation', () => {
it('get the umask from permissions', () => {
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: true, write: true, execute: true },
group: { read: true, write: true, execute: true },
public: { read: true, write: true, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.deep.eq({
octal: '000',
symbolic: 'umask u=rwx,g=rwx,o=rwx',
});
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: false, write: false, execute: false },
group: { read: false, write: false, execute: false },
public: { read: false, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.deep.eq({
octal: '777',
symbolic: 'umask u=,g=,o=',
});
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: true },
public: { read: true, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.deep.eq({
octal: '542',
symbolic: 'umask u=w,g=wx,o=rx',
});
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: true, write: false, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: false, execute: true },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.deep.eq({
octal: '356',
symbolic: 'umask u=r,g=w,o=x',
});
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.deep.eq({
octal: '653',
symbolic: 'umask u=x,g=w,o=r',
});
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: false, write: true, execute: false },
group: { read: false, write: true, execute: false },
public: { read: false, write: true, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
},
}),
).to.deep.eq({
octal: '555',
symbolic: 'umask u=w,g=w,o=w',
});
expect(
computeUmaskRepresentation({
permissions: {
owner: { read: false, write: false, execute: true },
group: { read: false, write: true, execute: false },
public: { read: true, write: false, execute: false },
flags: { setuid: true, setgid: true, stickybit: true },
},
}),
).to.deep.eq({
octal: '653',
symbolic: 'umask u=x,g=w,o=r',
});
}); });
}); });
}); });

View file

@ -1,15 +1,21 @@
import _ from 'lodash'; import _ from 'lodash';
import type { GroupPermissions, Permissions } from './chmod-calculator.types'; import type { GroupPermissions, Permissions, SpecialPermissions } from './chmod-calculator.types';
export { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation }; export { computeUmaskRepresentation, computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation, computePermissionsFromChmodSymbolicRepresentation };
function computeChmodOctalRepresentation({ permissions }: { permissions: Permissions }): string { function computeChmodOctalRepresentation({ permissions }: { permissions: Permissions }): string {
const permissionValue = { read: 4, write: 2, execute: 1 }; const permissionValue = { read: 4, write: 2, execute: 1 };
const specialPermissionValue = { setuid: 4, setgid: 2, stickybit: 1 };
const getGroupPermissionValue = (permission: GroupPermissions) => const getGroupPermissionValue = (permission: GroupPermissions) =>
_.reduce(permission, (acc, isPermSet, key) => acc + (isPermSet ? _.get(permissionValue, key, 0) : 0), 0); _.reduce(permission, (acc, isPermSet, key) => acc + (isPermSet ? _.get(permissionValue, key, 0) : 0), 0);
const getSpecialPermissionValue = (permission: SpecialPermissions) => {
const octalValue = _.reduce(permission, (acc, isPermSet, key) => acc + (isPermSet ? _.get(specialPermissionValue, key, 0) : 0), 0);
return octalValue > 0 ? octalValue.toString() : '';
};
return [ return [
getSpecialPermissionValue(permissions.flags || { setuid: false, setgid: false, stickybit: false }),
getGroupPermissionValue(permissions.owner), getGroupPermissionValue(permissions.owner),
getGroupPermissionValue(permissions.group), getGroupPermissionValue(permissions.group),
getGroupPermissionValue(permissions.public), getGroupPermissionValue(permissions.public),
@ -18,13 +24,92 @@ function computeChmodOctalRepresentation({ permissions }: { permissions: Permiss
function computeChmodSymbolicRepresentation({ permissions }: { permissions: Permissions }): string { function computeChmodSymbolicRepresentation({ permissions }: { permissions: Permissions }): string {
const permissionValue = { read: 'r', write: 'w', execute: 'x' }; const permissionValue = { read: 'r', write: 'w', execute: 'x' };
const specialFlagPermission = 'execute';
const getGroupPermissionValue = (permission: GroupPermissions) => const getGroupPermissionValue = (permission: GroupPermissions, specialFlag: null | 's' | 't') =>
_.reduce(permission, (acc, isPermSet, key) => acc + (isPermSet ? _.get(permissionValue, key, '') : '-'), ''); _.reduce(permission, (acc, isPermSet, key) => acc + ((key === specialFlagPermission ? specialFlag : undefined)
|| (isPermSet ? _.get(permissionValue, key, '') : '-')), '');
return [ return [
getGroupPermissionValue(permissions.owner), getGroupPermissionValue(permissions.owner, permissions.flags?.setuid ? 's' : null),
getGroupPermissionValue(permissions.group), getGroupPermissionValue(permissions.group, permissions.flags?.setgid ? 's' : null),
getGroupPermissionValue(permissions.public), getGroupPermissionValue(permissions.public, permissions.flags?.stickybit ? 't' : null),
].join(''); ].join('');
} }
function computePermissionsFromChmodOctalRepresentation(octalPermissions: string): Permissions {
const permissionValue = { read: 4, write: 2, execute: 1 };
const specialPermissionValue = { setuid: 4, setgid: 2, stickybit: 1 };
if (!octalPermissions || !octalPermissions.match(/^[0-7]{3,4}$/)) {
throw new Error(`Invalid octal permissions (must be 3 or 4 octal digits): ${octalPermissions}`);
}
const fullOctalPermissions = octalPermissions.length === 3 ? `0${octalPermissions}` : octalPermissions;
const hasSet = (position: number, flagValue: number) => (Number(fullOctalPermissions[position]) & flagValue) === flagValue;
function computePermissionObject<T>(permissionSet: object, position: number): T {
return _.reduce(permissionSet, (acc, flag, key) => ({ ...acc, [key]: hasSet(position, flag) }), {}) as T;
}
const flagsPosition = 0;
const ownerPosition = 1;
const groupPosition = 2;
const publicPosition = 3;
return {
owner: computePermissionObject(permissionValue, ownerPosition),
group: computePermissionObject(permissionValue, groupPosition),
public: computePermissionObject(permissionValue, publicPosition),
flags: computePermissionObject(specialPermissionValue, flagsPosition),
};
}
function computePermissionsFromChmodSymbolicRepresentation(symbolicPermissions: string): Permissions {
const formatRegex = /^[-dlbcsp]?([r-])([w-])([xs-])([r-])([w-])([xs-])([r-])([w-])([xt-])$/;
if (!symbolicPermissions || !symbolicPermissions.match(formatRegex)) {
throw new Error(`Invalid string permissions (must be in form 'rwxrwxrwx'): ${symbolicPermissions}`);
}
const [_, rOwner, wOwner, xOwner, rGroup, wGroup, xGroup, rAll, wAll, xAll] = formatRegex.exec(symbolicPermissions) || [];
const getOctal = (flag: string, flagLetter: string, flagValue: number) => flag === flagLetter ? flagValue : 0;
const owner = getOctal(rOwner, 'r', 4)
+ getOctal(wOwner, 'w', 2)
+ getOctal(xOwner, 'x', 1) + getOctal(xOwner, 's', 1);
const groups = getOctal(rGroup, 'r', 4)
+ getOctal(wGroup, 'w', 2)
+ getOctal(xGroup, 'x', 1) + getOctal(xGroup, 's', 1);
const all = getOctal(rAll, 'r', 4)
+ getOctal(wAll, 'w', 2)
+ getOctal(xAll, 'x', 1) + getOctal(xAll, 't', 1);
const flags = getOctal(xOwner, 's', 4)
+ getOctal(xGroup, 's', 2)
+ getOctal(xAll, 't', 1);
const octalString = `${(flags > 0 ? flags : '')}${owner}${groups}${all}`;
return computePermissionsFromChmodOctalRepresentation(octalString);
}
function computeUmaskRepresentation({ permissions }: { permissions: Permissions }): {
octal: string
symbolic: string
} {
const permissionValue = { read: 'r', write: 'w', execute: 'x' };
const getGroupPermissionValue = (permission: GroupPermissions) =>
_.reduce(permission, (acc, isPermSet, key) => acc + ((isPermSet ? _.get(permissionValue, key, '') : '')), '');
const symbolic = `umask u=${getGroupPermissionValue(permissions.owner)},g=${getGroupPermissionValue(permissions.group)},o=${getGroupPermissionValue(permissions.public)}`;
const octal = (0o777 - Number.parseInt(
computeChmodOctalRepresentation({
permissions:
{
owner: permissions.owner,
group: permissions.group,
public: permissions.public,
flags: { setuid: false, setgid: false, stickybit: false },
},
}), 8))
.toString(8)
.padStart(3, '0');
return {
symbolic, octal,
};
}

View file

@ -1,10 +1,17 @@
export type Scope = 'read' | 'write' | 'execute'; export type Scope = 'read' | 'write' | 'execute';
export type Group = 'owner' | 'group' | 'public'; export type Group = 'owner' | 'group' | 'public';
export type SpecialFlags = 'setuid' | 'setgid' | 'stickybit';
export type GroupPermissions = { export type GroupPermissions = {
[k in Scope]: boolean; [k in Scope]: boolean;
}; };
export type SpecialPermissions = {
[k in SpecialFlags]: boolean;
};
export type Permissions = { export type Permissions = {
[k in Group]: GroupPermissions; [k in Group]: GroupPermissions;
} & {
flags: SpecialPermissions
}; };

View file

@ -2,9 +2,10 @@
import { useThemeVars } from 'naive-ui'; import { useThemeVars } from 'naive-ui';
import InputCopyable from '../../components/InputCopyable.vue'; import InputCopyable from '../../components/InputCopyable.vue';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation } from './chmod-calculator.service'; import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation, computePermissionsFromChmodSymbolicRepresentation, computeUmaskRepresentation } from './chmod-calculator.service';
import type { Group, Scope } from './chmod-calculator.types'; import type { Group, Scope } from './chmod-calculator.types';
import { useValidation } from '@/composable/validation';
const themeVars = useThemeVars(); const themeVars = useThemeVars();
@ -19,14 +20,92 @@ const permissions = ref({
owner: { read: false, write: false, execute: false }, owner: { read: false, write: false, execute: false },
group: { read: false, write: false, execute: false }, group: { read: false, write: false, execute: false },
public: { read: false, write: false, execute: false }, public: { read: false, write: false, execute: false },
flags: { setuid: false, setgid: false, stickybit: false },
}); });
const octalPermissionsInput = ref('000');
const octalPermissionsInputValidation = useValidation({
source: octalPermissionsInput,
rules: [
{
message: 'Invalid octal permission string',
validator: (value) => {
try {
computePermissionsFromChmodOctalRepresentation(value.trim());
return true;
}
catch {
return false;
}
},
},
],
});
watch(
octalPermissionsInput,
(newPermissions) => {
if (!octalPermissionsInputValidation.isValid) {
return;
}
permissions.value = computePermissionsFromChmodOctalRepresentation(newPermissions.trim());
},
);
const symbolicPermissionsInput = ref('---------');
const symbolicPermissionsInputValidation = useValidation({
source: symbolicPermissionsInput,
rules: [
{
message: 'Invalid symbolic permission string',
validator: (value) => {
try {
computePermissionsFromChmodSymbolicRepresentation(value.trim());
return true;
}
catch {
return false;
}
},
},
],
});
watch(
symbolicPermissionsInput,
(newPermissions) => {
if (!symbolicPermissionsInputValidation.isValid) {
return;
}
permissions.value = computePermissionsFromChmodSymbolicRepresentation(newPermissions.trim());
},
);
const octal = computed(() => computeChmodOctalRepresentation({ permissions: permissions.value })); const octal = computed(() => computeChmodOctalRepresentation({ permissions: permissions.value }));
const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions: permissions.value })); const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions: permissions.value }));
const umask = computed(() => computeUmaskRepresentation({ permissions: permissions.value }));
</script> </script>
<template> <template>
<div> <div>
<c-input-text
v-model:value="octalPermissionsInput"
placeholder="Put your octal permissions here..."
label="Copy your octal permissions"
:validation="octalPermissionsInputValidation"
mb-2
/>
<n-divider />
<c-input-text
v-model:value="symbolicPermissionsInput"
placeholder="Put your symbolic permissions here..."
label="Copy your symbolic permissions"
:validation="symbolicPermissionsInputValidation"
mb-2
/>
<n-divider />
<n-table :bordered="false" :bottom-bordered="false" single-column class="permission-table"> <n-table :bordered="false" :bottom-bordered="false" single-column class="permission-table">
<thead> <thead>
<tr> <tr>
@ -52,6 +131,20 @@ const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions
<n-checkbox v-model:checked="permissions[group][scope]" size="large" /> <n-checkbox v-model:checked="permissions[group][scope]" size="large" />
</td> </td>
</tr> </tr>
<tr>
<td class="line-header">
Flags
</td>
<td class="text-center">
<n-checkbox v-model:checked="permissions.flags.setuid" size="large" />
</td>
<td class="text-center">
<n-checkbox v-model:checked="permissions.flags.setgid" size="large" />
</td>
<td class="text-center">
<n-checkbox v-model:checked="permissions.flags.stickybit" size="large" />
</td>
</tr>
</tbody> </tbody>
</n-table> </n-table>
@ -63,6 +156,11 @@ const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions
</div> </div>
<InputCopyable :value="`chmod ${octal} path`" readonly /> <InputCopyable :value="`chmod ${octal} path`" readonly />
<c-card title="Umask">
<InputCopyable :value="umask.octal" readonly />
<InputCopyable :value="umask.symbolic" readonly />
</c-card>
</div> </div>
</template> </template>

View file

@ -1,11 +1,10 @@
import { FileInvoice } from '@vicons/tabler'; import { FileInvoice } from '@vicons/tabler';
import { defineTool } from '../tool'; import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({ export const tool = defineTool({
name: translate('tools.chmod-calculator.title'), name: 'Chmod calculator',
path: '/chmod-calculator', path: '/chmod-calculator',
description: translate('tools.chmod-calculator.description'), description: 'Compute your chmod permissions and commands with this online chmod calculator.',
keywords: [ keywords: [
'chmod', 'chmod',
'calculator', 'calculator',
@ -17,6 +16,7 @@ export const tool = defineTool({
'recursive', 'recursive',
'generator', 'generator',
'octal', 'octal',
'umask',
], ],
component: () => import('./chmod-calculator.vue'), component: () => import('./chmod-calculator.vue'),
icon: FileInvoice, icon: FileInvoice,