fix: account rename caused inconsistencies in refs

Massimo Melina committed Feb 11, 2022 at 12:38 UTC 53af72395be458a55eff4dd2e7d65a0316d61707
2 files changed +33 -18
src/misc.ts
+8
@@ -5,6 +5,7 @@ import { watch } from 'fs'
5 import _ from 'lodash'
6
7 export type Callback<IN=void, OUT=void> = (x:IN) => OUT
8 +export type Dict<T = any> = Record<string, T>
9
10 export function enforceFinal(sub:string, s:string) {
11 return s.endsWith(sub) ? s : s+sub
@@ -186,3 +187,10 @@ export function dirTraversal(s?: string) {
187 export function isWindowsDrive(s?: string) {
188 return s && /^[a-zA-Z]:$/.test(s)
189 }
190 +
191 +export function objRenameKey(o: Dict | undefined, from: string, to: string) {
192 + if (!o || !o.hasOwnProperty(from) || from === to) return
193 + o[to] = o[from]
194 + delete o[from]
195 + return true
196 +}
src/perm.ts
+25 -18
@@ -1,7 +1,7 @@
1 import _ from 'lodash'
2 import yaml from 'yaml'
3 import { hashPassword } from './crypt'
4 -import { setHidden, wantArray } from './misc'
4 +import { objRenameKey, setHidden, wantArray } from './misc'
5 import { watchLoad } from './watchLoad'
6 import Koa from 'koa'
7 import { CFG_ALLOW_CLEAR_TEXT_LOGIN, getConfig, subscribeConfig } from './config'
@@ -106,37 +106,44 @@ async function applyAccounts(newAccounts: Accounts) {
106 // we should validate content here
107 accounts = newAccounts
108 await Promise.all(_.map(accounts, async (rec,k) => {
109 - const lc = k.toLocaleLowerCase()
109 + const norm = normalizeUsername(k)
110 if (!rec) // an empty object in yaml is stored as null
111 - rec = accounts[lc] = { username: lc, srp:'' }
112 - else if (lc !== k) {
113 - accounts[lc] = rec
114 - delete accounts[k]
115 - k = lc
116 - }
117 - setHidden(rec, { username: k })
111 + rec = accounts[norm] = { username: norm, srp:'' }
112 + else
113 + objRenameKey(accounts, k, norm)
114 + setHidden(rec, { username: norm })
115 await updateAccount(rec)
116 }))
117 }
118
119 +function normalizeUsername(username: string) {
120 + return username.toLocaleLowerCase()
121 +}
122 +
123 export function renameAccount(from: string, to: string) {
124 + from = normalizeUsername(from)
125 + to = normalizeUsername(to)
126 if (!to || !accounts[from] || accounts[to])
127 return false
128 if (to === from)
129 return true
127 - accounts[to] = accounts[from]
128 - delete accounts[from]
129 - setHidden(accounts[to], { username: to })
130 - recur(vfs.root)
130 + objRenameKey(accounts, from, to)
131 + updateReferences()
132 saveAccountsAsap()
133 return true
134
134 - function recur(n: VfsNode) {
135 - const p = n.perm
136 - if (p?.[from]) {
137 - p[to] = p[from]
138 - delete p[from]
135 + function updateReferences() {
136 + setHidden(accounts[to], { username: to })
137 + recur(vfs.root)
138 + for (const a of Object.values(accounts)) {
139 + const idx = a.belongs?.indexOf(from)
140 + if (idx !== undefined && idx >= 0)
141 + a.belongs![idx] = to
142 }
143 + }
144 +
145 + function recur(n: VfsNode) {
146 + objRenameKey(n.perm, from, to)
147 if (n.children)
148 for (const c of n.children)
149 recur(c)