Skip to content

Commit f8ad326

Browse files
authored
Merge pull request #61312 from nextcloud/fix/files-external-edit-auth-mechanism
fix(files_external): Allow editing auth mechanism on saved mounts
2 parents 5467d41 + 230b149 commit f8ad326

340 files changed

Lines changed: 384 additions & 306 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/files_external/src/components/AddExternalStorageDialog/AddExternalStorageDialog.vue

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import ApplicableEntities from './ApplicableEntities.vue'
2727
import AuthMechanismConfiguration from './AuthMechanismConfiguration.vue'
2828
import BackendConfiguration from './BackendConfiguration.vue'
2929
import MountOptions from './MountOptions.vue'
30+
import { pruneUnusedAuthMechanismOptions } from '../../utils/externalStorageUtils.ts'
3031
3132
const open = defineModel<boolean>('open', { default: true })
3233
@@ -63,6 +64,14 @@ const authMechanism = computed({
6364
return authMechanisms.value.find((a) => a.identifier === internalStorage.value.authMechanism)
6465
},
6566
set(value?: IAuthMechanism) {
67+
const previous = authMechanisms.value.find((a) => a.identifier === internalStorage.value.authMechanism)
68+
if (previous && previous.identifier !== value?.identifier && internalStorage.value.backendOptions) {
69+
pruneUnusedAuthMechanismOptions(
70+
internalStorage.value.backendOptions,
71+
previous.configuration,
72+
[value?.configuration, backend.value?.configuration],
73+
)
74+
}
6675
internalStorage.value.authMechanism = value?.identifier
6776
},
6877
})
@@ -106,7 +115,7 @@ watch(authMechanisms, () => {
106115
<NcSelect
107116
v-model="authMechanism"
108117
:options="authMechanisms"
109-
:disabled="!internalStorage.backend || authMechanisms.length <= 1 || !!(internalStorage.id && internalStorage.authMechanism)"
118+
:disabled="!internalStorage.backend || authMechanisms.length <= 1"
110119
:inputLabel="t('files_external', 'Authentication')"
111120
label="name"
112121
required />

apps/files_external/src/utils/externalStorageUtils.spec.ts

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
import { File, Folder, Permission } from '@nextcloud/files'
77
import { describe, expect, test } from 'vitest'
8-
import { isNodeExternalStorage } from './externalStorageUtils.ts'
8+
import { isNodeExternalStorage, pruneUnusedAuthMechanismOptions } from './externalStorageUtils.ts'
99

1010
describe('Is node an external storage', () => {
1111
test('A Folder with a backend and a valid scope is an external storage', () => {
@@ -78,3 +78,41 @@ describe('Is node an external storage', () => {
7878
expect(isNodeExternalStorage(folder)).toBe(false)
7979
})
8080
})
81+
82+
describe('Prune unused authentication mechanism options', () => {
83+
test('removes the previous mechanism options the new mechanism does not use', () => {
84+
const backendOptions: Record<string, unknown> = { user: 'alice', password: 'secret', client_id: 'abc' }
85+
pruneUnusedAuthMechanismOptions(
86+
backendOptions,
87+
{ user: {}, password: {} },
88+
[{ client_id: {}, client_secret: {} }, {}],
89+
)
90+
expect(backendOptions).toEqual({ client_id: 'abc' })
91+
})
92+
93+
test('keeps backend options when only the mechanism changes', () => {
94+
const backendOptions: Record<string, unknown> = { host: 'h', root: '/r', user: 'alice', password: 'secret' }
95+
pruneUnusedAuthMechanismOptions(
96+
backendOptions,
97+
{ user: {}, password: {} },
98+
[{ token: {} }, { host: {}, root: {} }],
99+
)
100+
expect(backendOptions).toEqual({ host: 'h', root: '/r' })
101+
})
102+
103+
test('keeps fields shared between the old and new mechanism', () => {
104+
const backendOptions: Record<string, unknown> = { configured: true, user: 'alice' }
105+
pruneUnusedAuthMechanismOptions(
106+
backendOptions,
107+
{ configured: {}, user: {} },
108+
[{ configured: {} }, {}],
109+
)
110+
expect(backendOptions).toEqual({ configured: true })
111+
})
112+
113+
test('does nothing when there is no previous configuration', () => {
114+
const backendOptions: Record<string, unknown> = { user: 'alice' }
115+
pruneUnusedAuthMechanismOptions(backendOptions, undefined, [{}, {}])
116+
expect(backendOptions).toEqual({ user: 'alice' })
117+
})
118+
})

apps/files_external/src/utils/externalStorageUtils.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,28 @@ export function isNodeExternalStorage(node: INode) {
2828
// Specific markers that we're sure are ext storage only
2929
return attributes.scope === 'personal' || attributes.scope === 'system'
3030
}
31+
32+
/**
33+
* Remove stored option values that belonged to the previous authentication
34+
* mechanism and are not used by the newly selected mechanism or the backend.
35+
*
36+
* A mount keeps all backend and authentication values in a single map keyed by
37+
* configuration field name. When the mechanism is changed on an existing mount,
38+
* the previous mechanism's fields would otherwise linger as unused values.
39+
*
40+
* @param backendOptions - The stored option values, mutated in place
41+
* @param previousConfiguration - Configuration of the previously selected mechanism
42+
* @param keptConfigurations - Configurations whose field names must be preserved
43+
*/
44+
export function pruneUnusedAuthMechanismOptions(
45+
backendOptions: Record<string, unknown>,
46+
previousConfiguration: Record<string, unknown> | undefined,
47+
keptConfigurations: Array<Record<string, unknown> | undefined>,
48+
): void {
49+
const kept = new Set(keptConfigurations.flatMap((configuration) => Object.keys(configuration ?? {})))
50+
for (const key of Object.keys(previousConfiguration ?? {})) {
51+
if (!kept.has(key)) {
52+
delete backendOptions[key]
53+
}
54+
}
55+
}

dist/ActivityCommentAction-C8NJ--_R.chunk.mjs

Lines changed: 0 additions & 2 deletions
This file was deleted.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import{a as t}from"./index-DL1yHC1K-orb-zzee.chunk.mjs";import{t as e}from"./translation-DoG5ZELJ-C5oC8Tcn.chunk.mjs";import{C as m,a}from"./CommentView-CO7XL0eV.chunk.mjs";import{l as p}from"./activity-B66lnZYB.chunk.mjs";import{b as i,r as s,o as n,c,m as u}from"./Web-ByHSuvRG.chunk.mjs";import{_ as l}from"./public-C1mLBHT3.chunk.mjs";import"./index-BRuD4Qrz.chunk.mjs";import"./NcModal-DUWLRm_F-C0gD58QY.chunk.mjs";import"./logger-D3RVzcfQ-B261d025.chunk.mjs";import"./createElementId-DhjFt1I9-CZ2eH1SD.chunk.mjs";import"./index-C6ey-Mhx.chunk.mjs";import"./TrashCanOutline-CW4_EEhq.chunk.mjs";import"./mdi-Ci0zJ0QG.chunk.mjs";import"./pinia-Bn5aG74F.chunk.mjs";import"./PencilOutline-DJX4SP_q.chunk.mjs";/* empty css */import"./NcAvatar-M3-CbKbq-DbNpmr-b.chunk.mjs";import"./index-D-iKxf2E.chunk.mjs";import"./util-djQ-4xJ5.chunk.mjs";import"./ArrowRight-BgQTbtKu.chunk.mjs";import"./colors-BDeMBgfq-D1xNHBAd.chunk.mjs";import"./NcUserStatusIcon-DsviB2Cr-CJ45dJff.chunk.mjs";import"./NcDateTime.vue_vue_type_script_setup_true_lang-BJuPH7S7-BRczm9CK.chunk.mjs";import"./NcUserBubble-CDQa0hGy-DMYvkftD.chunk.mjs";import"./GetComments-DFpRzp64.chunk.mjs";import"./index-CI-5vlTq.chunk.mjs";const d=i({components:{Comment:a},mixins:[m],props:{reloadCallback:{type:Function,required:!0}},methods:{onNewComment(){try{this.reloadCallback()}catch(o){t(e("comments","Could not reload comments")),p.error("Could not reload comments",{error:o})}}}});function C(o,f,y,w,D,N){const r=s("Comment");return n(),c(r,u(o.editorData,{autoComplete:o.autoComplete,resourceType:o.resourceType,editor:!0,userData:o.userData,resourceId:o.resourceId,class:"comments-action",onNew:o.onNewComment}),null,16,["autoComplete","resourceType","userData","resourceId","onNew"])}const S=l(d,[["render",C],["__scopeId","data-v-29a1e244"]]);export{S as default};
2+
//# sourceMappingURL=ActivityCommentAction-CxhX-kOL.chunk.mjs.map
File renamed without changes.

dist/ActivityCommentAction-C8NJ--_R.chunk.mjs.map renamed to dist/ActivityCommentAction-CxhX-kOL.chunk.mjs.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/ActivityCommentAction-C8NJ--_R.chunk.mjs.map.license renamed to dist/ActivityCommentAction-CxhX-kOL.chunk.mjs.map.license

File renamed without changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import{t as s}from"./translation-DoG5ZELJ-C5oC8Tcn.chunk.mjs";import{C as p,a}from"./CommentView-CO7XL0eV.chunk.mjs";import{_ as i}from"./public-C1mLBHT3.chunk.mjs";import{r as n,o as c,c as u,m as l}from"./Web-ByHSuvRG.chunk.mjs";import"./index-C6ey-Mhx.chunk.mjs";import"./pinia-Bn5aG74F.chunk.mjs";import"./PencilOutline-DJX4SP_q.chunk.mjs";import"./logger-D3RVzcfQ-B261d025.chunk.mjs";import"./createElementId-DhjFt1I9-CZ2eH1SD.chunk.mjs";import"./NcModal-DUWLRm_F-C0gD58QY.chunk.mjs";/* empty css */import"./NcAvatar-M3-CbKbq-DbNpmr-b.chunk.mjs";import"./index-D-iKxf2E.chunk.mjs";import"./util-djQ-4xJ5.chunk.mjs";import"./ArrowRight-BgQTbtKu.chunk.mjs";import"./colors-BDeMBgfq-D1xNHBAd.chunk.mjs";import"./NcUserStatusIcon-DsviB2Cr-CJ45dJff.chunk.mjs";import"./NcDateTime.vue_vue_type_script_setup_true_lang-BJuPH7S7-BRczm9CK.chunk.mjs";import"./TrashCanOutline-CW4_EEhq.chunk.mjs";import"./NcUserBubble-CDQa0hGy-DMYvkftD.chunk.mjs";import"./index-DL1yHC1K-orb-zzee.chunk.mjs";import"./index-BRuD4Qrz.chunk.mjs";import"./mdi-Ci0zJ0QG.chunk.mjs";import"./activity-B66lnZYB.chunk.mjs";import"./GetComments-DFpRzp64.chunk.mjs";import"./index-CI-5vlTq.chunk.mjs";const d={name:"ActivityCommentEntry",components:{Comment:a},mixins:[p],props:{comment:{type:Object,required:!0},reloadCallback:{type:Function,required:!0}},data(){return{commentMessage:""}},watch:{comment(){this.commentMessage=this.comment.props.message}},mounted(){this.commentMessage=this.comment.props.message},methods:{t:s}};function g(t,e,o,f,m,C){const r=n("Comment");return c(),u(r,l({ref:"comment",tag:"li"},o.comment.props,{autoComplete:t.autoComplete,resourceType:t.resourceType,message:m.commentMessage,resourceId:t.resourceId,userData:t.genMentionsData(o.comment.props.mentions),class:"comments-activity",onDelete:e[0]||(e[0]=y=>o.reloadCallback())}),null,16,["autoComplete","resourceType","message","resourceId","userData"])}const Q=i(d,[["render",g],["__scopeId","data-v-afc310f1"]]);export{Q as default};
2+
//# sourceMappingURL=ActivityCommentEntry-DFdL49TV.chunk.mjs.map
File renamed without changes.

0 commit comments

Comments
 (0)