fix(database): use per-user admin passwords
This commit is contained in:
1 parent
33e80a566e
commit
9862348d70
17 files changed
+188
-38
No files matched your search
+22
-2
@@ -9,6 +9,7 @@ sqlite.exec("PRAGMA foreign_keys = ON");
|
||||
sqlite.exec(readFileSync(new URL("../drizzle/0000_lifeos_cloud_state.sql", import.meta.url), "utf8"));
|
||||
sqlite.exec(readFileSync(new URL("../drizzle/0001_lifeos_records.sql", import.meta.url), "utf8"));
|
||||
sqlite.exec(readFileSync(new URL("../drizzle/0002_lifeos_db_admin.sql", import.meta.url), "utf8"));
|
||||
sqlite.exec(readFileSync(new URL("../drizzle/0003_lifeos_admin_credentials.sql", import.meta.url), "utf8"));
|
||||
const db = {
|
||||
prepare(sql) {
|
||||
return {
|
||||
@@ -37,7 +38,7 @@ async function call(path, method = "GET", data, as = headers) {
|
||||
const response = await worker.fetch(new Request("https://life-os.test" + path, {
|
||||
method, headers: { ...as, "content-type": "application/json" },
|
||||
body: data === undefined ? undefined : JSON.stringify(data)
|
||||
}), { DB: db, LIFEOS_DB_ADMIN_PASSWORD: "local test password with 28 chars" });
|
||||
}), { DB: db });
|
||||
return { status: response.status, body: await response.json() };
|
||||
}
|
||||
|
||||
@@ -104,6 +105,15 @@ assert.deepEqual(Array.from(pending, item => item.kind), ["tasks"], "editing a t
|
||||
await client.window.CloudTest.sync();
|
||||
assert.equal((await call("/api/lifeos-state")).body.state.system.tasksBoard.tasks[0].done, true);
|
||||
assert.equal((await call("/api/db-admin/tables/tasks/rows")).status, 401);
|
||||
assert.equal((await call("/api/db-admin/config")).body.configured, false);
|
||||
assert.equal((await call("/api/db-admin/login", "POST", { username: "lifeos-admin", password: "local test password with 28 chars", acknowledged: true })).status, 409);
|
||||
const setup = await call("/api/db-admin/setup", "POST", {
|
||||
username: "lifeos-admin", password: "local test password with 28 chars", acknowledged: true
|
||||
});
|
||||
assert.equal(setup.status, 200);
|
||||
assert.equal((await call("/api/db-admin/config")).body.configured, true);
|
||||
assert.equal((await call("/api/db-admin/setup", "POST", { username: "lifeos-admin", password: "replacement password 123", acknowledged: true })).status, 409);
|
||||
assert.equal((await call("/api/db-admin/tables/tasks/rows", "GET", undefined, { ...headers, "x-lifeos-admin-session": setup.body.token })).status, 200);
|
||||
assert.equal((await call("/api/db-admin/login", "POST", { username: "lifeos-admin", password: "bad", acknowledged: true })).status, 401);
|
||||
const login = await call("/api/db-admin/login", "POST", {
|
||||
username: "lifeos-admin", password: "local test password with 28 chars", acknowledged: true
|
||||
@@ -115,12 +125,22 @@ let admin = await call("/api/db-admin/tables/tasks/rows", "GET", undefined, auth
|
||||
assert.equal(admin.status, 200);
|
||||
assert.equal(admin.body.total, 2);
|
||||
assert.equal((await call("/api/db-admin/tables/tasks/rows", "GET", undefined, { ...auth, "oai-authenticated-user-id": "bob" })).status, 401, "admin session is tied to one user");
|
||||
assert.equal((await call("/api/db-admin/config", "GET", undefined, { "oai-authenticated-user-id": "bob" })).body.configured, false);
|
||||
assert.equal((await call("/api/db-admin/setup", "POST", { username: "lifeos-admin", password: "bob account password 67890", acknowledged: true }, { "oai-authenticated-user-id": "bob" })).status, 200);
|
||||
assert.equal((await call("/api/db-admin/login", "POST", { username: "lifeos-admin", password: "bob account password 67890", acknowledged: true })).status, 401, "another user's password cannot unlock Alice");
|
||||
admin = await call("/api/db-admin/tables/tasks/rows/t3", "POST", { baseVersion: 0, value: { id: "t3", title: "From database" } }, auth);
|
||||
assert.equal(admin.body.version, 1);
|
||||
admin = await call("/api/db-admin/tables/tasks/rows/t3", "PATCH", { baseVersion: 1, value: { id: "t3", title: "Edited" } }, auth);
|
||||
assert.equal(admin.body.version, 2);
|
||||
assert.equal((await call("/api/db-admin/tables/tasks/rows/t3", "PATCH", { baseVersion: 1, value: { id: "t3", title: "Stale" } }, auth)).status, 409);
|
||||
assert.equal((await call("/api/db-admin/tables/tasks/rows/t3?version=2", "DELETE", undefined, auth)).status, 200);
|
||||
assert.equal((await call("/api/db-admin/logout", "POST", {}, auth)).status, 200);
|
||||
assert.equal((await call("/api/db-admin/password", "POST", { currentPassword: "wrong password 123", newPassword: "new secure password 12345" }, auth)).status, 401);
|
||||
const rotated = await call("/api/db-admin/password", "POST", {
|
||||
currentPassword: "local test password with 28 chars", newPassword: "new secure password 12345"
|
||||
}, auth);
|
||||
assert.equal(rotated.status, 200);
|
||||
assert.equal((await call("/api/db-admin/tables/tasks/rows", "GET", undefined, auth)).status, 401);
|
||||
assert.equal((await call("/api/db-admin/login", "POST", { username: "lifeos-admin", password: "local test password with 28 chars", acknowledged: true })).status, 401);
|
||||
assert.equal((await call("/api/db-admin/login", "POST", { username: "lifeos-admin", password: "new secure password 12345", acknowledged: true })).status, 200);
|
||||
assert.equal((await call("/api/db-admin/logout", "POST", {}, { ...headers, "x-lifeos-admin-session": rotated.body.token })).status, 200);
|
||||
console.log("Life OS record migration, sync conflict, tags, and user isolation valid");
|
||||
Reference in new issue
Block a user