diff options
Diffstat (limited to 'apps/files_sharing/src/utils')
-rw-r--r-- | apps/files_sharing/src/utils/AccountIcon.spec.ts | 40 | ||||
-rw-r--r-- | apps/files_sharing/src/utils/AccountIcon.ts | 28 | ||||
-rw-r--r-- | apps/files_sharing/src/utils/GeneratePassword.js | 55 | ||||
-rw-r--r-- | apps/files_sharing/src/utils/GeneratePassword.ts | 66 | ||||
-rw-r--r-- | apps/files_sharing/src/utils/NodeShareUtils.ts | 58 | ||||
-rw-r--r-- | apps/files_sharing/src/utils/SharedWithMe.js | 40 |
6 files changed, 203 insertions, 84 deletions
diff --git a/apps/files_sharing/src/utils/AccountIcon.spec.ts b/apps/files_sharing/src/utils/AccountIcon.spec.ts new file mode 100644 index 00000000000..bbc7f031774 --- /dev/null +++ b/apps/files_sharing/src/utils/AccountIcon.spec.ts @@ -0,0 +1,40 @@ +/*! + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { describe, expect, it, afterEach } from 'vitest' +import { generateAvatarSvg } from './AccountIcon' +describe('AccountIcon', () => { + + afterEach(() => { + delete document.body.dataset.themes + }) + + it('should generate regular account avatar svg', () => { + const svg = generateAvatarSvg('admin') + expect(svg).toContain('/avatar/admin/32') + expect(svg).not.toContain('dark') + expect(svg).toContain('?guestFallback=true') + }) + + it('should generate guest account avatar svg', () => { + const svg = generateAvatarSvg('admin', true) + expect(svg).toContain('/avatar/guest/admin/32') + expect(svg).not.toContain('dark') + expect(svg).not.toContain('?guestFallback=true') + }) + + it('should generate dark mode account avatar svg', () => { + document.body.dataset.themes = 'dark' + const svg = generateAvatarSvg('admin') + expect(svg).toContain('/avatar/admin/32/dark') + expect(svg).toContain('?guestFallback=true') + }) + + it('should generate dark mode guest account avatar svg', () => { + document.body.dataset.themes = 'dark' + const svg = generateAvatarSvg('admin', true) + expect(svg).toContain('/avatar/guest/admin/32/dark') + expect(svg).not.toContain('?guestFallback=true') + }) +}) diff --git a/apps/files_sharing/src/utils/AccountIcon.ts b/apps/files_sharing/src/utils/AccountIcon.ts new file mode 100644 index 00000000000..21732f08f68 --- /dev/null +++ b/apps/files_sharing/src/utils/AccountIcon.ts @@ -0,0 +1,28 @@ +/*! + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +import { generateUrl } from '@nextcloud/router' + +const isDarkMode = () => { + return window?.matchMedia?.('(prefers-color-scheme: dark)')?.matches === true + || document.querySelector('[data-themes*=dark]') !== null +} + +export const generateAvatarSvg = (userId: string, isGuest = false) => { + // normal avatar url: /avatar/{userId}/32?guestFallback=true + // dark avatar url: /avatar/{userId}/32/dark?guestFallback=true + // guest avatar url: /avatar/guest/{userId}/32 + // guest dark avatar url: /avatar/guest/{userId}/32/dark + const basePath = isGuest ? `/avatar/guest/${userId}` : `/avatar/${userId}` + const darkModePath = isDarkMode() ? '/dark' : '' + const guestFallback = isGuest ? '' : '?guestFallback=true' + + const url = `${basePath}/32${darkModePath}${guestFallback}` + const avatarUrl = generateUrl(url, { userId }) + + return `<svg width="32" height="32" viewBox="0 0 32 32" + xmlns="http://www.w3.org/2000/svg" class="sharing-status__avatar"> + <image href="${avatarUrl}" height="32" width="32" /> + </svg>` +} diff --git a/apps/files_sharing/src/utils/GeneratePassword.js b/apps/files_sharing/src/utils/GeneratePassword.js deleted file mode 100644 index f3122de1644..00000000000 --- a/apps/files_sharing/src/utils/GeneratePassword.js +++ /dev/null @@ -1,55 +0,0 @@ -/** - * @copyright Copyright (c) 2020 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @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 <http://www.gnu.org/licenses/>. - * - */ - -import axios from '@nextcloud/axios' -import Config from '../services/ConfigService' - -const config = new Config() -const passwordSet = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789' - -/** - * Generate a valid policy password or - * request a valid password if password_policy - * is enabled - * - * @return {string} a valid password - */ -export default async function() { - // password policy is enabled, let's request a pass - if (config.passwordPolicy.api && config.passwordPolicy.api.generate) { - try { - const request = await axios.get(config.passwordPolicy.api.generate) - if (request.data.ocs.data.password) { - return request.data.ocs.data.password - } - } catch (error) { - console.info('Error generating password from password_policy', error) - } - } - - // generate password of 10 length based on passwordSet - return Array(10).fill(0) - .reduce((prev, curr) => { - prev += passwordSet.charAt(Math.floor(Math.random() * passwordSet.length)) - return prev - }, '') -} diff --git a/apps/files_sharing/src/utils/GeneratePassword.ts b/apps/files_sharing/src/utils/GeneratePassword.ts new file mode 100644 index 00000000000..82efaaa69d4 --- /dev/null +++ b/apps/files_sharing/src/utils/GeneratePassword.ts @@ -0,0 +1,66 @@ +/** + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import axios from '@nextcloud/axios' +import Config from '../services/ConfigService.ts' +import { showError, showSuccess } from '@nextcloud/dialogs' +import { translate as t } from '@nextcloud/l10n' + +const config = new Config() +// note: some chars removed on purpose to make them human friendly when read out +const passwordSet = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789' + +/** + * Generate a valid policy password or request a valid password if password_policy is enabled + * + * @param {boolean} verbose If enabled the the status is shown to the user via toast + */ +export default async function(verbose = false): Promise<string> { + // password policy is enabled, let's request a pass + if (config.passwordPolicy.api && config.passwordPolicy.api.generate) { + try { + const request = await axios.get(config.passwordPolicy.api.generate) + if (request.data.ocs.data.password) { + if (verbose) { + showSuccess(t('files_sharing', 'Password created successfully')) + } + return request.data.ocs.data.password + } + } catch (error) { + console.info('Error generating password from password_policy', error) + if (verbose) { + showError(t('files_sharing', 'Error generating password from password policy')) + } + } + } + + const array = new Uint8Array(10) + const ratio = passwordSet.length / 255 + getRandomValues(array) + let password = '' + for (let i = 0; i < array.length; i++) { + password += passwordSet.charAt(array[i] * ratio) + } + return password +} + +/** + * Fills the given array with cryptographically secure random values. + * If the crypto API is not available, it falls back to less secure Math.random(). + * Crypto API is available in modern browsers on secure contexts (HTTPS). + * + * @param {Uint8Array} array - The array to fill with random values. + */ +function getRandomValues(array: Uint8Array): void { + if (self?.crypto?.getRandomValues) { + self.crypto.getRandomValues(array) + return + } + + let len = array.length + while (len--) { + array[len] = Math.floor(Math.random() * 256) + } +} diff --git a/apps/files_sharing/src/utils/NodeShareUtils.ts b/apps/files_sharing/src/utils/NodeShareUtils.ts new file mode 100644 index 00000000000..f14f981e2ad --- /dev/null +++ b/apps/files_sharing/src/utils/NodeShareUtils.ts @@ -0,0 +1,58 @@ +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +import { getCurrentUser } from '@nextcloud/auth' +import type { Node } from '@nextcloud/files' +import { ShareType } from '@nextcloud/sharing' + +type Share = { + /** The recipient display name */ + 'display-name': string + /** The recipient user id */ + id: string + /** The share type */ + type: ShareType +} + +const getSharesAttribute = function(node: Node) { + return Object.values(node.attributes.sharees).flat() as Share[] +} + +export const isNodeSharedWithMe = function(node: Node) { + const uid = getCurrentUser()?.uid + const shares = getSharesAttribute(node) + + // If you're the owner, you can't share with yourself + if (node.owner === uid) { + return false + } + + return shares.length > 0 && ( + // If some shares are shared with you as a direct user share + shares.some(share => share.id === uid && share.type === ShareType.User) + // Or of the file is shared with a group you're in + // (if it's returned by the backend, we assume you're in it) + || shares.some(share => share.type === ShareType.Group) + ) +} + +export const isNodeSharedWithOthers = function(node: Node) { + const uid = getCurrentUser()?.uid + const shares = getSharesAttribute(node) + + // If you're NOT the owner, you can't share with yourself + if (node.owner === uid) { + return false + } + + return shares.length > 0 + // If some shares are shared with you as a direct user share + && shares.some(share => share.id !== uid && share.type !== ShareType.Group) +} + +export const isNodeShared = function(node: Node) { + const shares = getSharesAttribute(node) + return shares.length > 0 +} diff --git a/apps/files_sharing/src/utils/SharedWithMe.js b/apps/files_sharing/src/utils/SharedWithMe.js index bd39c765221..2f63932bfbe 100644 --- a/apps/files_sharing/src/utils/SharedWithMe.js +++ b/apps/files_sharing/src/utils/SharedWithMe.js @@ -1,30 +1,12 @@ /** - * @copyright Copyright (c) 2019 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * - * @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 <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ -import { Type as ShareTypes } from '@nextcloud/sharing' +import { ShareType } from '@nextcloud/sharing' const shareWithTitle = function(share) { - if (share.type === ShareTypes.SHARE_TYPE_GROUP) { + if (share.type === ShareType.Group) { return t( 'files_sharing', 'Shared with you and the group {group} by {owner}', @@ -33,9 +15,9 @@ const shareWithTitle = function(share) { owner: share.ownerDisplayName, }, undefined, - { escape: false } + { escape: false }, ) - } else if (share.type === ShareTypes.SHARE_TYPE_CIRCLE) { + } else if (share.type === ShareType.Team) { return t( 'files_sharing', 'Shared with you and {circle} by {owner}', @@ -44,9 +26,9 @@ const shareWithTitle = function(share) { owner: share.ownerDisplayName, }, undefined, - { escape: false } + { escape: false }, ) - } else if (share.type === ShareTypes.SHARE_TYPE_ROOM) { + } else if (share.type === ShareType.Room) { if (share.shareWithDisplayName) { return t( 'files_sharing', @@ -56,7 +38,7 @@ const shareWithTitle = function(share) { owner: share.ownerDisplayName, }, undefined, - { escape: false } + { escape: false }, ) } else { return t( @@ -66,7 +48,7 @@ const shareWithTitle = function(share) { owner: share.ownerDisplayName, }, undefined, - { escape: false } + { escape: false }, ) } } else { @@ -75,7 +57,7 @@ const shareWithTitle = function(share) { 'Shared with you by {owner}', { owner: share.ownerDisplayName }, undefined, - { escape: false } + { escape: false }, ) } } |