diff --git a/src/rt/crypto/generate.ts b/src/rt/crypto/generate.ts new file mode 100644 index 0000000..ac3b710 --- /dev/null +++ b/src/rt/crypto/generate.ts @@ -0,0 +1,21 @@ +type SHA256_String = string + +class Crypto { + protected inner: string + + constructor(inner: string) { + this.inner = inner + } + + public async sha256_string(): Promise { + const encoder = new TextEncoder() + const hash = await crypto.subtle.digest("SHA-256", encoder.encode(this.inner)) + const hash_as_uint8 = new Uint8Array(hash) + return Array.from(hash_as_uint8).map(byte => byte.toString(16).padStart(2, "0")).join("") + } +} + +export default Crypto +export { + type SHA256_String +} \ No newline at end of file diff --git a/src/rt/fs.ts b/src/rt/fs.ts index 8ca2d4c..1436882 100644 --- a/src/rt/fs.ts +++ b/src/rt/fs.ts @@ -1,11 +1,16 @@ -import { Permissions } from "./rfwfs/main" - -import rfwfs from "./rfwfs/main" +import rfwfs, { DirectoryInRoot, Permissions } from "./rfwfs/main" const time_now = (Date.now()/1000) | 0 - const fs = new rfwfs() -fs.add_file(default_name, default_permissions) +const bin = rfwfs.directory_in_root({ + name: "bin", + timestamp: time_now, + permissions: Permissions.r | Permissions.w +}) + +fs.push_bulk_unsafe([ + bin.dir as DirectoryInRoot +]) export default fs \ No newline at end of file diff --git a/src/rt/rfwfs/enum/status.ts b/src/rt/rfwfs/enum/status.ts deleted file mode 100644 index 8abbb63..0000000 --- a/src/rt/rfwfs/enum/status.ts +++ /dev/null @@ -1,21 +0,0 @@ -const enum PushStatus { - Ok, - Duplicate, - Denied, -} -const enum ReadStatus { - Ok, - NotFound, - Denied, -} -const enum ExecuteStatus { - Ok, - Panic, - Denied, -} - -export { - ExecuteStatus, - ReadStatus, - PushStatus, -} \ No newline at end of file diff --git a/src/rt/rfwfs/hash.ts b/src/rt/rfwfs/hash.ts deleted file mode 100644 index 5f64006..0000000 --- a/src/rt/rfwfs/hash.ts +++ /dev/null @@ -1,11 +0,0 @@ -async function hash(inner_as_string: string) { - const encoder = new TextEncoder() - const hash = await crypto.subtle.digest("SHA-256", encoder.encode(inner_as_string)) - const hash_as_uint8 = new Uint8Array(hash) - return Array.from(hash_as_uint8).map(byte => byte.toString(16).padStart(2, "0")).join("") -} - -export default async function generate_sha256(inner_as_string: string) { - const sha256 = await hash(inner_as_string) - return sha256 -} \ No newline at end of file diff --git a/src/rt/rfwfs/index.ts b/src/rt/rfwfs/index.ts index d68ca0b..795f9df 100644 --- a/src/rt/rfwfs/index.ts +++ b/src/rt/rfwfs/index.ts @@ -1,7 +1,11 @@ import { type Entry } from "./main" -import { wrap_bsearch, type WrapBSearch } from "./wrap" +import wrap, { WrapResult } from "./wrap" -export default function directory_search(entry_collection: T[], file_name: string): WrapBSearch | undefined { +function wrap_bsearch(index: number, result: T): WrapResult { + return wrap(result, index) +} + +export default function directory_search(entry_collection: T[], file_name: string): WrapResult | undefined { let start = 0 let end = entry_collection.length-1 while (start<=end) { diff --git a/src/rt/rfwfs/main.ts b/src/rt/rfwfs/main.ts index ba5ac34..7602fb3 100644 --- a/src/rt/rfwfs/main.ts +++ b/src/rt/rfwfs/main.ts @@ -1,5 +1,4 @@ -import { ReadStatus, PushStatus, ExecuteStatus } from "./enum/status" -import { wrap_entry, wrap_none, wrap_binary, type WrapResultEntry, type WrapResultNone, type WrapBinary } from "./wrap" +import wrap, { type WrapResult, ConstEnum, Option } from "./wrap" import directory_search from "./index" @@ -15,230 +14,192 @@ const enum Permissions { x = 1<<2, none = 1<<3, } -const enum ROOT_ID { TRUNK = "/" } +const enum ROOT_ID { + TRUNK = "/", + NAME = "root" +} -type FileInner = string | number -type BinaryError = string +const enum PushStatus { + Ok, + Duplicate, + Denied, +} +const enum ReadStatus { + Ok, + NotFound, + Denied, +} -type BinaryEntry = () => EntryStripped -type BinaryLambda = (binary_entry: BinaryEntry) => void +interface UserPermissions { + readonly root: Permissions.r | Permissions.w | Permissions.x, //Flip OR + [index: string]: Permissions +} -type Directory = EntryCollection -type DirectoryAny = EntryCollection -type DirectoryAnyDepth = EntryCollection +interface Entry> { + readonly type: T, + permissions: UserPermissions, + timestamp: number, + name: N +} -interface EntryStripped { - readonly type: EntryType, +interface DirectoryContainer extends Entry { + files: EntryValue, + parent: T | null +} + +type Directory = DirectoryContainer> +type DirectoryInRoot = DirectoryContainer + +interface Root extends Entry { + timestamp: number, + parent: null, + files: EntryValue, +} + +interface DirectoryInRootProperties { permissions: Permissions, - timestamp: EntryValue, - name: EntryValue, + name: string + timestamp: number, } -interface Entry extends EntryStripped { - parent: T, +interface DirectoryProperties extends DirectoryInRootProperties { + parent: RfwfsDirectory, } -interface EntryFile extends Entry { - inner: EntryValue, - hash: string, +interface FileProperties extends Entry { + } -interface EntryCollection extends Entry { - inner: RfwfsDirectory, +/** Other directory types that can be treated as a single arbitrary directory. + +Do not cast. +*/ +type DirectoryAssociates = Directory | DirectoryInRoot | Root +/** Other entry types that can be treated as a single arbitrary entry. + +Do not cast. +*/ +type EntryAssociates = Entry | Root + +type WrapResultEntry = WrapResult +type WrapResultNone = WrapResult + +function wrap_entry(status: T, result?: U): WrapResultEntry { + return wrap(result, status) } -interface EntryBinary extends Entry { - inner: RfwfsBinary +function wrap_none(status: T): WrapResultNone { + return wrap(Option.None, status) } -interface Root { - readonly type: EntryType, - readonly permissions: Permissions, - readonly timestamp: EntryValueRoot, - readonly parent: null, - readonly inner: RfwfsRootDirectory, - readonly name: ROOT_ID.TRUNK, +function fs_dir_sort(dir: DirectoryAssociates) { + dir.files.inner.sort((a,z) => a.name.inner.localeCompare(z.name.inner)) } -function strip_entry(entry: T): EntryStripped { - return { - type: entry.type, - permissions: entry.permissions, - timestamp: entry.timestamp, - name: entry.name, - } -} - -function fs_dir_clone(directory: T[], file_name: string): WrapResultEntry { - const clone_find = directory_search(directory, file_name) +function fs_dir_clone(dir: DirectoryAssociates, file_name: string): WrapResultEntry { + const clone_find = directory_search(dir.files.inner, file_name) if (clone_find) { - return wrap_entry(ReadStatus.Ok, { ...clone_find.result }) + return wrap_entry(ReadStatus.Ok, { ...clone_find.result as T }) } return wrap_entry(ReadStatus.NotFound) } -function fs_dir_find(directory: T[], file_name: string): WrapResultEntry { - const file_search = directory_search(directory, file_name) +function fs_dir_find(dir: DirectoryAssociates, file_name: string): WrapResultEntry { + const file_search = directory_search(dir.files.inner, file_name) if (file_search) { - return wrap_entry(ReadStatus.Ok, file_search.result) + return wrap_entry(ReadStatus.Ok, file_search.result as T) } return wrap_entry(ReadStatus.NotFound) } -function fs_dir_push(directory: T[], entry: E) { - const no_duplicates = directory_search(directory, entry.name.inner) +function fs_dir_push(dir: DirectoryAssociates, entry: Entry) { + const no_duplicates = directory_search(dir.files.inner, entry.name.inner) if (!no_duplicates) { - directory.push(entry) - directory.sort() + dir.files.inner.push(entry) + fs_dir_sort(dir) return wrap_none(PushStatus.Ok) } return wrap_none(PushStatus.Duplicate) } -function fs_dir_pop(directory: T[], file_name: string): WrapResultEntry { - const pop_find = directory_search(directory, file_name) +function fs_dir_pop(dir: DirectoryAssociates, file_name: string): WrapResultEntry { + const pop_find = directory_search(dir.files.inner, file_name) if (pop_find) { - directory.splice(pop_find.status, 1) - return wrap_entry(ReadStatus.Ok, pop_find.result) + dir.files.inner.splice(pop_find.status, 1) + return wrap_entry(ReadStatus.Ok, pop_find.result as T) } return wrap_entry(ReadStatus.NotFound) } -class EntryValue { - public inner: T; - protected entry: Entry; +class EntryValue { + public inner: V; + protected user_perms: UserPermissions; - constructor(entry: Entry, inner_default: T) { - this.inner = inner_default - this.entry = entry + constructor(user: UserPermissions, value: V) { + this.inner = value + this.user_perms = user } - /** - Safe write - */ - public write(item: I): boolean { - if (rfwfs.write_access(this.entry.permissions)) { - this.inner = item + public read(): V | undefined { + return rfwfs_lib.read_access(this.user_perms.permissions) ? this.inner : undefined + } + + public write(new_value: T): boolean { + if (rfwfs_lib.write_access(this.user_perms.permissions)) { + this.inner = new_value return true } return false } - - /** - Convert the inner file value - - Same as `write` but mutates the inner value `T` into a `FileInner` - */ - public write_into(item: FileInner): boolean { - return this.write(item as T) - } - - public read(): T | undefined { - return rfwfs.write_access(this.entry.permissions) ? this.inner : undefined - } -} - -class EntryValueRoot { - protected inner: T; - - constructor(inner: T) { - this.inner = inner - } - - public read(): T { - return this.inner - } } class RfwfsDirectory { - public directory: T[]; - protected entry: Entry; + public dir: DirectoryAssociates; - constructor(entry: Entry, directory: T[]) { - this.directory = directory - this.entry = entry + constructor(dir: DirectoryAssociates) { + this.dir = dir } public sort() { - this.directory.sort((a,z) => a.name.inner.localeCompare(z.name.inner)) + fs_dir_sort(this.dir) } - public clone(file_name: string): WrapResultEntry { - if (rfwfs.read_write_access(this.entry.permissions)) { - return fs_dir_clone(this.directory, file_name) + public clone(file_name: string): WrapResultEntry { + if (rfwfs.read_write_access(this.dir.permissions)) { + return fs_dir_clone(this.dir, file_name) } return wrap_entry(ReadStatus.Denied) } - public find(file_name: string): WrapResultEntry { - if (rfwfs.read_write_access(this.entry.permissions)) { - return fs_dir_find(this.directory, file_name) + public find(file_name: string): WrapResultEntry { + if (rfwfs.read_write_access(this.dir.permissions)) { + return fs_dir_find(this.dir, file_name) } return wrap_entry(ReadStatus.Denied) } - public push(entry: E): WrapResultNone { - if (rfwfs.read_write_access(this.entry.permissions)) { - return fs_dir_push(this.directory, entry) + public push(entry: E): WrapResultNone { + if (rfwfs.read_write_access(this.dir.permissions)) { + return fs_dir_push(this.dir, entry) } return wrap_none(PushStatus.Denied) } - public pop(file_name: string): WrapResultEntry { - if (rfwfs.read_write_access(this.entry.permissions)) { - fs_dir_pop(this.directory, file_name) + public pop(file_name: string): WrapResultEntry { + if (rfwfs.read_write_access(this.dir.permissions)) { + fs_dir_pop(this.dir, file_name) } return wrap_entry(ReadStatus.Denied) } -} -class RfwfsRootDirectory { - public directory: T[]; - - constructor() { - this.directory = [] + public push_bulk_unsafe(dirs: T[]) { + dirs.forEach(dir => this.dir.files.inner.push(dir)) + this.sort() } - public sort() { - this.directory.sort((a,z) => a.name.inner.localeCompare(z.name.inner)) - } - - public clone(file_name: string): WrapResultEntry { - return fs_dir_clone(this.directory, file_name) - } - - public find(file_name: string): WrapResultEntry { - return fs_dir_find(this.directory, file_name) - } - - public push(entry: E): WrapResultNone { - return fs_dir_push(this.directory, entry) - } - - public pop(file_name: string): WrapResultEntry { - return fs_dir_pop(this.directory, file_name) - } -} - -class RfwfsBinary { - public lambda: BinaryLambda; - protected entry: Entry; - - constructor(entry: Entry, lambda: BinaryLambda) { - this.lambda = lambda - this.entry = entry - } - - public execute(): WrapBinary { - if (rfwfs.execute_access(this.entry.permissions)) { - try { - this.lambda(() => strip_entry(this.entry)) - } catch(binary_e) { - return wrap_binary(ExecuteStatus.Panic, (binary_e as object).toString()) - } - return wrap_binary(ExecuteStatus.Ok) - } - return wrap_binary(ExecuteStatus.Denied) + public push_unsafe(dir: T) { + this.dir.files.inner.push(dir) + this.sort() } } @@ -268,88 +229,105 @@ class rfwfs_lib { public static read_write_access(permissions: Permissions): boolean { return rfwfs.read_access(permissions) && rfwfs.write_access(permissions) } + + public static directory_in_root(properties: DirectoryInRootProperties): RfwfsDirectory { + class dir { + public parent: P; + public permissions: Permissions; + public timestamp: number; + public files: EntryValue; + public name: EntryValue; + + constructor(permissions: Permissions, timestamp: number, name: string, parent: P, files: F[]) { + this.parent = parent + this.permissions = permissions + this.timestamp = timestamp + this.files = new EntryValue(this.permissions, files) + } + } + // const dir_o = { type: EntryType.Directory } as DirectoryInRoot + // dir_o.parent = null + // dir_o.permissions = properties.permissions + // dir_o.timestamp = properties.timestamp + // dir_o.files = new EntryValue(dir_o, []) + // dir_o.name = new EntryValue(dir_o, properties.name) + // return new RfwfsDirectory(dir_o) + } + public static directory(properties: DirectoryProperties): RfwfsDirectory { + const dir_o = { type: EntryType.Directory } as Directory + dir_o.parent = properties.parent + dir_o.permissions = properties.permissions + dir_o.timestamp = properties.timestamp + dir_o.files = new EntryValue(dir_o, []) + dir_o.name = new EntryValue(dir_o, properties.name) + return new RfwfsDirectory(dir_o) + } + public static file(properties: FileProperties) { + + } } -class rfwfs extends rfwfs_lib { - public root: Root; +class rfwfs extends rfwfs_lib { + public root: Root; constructor() { super() - this.root = { - type: EntryType.Root, - permissions: Permissions.r | Permissions.w, - timestamp: new EntryValueRoot((Date.now()/1000) | 0), - parent: null, - inner: new RfwfsRootDirectory(), - name: ROOT_ID.TRUNK, - } as Root + this.root = { type: EntryType.Root } as Root + this.root.permissions = Permissions.r | Permissions.w + this.root.timestamp = (Date.now()/1000) | 0 + this.root.parent = null + this.root.files = new EntryValue(this.root, []) + this.root.name = ROOT_ID.TRUNK } - public push_directory_into_root(dir: EntryCollection): EntryCollection { - this.root.inner.push(dir) - return dir + public sort() { + fs_dir_sort(this.root) } - public static file( - default_name: string, - default_permissions: Permissions, - default_parent: DirectoryAny, - default_timestamp?: number, - default_inner?: FileInner - ): EntryFile { - const file = { type: EntryType.File } as EntryFile - file.hash = "0" - file.permissions = default_permissions - file.parent = default_parent - file.timestamp = new EntryValue(file, default_timestamp ? default_timestamp : (Date.now()/1000) | 0) - file.inner = new EntryValue(file, default_inner ? default_inner : "") - file.name = new EntryValue(file, default_name) - return file + public clone(file_name: string): WrapResultEntry { + if (rfwfs.read_write_access(this.root.permissions)) { + return fs_dir_clone(this.root, file_name) + } + return wrap_entry(ReadStatus.Denied) } - public static directory( - default_name: string, - default_permissions: Permissions, - default_parent: DirectoryAny, - default_timestamp?: number, - default_inner?: T[] - ): EntryCollection { - const directory = { type: EntryType.Directory } as EntryCollection - directory.parent = default_parent - directory.permissions = default_permissions - directory.timestamp = new EntryValue(directory, default_timestamp ? default_timestamp : (Date.now()/1000) | 0) - directory.inner = new RfwfsDirectory(directory, default_inner ? default_inner : []) - directory.name = new EntryValue(directory, default_name) - return directory + public find(file_name: string): WrapResultEntry { + if (rfwfs.read_write_access(this.root.permissions)) { + return fs_dir_find(this.root, file_name) + } + return wrap_entry(ReadStatus.Denied) } - public static binary( - default_name: string, - default_permissions: Permissions, - default_parent: DirectoryAny, - default_timestamp?: number, - default_inner?: BinaryLambda - ): EntryBinary { - const binary = { type: EntryType.Binary } as EntryBinary - binary.parent = default_parent - binary.permissions = default_permissions - binary.timestamp = new EntryValue(binary, default_timestamp ? default_timestamp : (Date.now()/1000) | 0) - binary.inner = new RfwfsBinary(binary, default_inner ? default_inner : () => {}) - binary.name = new EntryValue(binary, default_name) - return binary + public push(entry: T): WrapResultNone { + if (rfwfs.read_write_access(this.root.permissions)) { + return fs_dir_push(this.root, entry) + } + return wrap_none(PushStatus.Denied) + } + + public pop(file_name: string): WrapResultEntry { + if (rfwfs.read_write_access(this.root.permissions)) { + fs_dir_pop(this.root, file_name) + } + return wrap_entry(ReadStatus.Denied) + } + + public push_bulk_unsafe(dirs: DirectoryInRoot[]) { + dirs.forEach(dir => this.root.files.inner.push(dir)) + this.sort() + } + + public push_unsafe(dir: DirectoryInRoot) { + this.root.files.inner.push(dir) + this.sort() } } export default rfwfs export { - type DirectoryAnyDepth, - type EntryCollection, + type DirectoryInRoot, type RfwfsDirectory, - type DirectoryAny, - type BinaryError, type Directory, - type FileInner, - type EntryFile, type Entry, Permissions, EntryType, diff --git a/src/rt/rfwfs/users.ts b/src/rt/rfwfs/users.ts new file mode 100644 index 0000000..f6ad472 --- /dev/null +++ b/src/rt/rfwfs/users.ts @@ -0,0 +1,112 @@ +import { ROOT_ID } from "./main"; + +import Crypto, { SHA256_String } from "../crypto/generate"; +import wrap, { type WrapResult } from "./wrap"; + +const enum SysGroups { + Wheel, + Users, +} +const enum GroupSearch { + NotFound, + WheelResult, + UsersResult, +} + +type WrapUserSearch = WrapResult + +const wheel: User[] = [] +const users: User[] = [] + +function wrap_user_search(status: GroupSearch, result?: User): WrapUserSearch { + return wrap(result, status) +} + +function groups_find_user(user_name: string): WrapUserSearch { + const exist_in_wheel = wheel.find(user => user.get_uname() === user_name) + if (exist_in_wheel) { + return wrap_user_search(GroupSearch.WheelResult, exist_in_wheel) + } + const exist_in_users = users.find(user => user.get_uname() === user_name) + if (exist_in_users) { + return wrap_user_search(GroupSearch.UsersResult, exist_in_users) + } + return wrap_user_search(GroupSearch.NotFound) +} + +function group_add(new_user: User, group: User[]): GroupSearch { + const dups = groups_find_user(new_user.get_uname()) + if (dups.status === GroupSearch.NotFound) { + group.push(new_user) + } + return dups.status +} + +function group_remove(user_name: string, group: User[]): boolean { + for (let i = 0; i { + if (new_password) { + this.password = await new Crypto(new_password).sha256_string() + } else { + this.password = undefined + } + } +} + +export default User +export { + group_wheel_remove, + group_users_remove, + group_wheel_add, + group_users_add, + SysGroups, +} \ No newline at end of file diff --git a/src/rt/rfwfs/wrap.ts b/src/rt/rfwfs/wrap.ts index 7c3b76d..e973b50 100644 --- a/src/rt/rfwfs/wrap.ts +++ b/src/rt/rfwfs/wrap.ts @@ -1,18 +1,10 @@ -import { type BinaryError, type Entry } from "./main" -import { ExecuteStatus } from "./enum/status" - const enum Option { - Ok, None, + Some, } type ConstEnum = number -type WrapResultEntry = WrapResult -type WrapBSearch = WrapResult -type WrapResultNone = WrapResult -type WrapBinary = WrapResult - interface WrapResult { /** The resulting value if `U` is a success status */ readonly result: T, @@ -24,31 +16,9 @@ function wrap(result: T, some: U): WrapResult { return { result: result, status: some } } -function wrap_bsearch(index: number, result: T): WrapBSearch { - return wrap(result, index) -} - -function wrap_entry(status: T, result?: U): WrapResultEntry { - return wrap(result, status) -} - -function wrap_none(status: T): WrapResultNone { - return wrap(Option.None, status) -} - -function wrap_binary(status: ExecuteStatus, result?: BinaryError): WrapBinary { - return wrap(status, result) -} - export default wrap export { - type WrapResultEntry, - type WrapResultNone, - type WrapBSearch, type WrapResult, - type WrapBinary, - wrap_bsearch, - wrap_binary, - wrap_entry, - wrap_none, -} + type ConstEnum, + Option, +} \ No newline at end of file