diff --git a/__tests__/files/file.spec.ts b/__tests__/files/file.spec.ts new file mode 100644 index 00000000..2c02fb53 --- /dev/null +++ b/__tests__/files/file.spec.ts @@ -0,0 +1,95 @@ +import { File } from '../../lib/files/file' +import { FileType } from '../../lib/files/fileType' +import { Permission } from '../../lib/permissions' + +describe('File creation', () => { + test('Valid dav file', () => { + const file = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma' + }) + + expect(file).toBeInstanceOf(File) + expect(file.type).toBe(FileType.File) + + // various data + expect(file.mime).toBe('image/jpeg') + expect(file.owner).toBe('emma') + expect(file.size).toBeUndefined() + expect(file.attributes).toStrictEqual({}) + + // path checks + expect(file.basename).toBe('picture.jpg') + expect(file.extension).toBe('.jpg') + expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.root).toBe('/files/emma/Photos') + expect(file.isDavRessource).toBe(true) + expect(file.permissions).toBe(Permission.READ) + }) + + test('Valid remote file', () => { + const file = new File({ + source: 'https://domain.com/Photos/picture.jpg', + mime: 'image/jpeg', + owner: null + }) + + expect(file).toBeInstanceOf(File) + expect(file.type).toBe(FileType.File) + + // various data + expect(file.mime).toBe('image/jpeg') + expect(file.owner).toBeNull() + expect(file.size).toBeUndefined() + expect(file.attributes).toStrictEqual({}) + + // path checks + expect(file.basename).toBe('picture.jpg') + expect(file.extension).toBe('.jpg') + expect(file.dirname).toBe('https://domain.com/Photos') + expect(file.root).toBeNull() + expect(file.isDavRessource).toBe(false) + expect(file.permissions).toBe(Permission.READ) + }) +}) + +describe('File data change', () => { + test('Rename a file', () => { + const file = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma' + }) + + expect(file.basename).toBe('picture.jpg') + expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.root).toBe('/files/emma/Photos') + + file.rename('picture-old.jpg') + + expect(file.basename).toBe('picture-old.jpg') + expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture-old.jpg') + expect(file.root).toBe('/files/emma/Photos') + }) + + test('Changing source', () => { + const file = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma' + }) + + expect(file.basename).toBe('picture.jpg') + expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + expect(file.root).toBe('/files/emma/Photos') + + file.move('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/picture-old.jpg') + + expect(file.basename).toBe('picture-old.jpg') + expect(file.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures') + expect(file.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/picture-old.jpg') + expect(file.root).toBe('/files/emma/Pictures') + }) +}) diff --git a/__tests__/files/folder.spec.ts b/__tests__/files/folder.spec.ts new file mode 100644 index 00000000..364eb22a --- /dev/null +++ b/__tests__/files/folder.spec.ts @@ -0,0 +1,91 @@ +import { Folder } from '../../lib/files/folder' +import { FileType } from '../../lib/files/fileType' +import { Permission } from '../../lib/permissions' + +describe('File creation', () => { + test('Valid dav folder', () => { + const folder = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/', + owner: 'emma' + }) + + expect(folder).toBeInstanceOf(Folder) + expect(folder.type).toBe(FileType.Folder) + + // various data + expect(folder.mime).toBe('httpd/unix-directory') + expect(folder.owner).toBe('emma') + expect(folder.size).toBeUndefined() + expect(folder.attributes).toStrictEqual({}) + + // path checks + expect(folder.basename).toBe('Photos') + expect(folder.extension).toBeNull() + expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.root).toBe('/files/emma') + expect(folder.isDavRessource).toBe(true) + expect(folder.permissions).toBe(Permission.READ) + }) + + test('Valid remote folder', () => { + const folder = new Folder({ + source: 'https://domain.com/Photos/', + owner: null + }) + + expect(folder).toBeInstanceOf(Folder) + expect(folder.type).toBe(FileType.Folder) + + // various data + expect(folder.mime).toBe('httpd/unix-directory') + expect(folder.owner).toBeNull() + expect(folder.size).toBeUndefined() + expect(folder.attributes).toStrictEqual({}) + + // path checks + expect(folder.basename).toBe('Photos') + expect(folder.extension).toBeNull() + expect(folder.dirname).toBe('https://domain.com') + expect(folder.root).toBeNull() + expect(folder.isDavRessource).toBe(false) + expect(folder.permissions).toBe(Permission.READ) + }) +}) + +describe('Folder data change', () => { + test('Rename a folder', () => { + const folder = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos', + owner: 'emma' + }) + + expect(folder.basename).toBe('Photos') + expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.root).toBe('/files/emma') + + folder.rename('Pictures') + + expect(folder.basename).toBe('Pictures') + expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures') + expect(folder.root).toBe('/files/emma') + }) + + test('Changing source', () => { + const folder = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/', + owner: 'emma' + }) + + expect(folder.basename).toBe('Photos') + expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.root).toBe('/files/emma') + + folder.move('https://cloud.domain.com/remote.php/dav/files/emma/Pictures/') + + expect(folder.basename).toBe('Pictures') + expect(folder.dirname).toBe('https://cloud.domain.com/remote.php/dav/files/emma') + expect(folder.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Pictures') + expect(folder.root).toBe('/files/emma') + }) +}) diff --git a/__tests__/files/node.spec.ts b/__tests__/files/node.spec.ts new file mode 100644 index 00000000..a66a54f7 --- /dev/null +++ b/__tests__/files/node.spec.ts @@ -0,0 +1,173 @@ +import { File } from '../../lib/files/file' +import { Folder } from '../../lib/files/folder' +import NodeData, { Attribute } from '../../lib/files/nodeData' + +describe('Node testing', () => { + test('Root null fallback', () => { + const file = new File({ + source: 'https://cloud.domain.com/remote.php/dav/picture.jpg', + mime: 'image/jpeg', + owner: 'emma' + }) + expect(file.root).toBeNull() + }) + + test('Remove source ending slash', () => { + const file = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/', + owner: 'emma' + }) + expect(file.source).toBe('https://cloud.domain.com/remote.php/dav/files/emma/Photos') + }) + + test('Invalid rename', () => { + const file = new Folder({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/', + owner: 'emma' + }) + expect(() => file.rename('new/folder')).toThrowError('Invalid basename') + }) +}) + +describe('Sanity checks', () => { + test('Invalid id', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + id: '1234' as unknown as number, + })).toThrowError('Invalid id type of value') + + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + id: -1234, + })).toThrowError('Invalid id type of value') + }) + + test('Invalid source', () => { + expect(() => new File({} as unknown as NodeData)).toThrowError('Missing mandatory source') + expect(() => new File({ + source: 'cloud.domain.com/remote.php/dav/Photos', + mime: 'image/jpeg', + owner: 'emma' + })).toThrowError('Invalid source') + expect(() => new File({ + source: '/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma' + })).toThrowError('Invalid source') + + }) + + test('Invalid mtime', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/Photos', + mime: 'image', + owner: 'emma', + mtime: 'invalid' as unknown as Date + })).toThrowError('Invalid mtime type') + }) + + + test('Invalid crtime', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/Photos', + mime: 'image', + owner: 'emma', + crtime: 'invalid' as unknown as Date + })).toThrowError('Invalid crtime type') + }) + + test('Invalid mime', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image', + owner: 'emma' + })).toThrowError('Missing or invalid mandatory mime') + }) + + test('Invalid attributes', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + attributes: 'test' as unknown as Attribute, + })).toThrowError('Invalid attributes format') + }) + + test('Invalid permissions', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + permissions: 324, + })).toThrowError('Invalid permissions') + }) + + test('Invalid size', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + size: 'test' as unknown as number, + })).toThrowError('Invalid size type') + }) + + test('Invalid owner', () => { + expect(() => new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: true as unknown as string, + })).toThrowError('Invalid owner') + }) +}) + +describe('Dav service detection', () => { + test('Known dav services', () => { + const file1 = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }) + expect(file1.isDavRessource).toBe(true) + + const file2 = new File({ + source: 'https://cloud.domain.com/remote.php/webdav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }) + expect(file2.isDavRessource).toBe(true) + + const file3 = new File({ + source: 'https://cloud.domain.com/public.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }) + expect(file3.isDavRessource).toBe(true) + + const file4 = new File({ + source: 'https://cloud.domain.com/public.php/webdav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }) + expect(file4.isDavRessource).toBe(true) + }) + + test('Custom dav service', () => { + const file1 = new File({ + source: 'https://cloud.domain.com/test.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }, /test\.php\/dav/) + expect(file1.isDavRessource).toBe(true) + + const file2 = new File({ + source: 'https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg', + mime: 'image/jpeg', + owner: 'emma', + }, /test\.php\/dav/) + expect(file2.isDavRessource).toBe(false) + }) +}) diff --git a/__tests__/humanFileSize.spec.ts b/__tests__/humanFileSize.spec.ts index 27cab605..aa6d249f 100644 --- a/__tests__/humanFileSize.spec.ts +++ b/__tests__/humanFileSize.spec.ts @@ -6,8 +6,6 @@ declare global { } } -jest.mock - describe('humanFileSize', () => { describe('formatFileSize', () => { it('renders file sizes with the correct unit', function() { diff --git a/__tests__/index.spec.ts b/__tests__/index.spec.ts index 0fca7562..aa8cbceb 100644 --- a/__tests__/index.spec.ts +++ b/__tests__/index.spec.ts @@ -3,7 +3,17 @@ import { addNewFileMenuEntry, removeNewFileMenuEntry, getNewFileMenuEntries, + FileType, + File, + Folder, + Permission, } from '../lib/index' + +import { File as FileSource } from '../lib/files/file' +import { Folder as FolderSource } from '../lib/files/folder' +import { Permission as PermissionSource } from '../lib/permissions' +import { FileType as FileTypeSource } from '../lib/files/fileType' + import { Entry, NewFileMenu } from '../lib/newFileMenu'; declare global { @@ -33,6 +43,26 @@ describe('Exports checks', () => { expect(getNewFileMenuEntries).toBeTruthy() expect(typeof getNewFileMenuEntries).toBe('function') }) + + test('FileType', () => { + expect(FileType).toBeTruthy() + expect(typeof FileType).toBe('object') + }) + + test('Permission', () => { + expect(Permission).toBeTruthy() + expect(typeof Permission).toBe('object') + }) + + test('File', () => { + expect(File).toBeTruthy() + expect(typeof File).toBe('function') + }) + + test('Folder', () => { + expect(Folder).toBeTruthy() + expect(typeof Folder).toBe('function') + }) }) diff --git a/lib/files/file.ts b/lib/files/file.ts new file mode 100644 index 00000000..77f4f658 --- /dev/null +++ b/lib/files/file.ts @@ -0,0 +1,29 @@ +/** + * @copyright Copyright (c) 2022 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +import { FileType } from './fileType' +import Node from './node' + +export class File extends Node { + get type(): FileType { + return FileType.File + } +} diff --git a/lib/files/fileType.ts b/lib/files/fileType.ts new file mode 100644 index 00000000..e6ab1f61 --- /dev/null +++ b/lib/files/fileType.ts @@ -0,0 +1,25 @@ +/** + * @copyright Copyright (c) 2022 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +export enum FileType { + Folder = 'folder', + File = 'file', +} diff --git a/lib/files/folder.ts b/lib/files/folder.ts new file mode 100644 index 00000000..52060699 --- /dev/null +++ b/lib/files/folder.ts @@ -0,0 +1,46 @@ +/** + * @copyright Copyright (c) 2022 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +import { FileType } from './fileType' +import Node from './node' +import NodeData from './nodeData' + +export class Folder extends Node { + constructor(data: NodeData) { + // enforcing mimes + super({ + ...data, + mime: 'httpd/unix-directory' + }) + } + + get type(): FileType { + return FileType.Folder + } + + get extension(): string|null { + return null + } + + get mime(): string { + return 'httpd/unix-directory' + } +} diff --git a/lib/files/node.ts b/lib/files/node.ts new file mode 100644 index 00000000..16b404a8 --- /dev/null +++ b/lib/files/node.ts @@ -0,0 +1,160 @@ +/** + * @copyright Copyright (c) 2022 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ +import { basename, extname, dirname } from 'path' +import { Permission } from '../permissions' +import { FileType } from './fileType' +import NodeData, { Attribute, validateData } from './nodeData' + +export default abstract class Node { + private _data: NodeData + private _attributes: Attribute[] + private _knownDavService = /(remote|public)\.php\/(web)?dav/i + + constructor(data: NodeData, davService?: RegExp) { + // Validate data + validateData(data) + + this._data = data + this._attributes = data.attributes || {} as any + delete this._data.attributes + + if (davService) { + this._knownDavService = davService + } + } + + /** + * Get the source url to this object + */ + get source(): string { + // strip any ending slash + return this._data.source.replace(/\/$/i, '') + } + + /** + * Get this object name + */ + get basename(): string { + return basename(this.source) + } + + /** + * Get this object's extension + */ + get extension(): string|null { + return extname(this.source) + } + + /** + * Get the directory path leading to this object + */ + get dirname(): string { + return dirname(this.source) + } + + /** + * Is it a file or a folder ? + */ + abstract get type(): FileType + + /** + * Get the file mime + */ + get mime(): string|undefined { + return this._data.mime + } + + /** + * Get the file size + */ + get size(): number|undefined { + return this._data.size + } + + /** + * Get the file attribute + */ + get attributes(): Attribute { + return this._attributes + } + + /** + * Get the file permissions + */ + get permissions(): Permission { + // If this is not a dav ressource, we can only read it + if (this.owner === null && !this.isDavRessource) { + return Permission.READ + } + + return this._data.permissions || Permission.READ + } + + /** + * Get the file owner + */ + get owner(): string|null { + // Remote ressources have no owner + if (!this.isDavRessource) { + return null + } + return this._data.owner + } + + /** + * Is this a dav-related ressource ? + */ + get isDavRessource(): boolean { + return this.source.match(this._knownDavService) !== null + } + + /** + * Get the dav root of this object + */ + get root(): string|null { + if (this.isDavRessource) { + return this.dirname.split(this._knownDavService).pop() || null + } + return null + } + + /** + * Move the node to a new destination + * + * @param {string} destination the new source. + * e.g. https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg + */ + move(destination: string) { + this._data.source = destination + } + + /** + * Rename the node + * This aliases the move method for easier usage + */ + rename(basename) { + if (basename.includes('/')) { + throw new Error('Invalid basename') + } + this.move(this.dirname + '/' + basename) + } +} diff --git a/lib/files/nodeData.ts b/lib/files/nodeData.ts new file mode 100644 index 00000000..3ddb0182 --- /dev/null +++ b/lib/files/nodeData.ts @@ -0,0 +1,108 @@ +/** + * @copyright Copyright (c) 2022 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +import { Permission } from "../permissions" + +export interface Attribute { [key: string]: any } + +export default interface NodeData { + /** Unique ID */ + id?: number + + /** URL to the ressource + * e.g. https://cloud.domain.com/remote.php/dav/files/emma/Photos/picture.jpg + * or https://domain.com/Photos/picture.jpg + */ + source: string + + /** Last modified time */ + mtime?: Date + + /** Creation time */ + crtime?: Date + + /** The mime type */ + mime?: string + + /** The node size type */ + size?: number + + /** The node permissions */ + permissions?: Permission + + /** The owner UID of this node */ + owner: string|null + + attributes?: Attribute +} + +/** + * Validate Node construct data + */ +export const validateData = (data: NodeData) => { + if ('id' in data && (typeof data.id !== 'number' || data.id < 0)) { + throw new Error('Invalid id type of value') + } + + if (!data.source) { + throw new Error('Missing mandatory source') + } + + if (!data.source.startsWith('http')) { + throw new Error('Invalid source format') + } + + if ('mtime' in data && !(data.mtime instanceof Date)) { + throw new Error('Invalid mtime type') + } + + if ('crtime' in data && !(data.crtime instanceof Date)) { + throw new Error('Invalid crtime type') + } + + if (!data.mime || typeof data.mime !== 'string' + || !data.mime.match(/^[-\w.]+\/[-+\w.]+$/gi)) { + throw new Error('Missing or invalid mandatory mime') + } + + if ('size' in data && typeof data.size !== 'number') { + throw new Error('Invalid size type') + } + + if ('permissions' in data && !( + typeof data.permissions === 'number' + && data.permissions >= Permission.NONE + && data.permissions <= Permission.ALL + )) { + throw new Error('Invalid permissions') + } + + if ('owner' in data + && data.owner !== null + && typeof data.owner !== 'string') { + throw new Error('Invalid owner type') + } + + if ('attributes' in data && typeof data.attributes !== 'object') { + throw new Error('Invalid attributes format') + } +} diff --git a/lib/index.ts b/lib/index.ts index b4783cbf..959afcd6 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -25,6 +25,11 @@ export { formatFileSize } from './humanfilesize' export { type Entry } from './newFileMenu' import { type Entry, getNewFileMenu, NewFileMenu } from './newFileMenu' +export { FileType } from './files/fileType' +export { File } from './files/file' +export { Folder } from './files/folder' +export { Permission } from './permissions' + declare global { interface Window { OC: any; diff --git a/lib/permissions.ts b/lib/permissions.ts new file mode 100644 index 00000000..efdc341f --- /dev/null +++ b/lib/permissions.ts @@ -0,0 +1,32 @@ + +/** + * @copyright Copyright (c) 2022 John Molakvoæ + * + * @author John Molakvoæ + * + * @license AGPL-3.0-or-later + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +export enum Permission { + NONE = 0, + CREATE = 4, + READ = 1, + UPDATE = 2, + DELETE = 8, + SHARE = 16, + ALL = 31, +}