-
Notifications
You must be signed in to change notification settings - Fork 158
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(password-protected-folders): add password protected folders app
- Loading branch information
Showing
16 changed files
with
436 additions
and
3 deletions.
There are no files selected for viewing
6 changes: 6 additions & 0 deletions
6
changelog/unreleased/enhancement-add-pasword-protected-folders-app.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
Enhancement: Add password protected folders app | ||
|
||
We've added a new application called "Password protected folders". This application allows users to create new folders that are accessible only by entering a password. | ||
|
||
https://github.com/owncloud/web/pull/12137 | ||
https://github.com/owncloud/web/issues/12039 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,6 +22,7 @@ | |
"epub-reader", | ||
"app-store", | ||
"activities", | ||
"preview" | ||
"preview", | ||
"password-protected-folders" | ||
] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
# web-app-password-protected-folders | ||
|
||
This web extension enhances the oCIS platform by allowing users to create password-protected folders. It provides an additional layer of security for sensitive or confidential information stored within oCIS. | ||
|
||
## Features | ||
|
||
- 🔒 Create password-protected folders within oCIS | ||
- 🔑 Set unique passwords for each protected folder | ||
- 🎨 Seamless integration with the oCIS user interface | ||
|
||
## Usage | ||
|
||
Once the extension is installed, users can create password-protected folders by following these steps: | ||
|
||
1. Log in to your oCIS instance. | ||
2. Navigate to the folder where you want to create a password-protected subfolder. | ||
3. Click the "New" button and select "Password Protected Folder." | ||
4. Enter a name for the folder and set a unique password. | ||
5. Click "Create" to create the password-protected folder. | ||
6. To access the protected folder, users will be prompted to enter the corresponding password. |
10 changes: 10 additions & 0 deletions
10
packages/web-app-password-protected-folders/l10n/.tx/config
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
[main] | ||
host = https://www.transifex.com | ||
|
||
[o:owncloud-org:p:owncloud-web:r:password-protected-folders] | ||
file_filter = locale/<lang>/app.po | ||
minimum_perc = 0 | ||
resource_name = web-password-protected-folders | ||
source_file = template.pot | ||
source_lang = en | ||
type = PO |
1 change: 1 addition & 0 deletions
1
packages/web-app-password-protected-folders/l10n/translations.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
{} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
{ | ||
"name": "web-app-password-protected-folders", | ||
"version": "0.4.0", | ||
"private": true, | ||
"description": "ownCloud Web password protected folders", | ||
"license": "AGPL-3.0", | ||
"type": "module", | ||
"scripts": { | ||
"build": "pnpm vite build", | ||
"build:w": "pnpm vite build --watch --mode development", | ||
"check:types": "vue-tsc --noEmit", | ||
"test:unit": "NODE_OPTIONS=--unhandled-rejections=throw vitest" | ||
}, | ||
"devDependencies": { | ||
"@ownclouders/web-client": "workspace:*", | ||
"@ownclouders/web-pkg": "workspace:*", | ||
"@ownclouders/web-test-helpers": "workspace:*", | ||
"@vue/test-utils": "^2.4.6", | ||
"uuid": "^11.0.0", | ||
"vitest-mock-extended": "2.0.2", | ||
"vue": "^3.4.21", | ||
"vue3-gettext": "^2.4.0" | ||
} | ||
} |
3 changes: 3 additions & 0 deletions
3
packages/web-app-password-protected-folders/public/manifest.json
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
{ | ||
"entrypoint": "password-protected-folders.js" | ||
} |
70 changes: 70 additions & 0 deletions
70
packages/web-app-password-protected-folders/src/components/CreateFolderModal.vue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,70 @@ | ||
<template> | ||
<form autocomplete="off" @submit.prevent="emit('confirm')"> | ||
<oc-text-input | ||
id="input-folder-name" | ||
v-model="formData.folderName" | ||
:label="$gettext('Folder name')" | ||
/> | ||
<oc-text-input | ||
id="input-folder-password" | ||
v-model="formData.password" | ||
type="password" | ||
:label="$gettext('Password')" | ||
class="oc-mt-s" | ||
/> | ||
<input type="submit" class="oc-hidden" /> | ||
</form> | ||
</template> | ||
|
||
<script lang="ts" setup> | ||
import { useMessages, useResourcesStore, useSpacesStore } from '@ownclouders/web-pkg' | ||
import { computed, reactive, unref, watch } from 'vue' | ||
import { useGettext } from 'vue3-gettext' | ||
import { useCreateFileHandler } from '../composables/useCreateFileHandler' | ||
const emit = defineEmits<{ | ||
confirm: [] | ||
'update:confirmDisabled': [isDisabled: boolean] | ||
}>() | ||
const { $gettext } = useGettext() | ||
const { showErrorMessage } = useMessages() | ||
const { createFileHandler } = useCreateFileHandler() | ||
const { currentFolder } = useResourcesStore() | ||
const { currentSpace } = useSpacesStore() | ||
const formData = reactive({ | ||
folderName: '', | ||
password: '' | ||
}) | ||
const isFormValid = computed(() => formData.folderName !== '' && formData.password !== '') | ||
const onConfirm = async () => { | ||
if (!unref(isFormValid)) { | ||
return Promise.reject() | ||
} | ||
try { | ||
await createFileHandler({ | ||
fileName: formData.folderName, | ||
currentFolder: unref(currentFolder), | ||
space: unref(currentSpace), | ||
password: formData.password | ||
}) | ||
} catch (error) { | ||
console.error(error) | ||
showErrorMessage({ title: $gettext('Failed to create folder'), errors: [error] }) | ||
} | ||
} | ||
watch( | ||
isFormValid, | ||
(isValid) => { | ||
emit('update:confirmDisabled', !isValid) | ||
}, | ||
{ immediate: true } | ||
) | ||
defineExpose({ onConfirm }) | ||
</script> |
45 changes: 45 additions & 0 deletions
45
packages/web-app-password-protected-folders/src/composables/useCreateFileHandler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
import { Resource, SpaceResource, urlJoin } from '@ownclouders/web-client' | ||
import { SharingLinkType } from '@ownclouders/web-client/graph/generated' | ||
import { useClientService, useResourcesStore, useSharesStore } from '@ownclouders/web-pkg' | ||
import { unref } from 'vue' | ||
|
||
export const useCreateFileHandler = () => { | ||
const clientService = useClientService() | ||
const { upsertResource } = useResourcesStore() | ||
const { addLink } = useSharesStore() | ||
|
||
const createFileHandler = async ({ | ||
fileName, | ||
space, | ||
currentFolder, | ||
password | ||
}: { | ||
fileName: string | ||
space: SpaceResource | ||
currentFolder: Resource | ||
password: string | ||
}) => { | ||
if (fileName === '') { | ||
return | ||
} | ||
|
||
const folderPath = '/.' + fileName | ||
|
||
const folder = await clientService.webdav.createFolder(unref(space), { path: folderPath }) | ||
upsertResource(folder) | ||
|
||
await addLink({ | ||
clientService, | ||
space, | ||
resource: folder, | ||
options: { password, type: SharingLinkType.Edit } | ||
}) | ||
|
||
const path = urlJoin(currentFolder.path, fileName + '.psec') | ||
|
||
const file = await clientService.webdav.putFileContents(unref(space), { path }) | ||
upsertResource(file) | ||
} | ||
|
||
return { createFileHandler } | ||
} |
18 changes: 18 additions & 0 deletions
18
packages/web-app-password-protected-folders/src/composables/useCustomHandler.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { useModals } from '@ownclouders/web-pkg' | ||
import { useGettext } from 'vue3-gettext' | ||
import CreateFolderModal from '../components/CreateFolderModal.vue' | ||
|
||
export const useCustomHandler = () => { | ||
const { dispatchModal } = useModals() | ||
const { $gettext } = useGettext() | ||
|
||
const customHandler = () => { | ||
dispatchModal({ | ||
title: $gettext('Create a new password protected folder'), | ||
customComponent: CreateFolderModal, | ||
confirmText: $gettext('Create') | ||
}) | ||
} | ||
|
||
return { customHandler } | ||
} |
16 changes: 16 additions & 0 deletions
16
packages/web-app-password-protected-folders/src/composables/useExtensions.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import { ActionExtension } from '@ownclouders/web-pkg' | ||
import { computed, unref } from 'vue' | ||
import { useOpenFolderAction } from './useOpenFolderAction' | ||
|
||
export const useExtensions = () => { | ||
const action = useOpenFolderAction() | ||
|
||
const actionExtension = computed<ActionExtension>(() => ({ | ||
id: 'com.github.owncloud.web-extensions.password-protected-folders', | ||
type: 'action', | ||
extensionPointIds: ['global.files.context-actions', 'global.files.default-actions'], | ||
action: unref(action) | ||
})) | ||
|
||
return computed(() => [unref(actionExtension)]) | ||
} |
29 changes: 29 additions & 0 deletions
29
packages/web-app-password-protected-folders/src/composables/useOpenFolderAction.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { FileAction } from '@ownclouders/web-pkg' | ||
import { computed } from 'vue' | ||
import { useGettext } from 'vue3-gettext' | ||
|
||
export const useOpenFolderAction = () => { | ||
const { $gettext } = useGettext() | ||
|
||
const action = computed<FileAction>(() => ({ | ||
name: 'open-password-protected-folder', | ||
icon: 'external-link', | ||
handler: () => { | ||
// TODO: add handler | ||
console.warn('NOT IMPLEMENTED') | ||
}, | ||
label: () => $gettext('Open folder'), | ||
isDisabled: () => false, | ||
isVisible: ({ resources }) => { | ||
if (resources.length !== 1) { | ||
return false | ||
} | ||
|
||
return resources[0].extension === 'psec' | ||
}, | ||
componentType: 'button', | ||
class: 'oc-files-actions-open-password-protected-folder' | ||
})) | ||
|
||
return action | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
import { useGettext } from 'vue3-gettext' | ||
import translations from '../l10n/translations.json' | ||
import { defineWebApplication } from '@ownclouders/web-pkg' | ||
import { useExtensions } from './composables/useExtensions' | ||
import { useCustomHandler } from './composables/useCustomHandler' | ||
|
||
export default defineWebApplication({ | ||
setup() { | ||
const { $gettext } = useGettext() | ||
const extensions = useExtensions() | ||
const { customHandler } = useCustomHandler() | ||
|
||
return { | ||
appInfo: { | ||
name: $gettext('Password Protected Folders'), | ||
id: 'password-protected-folders', | ||
extensions: [ | ||
{ | ||
newFileMenu: { menuTitle: () => $gettext('Password Protected Folder') }, | ||
extension: 'psec', | ||
customHandler | ||
} | ||
] | ||
}, | ||
translations, | ||
extensions | ||
} | ||
} | ||
}) |
69 changes: 69 additions & 0 deletions
69
packages/web-app-password-protected-folders/tests/unit/components/CreateFolderModal.spec.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
import { defaultComponentMocks, defaultPlugins, shallowMount } from '@ownclouders/web-test-helpers' | ||
import CreateFolderModal from '../../../src/components/CreateFolderModal.vue' | ||
import { useCreateFileHandler } from '../../../src/composables/useCreateFileHandler' | ||
import { mock } from 'vitest-mock-extended' | ||
import { Resource, SpaceResource } from '@ownclouders/web-client' | ||
import { VueWrapper } from '@vue/test-utils' | ||
|
||
vi.mock('../../../src/composables/useCreateFileHandler', () => ({ | ||
useCreateFileHandler: vi.fn().mockReturnValue({ createFileHandler: vi.fn() }) | ||
})) | ||
|
||
const currentFolder = mock<Resource>() | ||
const currentSpace = mock<SpaceResource>() | ||
|
||
const SELECTORS = Object.freeze({ | ||
inputFolderName: '#input-folder-name', | ||
inputFolderPassword: '#input-folder-password' | ||
}) | ||
|
||
describe('CreateFolderModal', () => { | ||
it('should call "createFileHandler" when form is valid', () => { | ||
const { wrapper } = getWrapper() | ||
|
||
const folderNameInput = wrapper.findComponent(SELECTORS.inputFolderName) as VueWrapper | ||
const passwordInput = wrapper.findComponent(SELECTORS.inputFolderPassword) as VueWrapper | ||
|
||
folderNameInput.vm.$emit('update:modelValue', 'name') | ||
passwordInput.vm.$emit('update:modelValue', 'password') | ||
|
||
wrapper.vm.onConfirm() | ||
|
||
expect(useCreateFileHandler().createFileHandler).toHaveBeenCalledWith({ | ||
fileName: 'name', | ||
password: 'password', | ||
space: currentSpace, | ||
currentFolder | ||
}) | ||
}) | ||
|
||
it('should not call "createFileHandler" when form is invalid', () => { | ||
const { wrapper } = getWrapper() | ||
|
||
const folderNameInput = wrapper.findComponent(SELECTORS.inputFolderName) as VueWrapper | ||
folderNameInput.vm.$emit('update:modelValue', 'name') | ||
|
||
expect(wrapper.vm.onConfirm()).rejects.toThrow() | ||
expect(useCreateFileHandler().createFileHandler).not.toHaveBeenCalled() | ||
}) | ||
}) | ||
|
||
function getWrapper() { | ||
const mocks = defaultComponentMocks() | ||
|
||
return { | ||
wrapper: shallowMount(CreateFolderModal, { | ||
global: { | ||
plugins: defaultPlugins({ | ||
piniaOptions: { | ||
resourcesStore: { currentFolder }, | ||
spacesState: { currentSpace } | ||
} | ||
}), | ||
mocks, | ||
provide: mocks | ||
} | ||
}), | ||
mocks | ||
} | ||
} |
Oops, something went wrong.