Skip to content

createUser and updateUser mutate the user array before the config write succeeds #2955

Description

@mairas

externalUserService.createUser mutates the in-memory user array before the write to security.json is known to have succeeded, and does not undo the mutation if it fails:

// src/tokensecurity.ts
options.users.push(newUser)

return new Promise((resolve, reject) => {
  saveSecurityConfig(app, options, (err) => {
    if (err) {
      reject(err)     // options.users still holds newUser
    } else {
      resolve()
    }
  })
})

updateUser has the same shape — it assigns user.type and user.oidc in place, then saves.

On a full or read-only filesystem (not exotic on a Raspberry Pi with a failing SD card) the promise rejects and the caller reports failure, but the record stays in memory. That produces three follow-on effects:

  1. The record authenticates. A retry finds it via findUserByProvider and login succeeds, even though no account exists on disk.
  2. It vanishes without warning. setConfig, addUser, deleteUser and others reassign options from a fresh read of security.json, which does not contain the unsaved record. Any token already issued for it stops resolving, and the user starts getting 401s with no explanation.
  3. It does not survive restart, so the same account works or does not depending on what else has happened since.

saveSecurityConfig writes atomically via tmp+rename, so the file itself is never torn. The inconsistency is purely memory-versus-disk, and nothing reconciles the two until a config reload or a restart.

Persisting first and mutating second — or splicing the entry back out in the rejection branch — would keep the in-memory array consistent with what is actually stored.

Worth noting saveSecurityConfig also calls back with an error if the post-rename chmodSync fails, in which case the data did persist but the caller is told it did not. That inverts the same inconsistency.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions