feat(Chmod Calculator): implement symbol form input

Fix #418 and #522
This commit is contained in:
ShareVB 2024-05-06 00:14:13 +02:00
parent ee04bfed87
commit 3d63fde283
5 changed files with 195 additions and 13 deletions

3
components.d.ts vendored
View file

@ -127,6 +127,7 @@ declare module '@vue/runtime-core' {
MetaTagGenerator: typeof import('./src/tools/meta-tag-generator/meta-tag-generator.vue')['default']
MimeTypes: typeof import('./src/tools/mime-types/mime-types.vue')['default']
NavbarButtons: typeof import('./src/components/NavbarButtons.vue')['default']
NCheckbox: typeof import('naive-ui')['NCheckbox']
NCode: typeof import('naive-ui')['NCode']
NCollapseTransition: typeof import('naive-ui')['NCollapseTransition']
NConfigProvider: typeof import('naive-ui')['NConfigProvider']
@ -145,6 +146,7 @@ declare module '@vue/runtime-core' {
NMenu: typeof import('naive-ui')['NMenu']
NScrollbar: typeof import('naive-ui')['NScrollbar']
NSpin: typeof import('naive-ui')['NSpin']
NTable: typeof import('naive-ui')['NTable']
NumeronymGenerator: typeof import('./src/tools/numeronym-generator/numeronym-generator.vue')['default']
OtpCodeGeneratorAndValidator: typeof import('./src/tools/otp-code-generator-and-validator/otp-code-generator-and-validator.vue')['default']
PasswordStrengthAnalyser: typeof import('./src/tools/password-strength-analyser/password-strength-analyser.vue')['default']
@ -159,6 +161,7 @@ declare module '@vue/runtime-core' {
RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView']
RsaKeyPairGenerator: typeof import('./src/tools/rsa-key-pair-generator/rsa-key-pair-generator.vue')['default']
SafelinkDecoder: typeof import('./src/tools/safelink-decoder/safelink-decoder.vue')['default']
SlugifyString: typeof import('./src/tools/slugify-string/slugify-string.vue')['default']
SpanCopyable: typeof import('./src/components/SpanCopyable.vue')['default']
SqlPrettify: typeof import('./src/tools/sql-prettify/sql-prettify.vue')['default']

View file

@ -1,5 +1,5 @@
import { describe, expect, it } from 'vitest';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation } from './chmod-calculator.service';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation, computePermissionsFromChmodSymbolicRepresentation } from './chmod-calculator.service';
describe('chmod-calculator', () => {
describe('computeChmodOctalRepresentation', () => {
@ -339,4 +339,119 @@ describe('chmod-calculator', () => {
});
});
});
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 },
});
});
});
});

View file

@ -1,7 +1,7 @@
import _ from 'lodash';
import type { GroupPermissions, Permissions, SpecialPermissions } from './chmod-calculator.types';
export { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation };
export { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation, computePermissionsFromChmodSymbolicRepresentation };
function computeChmodOctalRepresentation({ permissions }: { permissions: Permissions }): string {
const permissionValue = { read: 4, write: 2, execute: 1 };
@ -61,3 +61,28 @@ function computePermissionsFromChmodOctalRepresentation(octalPermissions: string
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);
}

View file

@ -2,7 +2,7 @@
import { useThemeVars } from 'naive-ui';
import InputCopyable from '../../components/InputCopyable.vue';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation } from './chmod-calculator.service';
import { computeChmodOctalRepresentation, computeChmodSymbolicRepresentation, computePermissionsFromChmodOctalRepresentation, computePermissionsFromChmodSymbolicRepresentation } from './chmod-calculator.service';
import type { Group, Scope } from './chmod-calculator.types';
import { useValidation } from '@/composable/validation';
@ -23,9 +23,9 @@ const permissions = ref({
flags: { setuid: false, setgid: false, stickybit: false },
});
const permissionsInput = ref('000');
const permissionsInputValidation = useValidation({
source: permissionsInput,
const octalPermissionsInput = ref('000');
const octalPermissionsInputValidation = useValidation({
source: octalPermissionsInput,
rules: [
{
message: 'Invalid octal permission string',
@ -42,15 +42,43 @@ const permissionsInputValidation = useValidation({
],
});
watch(
permissionsInput,
octalPermissionsInput,
(newPermissions) => {
if (!permissionsInputValidation.isValid) {
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 symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions: permissions.value }));
</script>
@ -58,13 +86,25 @@ const symbolic = computed(() => computeChmodSymbolicRepresentation({ permissions
<template>
<div>
<c-input-text
v-model:value="permissionsInput"
v-model:value="octalPermissionsInput"
placeholder="Put your octal permissions here..."
label="Copy your octal permissions"
:validation="permissionsInputValidation"
: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">
<thead>
<tr>

View file

@ -1,11 +1,10 @@
import { FileInvoice } from '@vicons/tabler';
import { defineTool } from '../tool';
import { translate } from '@/plugins/i18n.plugin';
export const tool = defineTool({
name: translate('tools.chmod-calculator.title'),
name: 'Chmod calculator',
path: '/chmod-calculator',
description: translate('tools.chmod-calculator.description'),
description: 'Compute your chmod permissions and commands with this online chmod calculator.',
keywords: [
'chmod',
'calculator',