Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const open = defineModel<boolean>('open', { default: true })
const {
storage = { backendOptions: {}, mountOptions: {}, type: isAdmin ? 'system' : 'personal' },
} = defineProps<{
storage?: Partial<IStorage>
storage?: Partial<IStorage> & { backendOptions: IStorage['backendOptions'] }
}>()

defineEmits<{
Expand Down Expand Up @@ -88,7 +88,7 @@ watch(authMechanisms, () => {
:label="t('files_external', 'Folder name')"
required />

<MountOptions v-model="internalStorage.mountOptions!" />
<MountOptions v-model="internalStorage.mountOptions" />

<ApplicableEntities
v-if="isAdmin"
Expand All @@ -112,13 +112,13 @@ watch(authMechanisms, () => {
required />

<BackendConfiguration
v-if="backend && internalStorage.backendOptions"
v-if="backend"
v-model="internalStorage.backendOptions"
:class="$style.externalStorageDialog__configuration"
:configuration="backend.configuration" />

<AuthMechanismConfiguration
v-if="authMechanism && internalStorage.backendOptions"
v-if="authMechanism"
v-model="internalStorage.backendOptions"
:class="$style.externalStorageDialog__configuration"
:authMechanism="authMechanism" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,17 @@ import NcButton from '@nextcloud/vue/components/NcButton'
import NcCheckboxRadioSwitch from '@nextcloud/vue/components/NcCheckboxRadioSwitch'
import NcIconSvgWrapper from '@nextcloud/vue/components/NcIconSvgWrapper'
import NcSelect from '@nextcloud/vue/components/NcSelect'
import { parseMountOptions } from '../../store/storages.ts'
import { MountOptionsCheckFilesystem } from '../../types.ts'

const mountOptions = defineModel<Partial<IMountOptions>>({ required: true })
watchEffect(() => {
if (Object.keys(mountOptions.value).length === 0) {
// parse and initialize with defaults if needed
mountOptions.value = parseMountOptions(mountOptions.value)
mountOptions.value.encrypt = true
mountOptions.value.previews = true
mountOptions.value.enable_sharing = false
mountOptions.value.filesystem_check_changes = MountOptionsCheckFilesystem.OncePerRequest
mountOptions.value.encoding_compatibility = false
mountOptions.value.readonly = false
}
})

Expand Down
54 changes: 5 additions & 49 deletions apps/files_external/src/store/storages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
* SPDX-License-Identifier: AGPL-3.0-or-later
*/

import type { IStorage } from '../types.ts'
import type { IStorage } from '../types.d.ts'

import axios from '@nextcloud/axios'
import { loadState } from '@nextcloud/initial-state'
import { addPasswordConfirmationInterceptors, PwdConfirmationMode } from '@nextcloud/password-confirmation'
import { generateUrl } from '@nextcloud/router'
import { defineStore } from 'pinia'
import { ref, toRaw } from 'vue'
import { MountOptionsCheckFilesystem } from '../types.ts'

const { isAdmin } = loadState<{ isAdmin: boolean }>('files_external', 'settings')

Expand All @@ -31,7 +30,7 @@ export const useStorages = defineStore('files_external--storages', () => {
toRaw(storage),
{ confirmPassword: PwdConfirmationMode.Strict },
)
globalStorages.value.push(parseStorage(data))
globalStorages.value.push(data)
}

/**
Expand All @@ -46,7 +45,7 @@ export const useStorages = defineStore('files_external--storages', () => {
toRaw(storage),
{ confirmPassword: PwdConfirmationMode.Strict },
)
userStorages.value.push(parseStorage(data))
userStorages.value.push(data)
}

/**
Expand Down Expand Up @@ -78,7 +77,7 @@ export const useStorages = defineStore('files_external--storages', () => {
{ confirmPassword: PwdConfirmationMode.Strict },
)

overrideStorage(parseStorage(data))
overrideStorage(data)
}

/**
Expand All @@ -88,7 +87,7 @@ export const useStorages = defineStore('files_external--storages', () => {
*/
async function reloadStorage(storage: IStorage) {
const { data } = await axios.get(getUrl(storage))
overrideStorage(parseStorage(data))
overrideStorage(data)
}

// initialize the store
Expand All @@ -112,7 +111,6 @@ export const useStorages = defineStore('files_external--storages', () => {
const url = `apps/files_external/${type}`
const { data } = await axios.get<Record<number, IStorage>>(generateUrl(url))
return Object.values(data)
.map(parseStorage)
}

/**
Expand Down Expand Up @@ -152,45 +150,3 @@ export const useStorages = defineStore('files_external--storages', () => {
}
}
})

/**
* @param storage - The storage from API
*/
function parseStorage(storage: IStorage) {
return {
...storage,
mountOptions: parseMountOptions(storage.mountOptions),
}
}

/**
* Parse the mount options and convert string boolean values to
* actual booleans and numeric strings to numbers
*
* @param options - The mount options to parse
*/
export function parseMountOptions(options: IStorage['mountOptions']) {
const mountOptions = { ...options }
mountOptions.encrypt = convertBooleanOptions(mountOptions.encrypt, true)
mountOptions.previews = convertBooleanOptions(mountOptions.previews, true)
mountOptions.enable_sharing = convertBooleanOptions(mountOptions.enable_sharing, false)
mountOptions.filesystem_check_changes = typeof mountOptions.filesystem_check_changes === 'string'
? Number.parseInt(mountOptions.filesystem_check_changes)
: (mountOptions.filesystem_check_changes ?? MountOptionsCheckFilesystem.OncePerRequest)
mountOptions.encoding_compatibility = convertBooleanOptions(mountOptions.encoding_compatibility, false)
mountOptions.readonly = convertBooleanOptions(mountOptions.readonly, false)
return mountOptions
}

/**
* Convert backend encoding of boolean options
*
* @param option - The option value from API
* @param fallback - The fallback (default) value
*/
function convertBooleanOptions(option: unknown, fallback = false) {
if (option === undefined) {
return fallback
}
return option === true || option === 'true' || option === '1'
}
5 changes: 2 additions & 3 deletions apps/files_external/src/views/ExternalStoragesSection.vue
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,7 @@ async function addStorage(storage?: Partial<IStorage>) {
}
newStorage.value = undefined
} catch (error) {
logger.error('Failed to add external storage', { error, storage })
newStorage.value = { ...storage }
logger.error('Failed to add external storage', { error })
showDialog.value = true
}
}
Expand Down Expand Up @@ -135,8 +134,8 @@ async function addStorage(storage?: Partial<IStorage>) {
</NcButton>

<AddExternalStorageDialog
v-model="newStorage"
v-model:open="showDialog"
:storage="newStorage"
@close="addStorage" />

<UserMountSettings v-if="settings.isAdmin" />
Expand Down
20 changes: 12 additions & 8 deletions apps/user_ldap/lib/Access.php
Original file line number Diff line number Diff line change
Expand Up @@ -704,7 +704,7 @@ private function ldap2NextcloudNames(array $ldapObjects, bool $isUsers): array {
continue;
}
$sndName = $ldapObject[$sndAttribute][0] ?? '';
$this->applyUserDisplayName($ncName, $nameByLDAP, $sndName);
$this->cacheUserDisplayName($ncName, $nameByLDAP, $sndName);
} elseif ($nameByLDAP !== null) {
$this->cacheGroupDisplayName($ncName, $nameByLDAP);
}
Expand Down Expand Up @@ -752,16 +752,20 @@ public function cacheGroupExists(string $gid): void {
$this->connection->writeToCache('groupExists' . $gid, true);
}

public function applyUserDisplayName(string $uid, string $displayName, string $displayName2 = ''): void {
$user = $this->userManager->get($uid);
/**
* caches the user display name
*
* @param string $ocName the internal Nextcloud username
* @param string $displayName the display name
* @param string $displayName2 the second display name
* @throws \Exception
*/
public function cacheUserDisplayName(string $ocName, string $displayName, string $displayName2 = ''): void {
$user = $this->userManager->get($ocName);
if ($user === null) {
return;
}
$composedDisplayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
$this->cacheUserDisplayName($uid, $composedDisplayName);
}

public function cacheUserDisplayName(string $ocName, string $displayName): void {
$displayName = $user->composeAndStoreDisplayName($displayName, $displayName2);
$cacheKeyTrunk = 'getDisplayName';
$this->connection->writeToCache($cacheKeyTrunk . $ocName, $displayName);
}
Expand Down
15 changes: 7 additions & 8 deletions apps/user_ldap/lib/User/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -119,8 +119,12 @@ public function processAttributes(array $ldapEntry): void {
$displayName2 = (string)$ldapEntry[$attr][0];
}
if ($displayName !== '') {
$composedDisplayName = $this->composeAndStoreDisplayName($displayName, $displayName2);
$this->access->cacheUserDisplayName($this->getUsername(), $composedDisplayName);
$this->composeAndStoreDisplayName($displayName, $displayName2);
$this->access->cacheUserDisplayName(
$this->getUsername(),
$displayName,
$displayName2
);
}
unset($attr);

Expand All @@ -130,8 +134,7 @@ public function processAttributes(array $ldapEntry): void {
$attr = strtolower($this->connection->ldapEmailAttribute);
if (isset($ldapEntry[$attr])) {
$mailValue = 0;
$emailValues = count($ldapEntry[$attr]);
for ($x = 0; $x < $emailValues; $x++) {
for ($x = 0; $x < count($ldapEntry[$attr]); $x++) {
if (filter_var($ldapEntry[$attr][$x], FILTER_VALIDATE_EMAIL)) {
$mailValue = $x;
break;
Expand Down Expand Up @@ -454,10 +457,6 @@ public function composeAndStoreDisplayName(string $displayName, string $displayN
return $displayName;
}

public function fetchStoredDisplayName(): string {
return $this->userConfig->getValueString($this->uid, 'user_ldap', 'displayName', '');
}

/**
* Stores the LDAP Username in the Database
*/
Expand Down
67 changes: 23 additions & 44 deletions apps/user_ldap/lib/User_LDAP.php
Original file line number Diff line number Diff line change
Expand Up @@ -262,8 +262,7 @@ public function getUsers($search = '', $limit = 10, $offset = 0) {
/**
* checks whether a user is still available on LDAP
*
* @param string|User $user either the Nextcloud user id or an instance of
* that user
* @param string|User $user either the Nextcloud user id or an instance of that user
* @throws \Exception
* @throws ServerNotAvailableException
*/
Expand Down Expand Up @@ -422,21 +421,26 @@ public function getHome($uid) {
return $path;
}

private function getDisplayNameFromDatabase(string $uid): ?string {
$user = $this->access->userManager->get($uid);
if ($user instanceof User) {
$displayName = $user->fetchStoredDisplayName();
if ($displayName !== '') {
return $displayName;
}
/**
* get display name of the user
* @param string $uid user ID of the user
* @return string|false display name
*/
public function getDisplayName($uid) {
if ($this->userPluginManager->implementsActions(Backend::GET_DISPLAYNAME)) {
return $this->userPluginManager->getDisplayName($uid);
}
if ($user instanceof OfflineUser) {
return $user->getDisplayName();

if (!$this->userExists($uid)) {
return false;
}

$cacheKey = 'getDisplayName' . $uid;
if (!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
return $displayName;
}
return null;
}

private function getDisplayNameFromLdap(string $uid): string {
//Check whether the display name is configured to have a 2nd feature
$additionalAttribute = $this->access->connection->ldapUserDisplayName2;
$displayName2 = '';
if ($additionalAttribute !== '') {
Expand All @@ -458,40 +462,16 @@ private function getDisplayNameFromLdap(string $uid): string {

$user = $this->access->userManager->get($uid);
if ($user instanceof User) {
return $user->composeAndStoreDisplayName($displayName, (string)$displayName2);
$displayName = $user->composeAndStoreDisplayName($displayName, (string)$displayName2);
$this->access->connection->writeToCache($cacheKey, $displayName);
}
if ($user instanceof OfflineUser) {
return $user->getDisplayName();
$displayName = $user->getDisplayName();
}
}

return '';
}

public function getDisplayName($uid): string {
if ($this->userPluginManager->implementsActions(Backend::GET_DISPLAYNAME)) {
return $this->userPluginManager->getDisplayName($uid);
}

if (!$this->userExists($uid)) {
return '';
}

$cacheKey = 'getDisplayName' . $uid;
if (!is_null($displayName = $this->access->connection->getFromCache($cacheKey))) {
return $displayName;
}

if ($displayName = $this->getDisplayNameFromDatabase($uid)) {
$this->access->connection->writeToCache($cacheKey, $displayName);
return $displayName;
}

if ($displayName = $this->getDisplayNameFromLdap($uid)) {
$this->access->connection->writeToCache($cacheKey, $displayName);
}

return $displayName;
return null;
}

/**
Expand All @@ -515,8 +495,7 @@ public function setDisplayName($uid, $displayName) {
* @param string $search
* @param int|null $limit
* @param int|null $offset
* @return array an array of all displayNames (value) and the corresponding
* uids (key)
* @return array an array of all displayNames (value) and the corresponding uids (key)
*/
public function getDisplayNames($search = '', $limit = null, $offset = null) {
$cacheKey = 'getDisplayNames-' . $search . '-' . $limit . '-' . $offset;
Expand Down
6 changes: 6 additions & 0 deletions build/psalm-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2605,10 +2605,16 @@
<DeprecatedInterface>
<code><![CDATA[User_LDAP]]></code>
</DeprecatedInterface>
<ImplementedReturnTypeMismatch>
<code><![CDATA[string|false]]></code>
</ImplementedReturnTypeMismatch>
<MoreSpecificImplementedParamType>
<code><![CDATA[$limit]]></code>
<code><![CDATA[$offset]]></code>
</MoreSpecificImplementedParamType>
<NullableReturnStatement>
<code><![CDATA[null]]></code>
</NullableReturnStatement>
<RedundantCondition>
<code><![CDATA[$displayName && (count($displayName) > 0)]]></code>
<code><![CDATA[is_string($dn)]]></code>
Expand Down
2 changes: 0 additions & 2 deletions dist/AuthMechanismRsa-CX_mdruD.chunk.mjs

This file was deleted.

2 changes: 2 additions & 0 deletions dist/AuthMechanismRsa-ClWp2M4c.chunk.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import{f as y,q as g,u as o,o as n,g as p,I as h,w as v,j as _,t as V,y as k,s as x,c as d,F as M,C as q,E as w,G as K,h as f,r as U}from"./runtime-dom.esm-bundler-DPEdZePn.chunk.mjs";import{c as j}from"./index-BCebL___.chunk.mjs";import{a as C}from"./index-C1xmmKTZ-CGpLs37u.chunk.mjs";import{t as s}from"./translation-DoG5ZELJ-XUrtIRvk.chunk.mjs";import{g as E}from"./createElementId-DhjFt1I9-Bh_4C_f2.chunk.mjs";import{N}from"./autolink-U5pBzLgI-Bpd-_ISJ.chunk.mjs";import{N as S}from"./NcSelect-DLheQ2yp-DujwD-Lb.chunk.mjs";import{N as A}from"./NcCheckboxRadioSwitch-BMsPx74L-fxtySBP5.chunk.mjs";import{N as L}from"./NcPasswordField-uaMO2pdt-Y_frnjqT.chunk.mjs";import{_ as z}from"./TrashCanOutline-CIT5iIpm.chunk.mjs";import{C as c,a as b}from"./types-DjW25Xxr.chunk.mjs";import{l as B}from"./logger-resIultJ.chunk.mjs";const P=y({__name:"ConfigurationEntry",props:k({configKey:{},configOption:{}},{modelValue:{type:[String,Boolean],default:""},modelModifiers:{}}),emits:["update:modelValue"],setup(e){const a=g(e,"modelValue");return(t,i)=>e.configOption.type!==o(c).Boolean?(n(),p(h(e.configOption.type===o(c).Password?o(L):o(z)),{key:0,modelValue:a.value,"onUpdate:modelValue":i[0]||(i[0]=l=>a.value=l),name:e.configKey,required:!(e.configOption.flags&o(b).Optional),label:e.configOption.value,title:e.configOption.tooltip},null,8,["modelValue","name","required","label","title"])):(n(),p(o(A),{key:1,modelValue:a.value,"onUpdate:modelValue":i[1]||(i[1]=l=>a.value=l),type:"switch",title:e.configOption.tooltip},{default:v(()=>[_(V(e.configOption.value),1)]),_:1},8,["modelValue","title"]))}}),R=y({__name:"AuthMechanismRsa",props:k({authMechanism:{}},{modelValue:{required:!0},modelModifiers:{}}),emits:["update:modelValue"],setup(e){const a=g(e,"modelValue"),t=U();x(t,()=>{t.value&&(a.value.private_key="",a.value.public_key="")});async function i(){try{const{data:l}=await j.post(E("/apps/files_external/ajax/public_key.php"),{keyLength:t.value});a.value.private_key=l.data.private_key,a.value.public_key=l.data.public_key}catch(l){B.error("Error generating RSA key pair",{error:l}),C(s("files_external","Error generating key pair"))}}return(l,m)=>(n(),d("div",null,[(n(!0),d(M,null,q(e.authMechanism.configuration,(r,u)=>w((n(),p(P,{key:r.value,modelValue:a.value[u],"onUpdate:modelValue":O=>a.value[u]=O,configKey:u,configOption:r},null,8,["modelValue","onUpdate:modelValue","configKey","configOption"])),[[K,!(r.flags&o(b).Hidden)]])),128)),f(o(S),{modelValue:t.value,"onUpdate:modelValue":m[0]||(m[0]=r=>t.value=r),clearable:!1,inputLabel:o(s)("files_external","Key size"),options:[1024,2048,4096],required:""},null,8,["modelValue","inputLabel"]),f(o(N),{disabled:!t.value,wide:"",onClick:i},{default:v(()=>[_(V(o(s)("files_external","Generate keys")),1)]),_:1},8,["disabled"])]))}}),$=Object.freeze(Object.defineProperty({__proto__:null,default:R},Symbol.toStringTag,{value:"Module"}));export{$ as A,P as _};
//# sourceMappingURL=AuthMechanismRsa-ClWp2M4c.chunk.mjs.map
Loading