mirror of
https://github.com/CorentinTh/it-tools.git
synced 2025-05-04 21:37:11 -04:00
feat(Chmod Calculator): octal input and special flags
Handle input of octal rights values and special flags Fix #670 and #527
This commit is contained in:
parent
d3b32cc14e
commit
3ead4d37ad
4 changed files with 314 additions and 10 deletions
|
@ -1,5 +1,5 @@
|
|||
import { describe, expect, it } from 'vitest';
|
||||
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation } from './chmod-calculator.service';
|
||||
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation } from './chmod-calculator.service';
|
||||
|
||||
describe('chmod-calculator', () => {
|
||||
describe('computeChmodOctalRepresentation', () => {
|
||||
|
@ -10,6 +10,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('777');
|
||||
|
@ -20,6 +21,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('000');
|
||||
|
@ -30,6 +32,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('235');
|
||||
|
@ -40,6 +43,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('421');
|
||||
|
@ -50,6 +54,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('124');
|
||||
|
@ -60,11 +65,57 @@ describe('chmod-calculator', () => {
|
|||
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.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', () => {
|
||||
expect(
|
||||
computeChmodSymbolicRepresentation({
|
||||
|
@ -72,6 +123,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('rwxrwxrwx');
|
||||
|
@ -82,6 +134,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('---------');
|
||||
|
@ -92,6 +145,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('-w--wxr-x');
|
||||
|
@ -102,6 +156,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('r---w---x');
|
||||
|
@ -112,6 +167,7 @@ describe('chmod-calculator', () => {
|
|||
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.eql('--x-w-r--');
|
||||
|
@ -122,9 +178,165 @@ describe('chmod-calculator', () => {
|
|||
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.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 },
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
|
|
@ -1,15 +1,21 @@
|
|||
import _ from 'lodash';
|
||||
import type { GroupPermissions, Permissions } from './chmod-calculator.types';
|
||||
import type { GroupPermissions, Permissions, SpecialPermissions } from './chmod-calculator.types';
|
||||
|
||||
export { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation };
|
||||
export { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation };
|
||||
|
||||
function computeChmodOctalRepresentation({ permissions }: { permissions: Permissions }): string {
|
||||
const permissionValue = { read: 4, write: 2, execute: 1 };
|
||||
const specialPermissionValue = { setuid: 4, setgid: 2, stickybit: 1 };
|
||||
|
||||
const getGroupPermissionValue = (permission: GroupPermissions) =>
|
||||
_.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 [
|
||||
getSpecialPermissionValue(permissions.flags || { setuid: false, setgid: false, stickybit: false }),
|
||||
getGroupPermissionValue(permissions.owner),
|
||||
getGroupPermissionValue(permissions.group),
|
||||
getGroupPermissionValue(permissions.public),
|
||||
|
@ -18,13 +24,40 @@ function computeChmodOctalRepresentation({ permissions }: { permissions: Permiss
|
|||
|
||||
function computeChmodSymbolicRepresentation({ permissions }: { permissions: Permissions }): string {
|
||||
const permissionValue = { read: 'r', write: 'w', execute: 'x' };
|
||||
const specialFlagPermission = 'execute';
|
||||
|
||||
const getGroupPermissionValue = (permission: GroupPermissions) =>
|
||||
_.reduce(permission, (acc, isPermSet, key) => acc + (isPermSet ? _.get(permissionValue, key, '') : '-'), '');
|
||||
const getGroupPermissionValue = (permission: GroupPermissions, specialFlag: null | 's' | 't') =>
|
||||
_.reduce(permission, (acc, isPermSet, key) => acc + ((key === specialFlagPermission ? specialFlag : undefined)
|
||||
|| (isPermSet ? _.get(permissionValue, key, '') : '-')), '');
|
||||
|
||||
return [
|
||||
getGroupPermissionValue(permissions.owner),
|
||||
getGroupPermissionValue(permissions.group),
|
||||
getGroupPermissionValue(permissions.public),
|
||||
getGroupPermissionValue(permissions.owner, permissions.flags?.setuid ? 's' : null),
|
||||
getGroupPermissionValue(permissions.group, permissions.flags?.setgid ? 's' : null),
|
||||
getGroupPermissionValue(permissions.public, permissions.flags?.stickybit ? 't' : null),
|
||||
].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),
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,10 +1,17 @@
|
|||
export type Scope = 'read' | 'write' | 'execute';
|
||||
export type Group = 'owner' | 'group' | 'public';
|
||||
export type SpecialFlags = 'setuid' | 'setgid' | 'stickybit';
|
||||
|
||||
export type GroupPermissions = {
|
||||
[k in Scope]: boolean;
|
||||
};
|
||||
|
||||
export type SpecialPermissions = {
|
||||
[k in SpecialFlags]: boolean;
|
||||
};
|
||||
|
||||
export type Permissions = {
|
||||
[k in Group]: GroupPermissions;
|
||||
} & {
|
||||
flags: SpecialPermissions
|
||||
};
|
||||
|
|
|
@ -2,9 +2,10 @@
|
|||
import { useThemeVars } from 'naive-ui';
|
||||
|
||||
import InputCopyable from '../../components/InputCopyable.vue';
|
||||
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation } from './chmod-calculator.service';
|
||||
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation } from './chmod-calculator.service';
|
||||
|
||||
import type { Group, Scope } from './chmod-calculator.types';
|
||||
import { useValidation } from '@/composable/validation';
|
||||
|
||||
const themeVars = useThemeVars();
|
||||
|
||||
|
@ -19,14 +20,51 @@ const permissions = ref({
|
|||
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 },
|
||||
});
|
||||
|
||||
const permissionsInput = ref('000');
|
||||
const permissionsInputValidation = useValidation({
|
||||
source: permissionsInput,
|
||||
rules: [
|
||||
{
|
||||
message: 'Invalid octal permission string',
|
||||
validator: (value) => {
|
||||
try {
|
||||
computePermissionsFromChmodOctalRepresentation(value.trim());
|
||||
return true;
|
||||
}
|
||||
catch {
|
||||
return false;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
});
|
||||
watch(
|
||||
permissionsInput,
|
||||
(newPermissions) => {
|
||||
if (!permissionsInputValidation.isValid) {
|
||||
return;
|
||||
}
|
||||
permissions.value = computePermissionsFromChmodOctalRepresentation(newPermissions.trim());
|
||||
},
|
||||
);
|
||||
|
||||
const octal = computed(() => computeChmodOctalRepresentation({ permissions: permissions.value }));
|
||||
const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions: permissions.value }));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<c-input-text
|
||||
v-model:value="permissionsInput"
|
||||
placeholder="Put your octal permissions here..."
|
||||
label="Copy your octal permissions"
|
||||
:validation="permissionsInputValidation"
|
||||
mb-2
|
||||
/>
|
||||
<n-divider />
|
||||
<n-table :bordered="false" :bottom-bordered="false" single-column class="permission-table">
|
||||
<thead>
|
||||
<tr>
|
||||
|
@ -52,6 +90,20 @@ const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions
|
|||
<n-checkbox v-model:checked="permissions[group][scope]" size="large" />
|
||||
</td>
|
||||
</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>
|
||||
</n-table>
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue