mirror of
https://github.com/gristlabs/grist-core.git
synced 2024-10-27 20:44:07 +00:00
ecff88bd32
Summary: A new set of endpoints for managing installation and site configuration have been added: - GET `/api/install/configs/:key` - get the value of the configuration item with the specified key - PUT `/api/install/configs/:key` - set the value of the configuration item with the specified key - body: the JSON value of the configuration item - DELETE `/api/install/configs/:key` - delete the configuration item with the specified key - GET `/api/orgs/:oid/configs/:key` - get the value of the configuration item with the specified key - PUT `/api/orgs/:oid/configs/:key` - set the value of the configuration item with the specified key - body: the JSON value of the configuration item - DELETE `/api/orgs/:oid/configs/:key` - delete the configuration item with the specified key Configuration consists of key/value pairs, where keys are strings (e.g. `"audit_logs_streaming_destinations"`) and values are JSON, including literals like numbers and strings. Only installation admins and site owners are permitted to modify installation and site configuration, respectively. The endpoints are planned to be used in an upcoming feature for enabling audit log streaming for an installation and/or site. Future functionality may use the endpoints as well, which may require extending the current capabilities (e.g. adding support for storing secrets, additional metadata fields, etc.). Test Plan: Server tests Reviewers: paulfitz, jarek Reviewed By: paulfitz, jarek Subscribers: jarek Differential Revision: https://phab.getgrist.com/D4377
352 lines
10 KiB
TypeScript
352 lines
10 KiB
TypeScript
import { Config } from "app/gen-server/entity/Config";
|
|
import { HomeDBManager } from "app/gen-server/lib/homedb/HomeDBManager";
|
|
import axios from "axios";
|
|
import * as chai from "chai";
|
|
import omit from "lodash/omit";
|
|
import { TestServer } from "test/gen-server/apiUtils";
|
|
import { configForUser } from "test/gen-server/testUtils";
|
|
import * as testUtils from "test/server/testUtils";
|
|
|
|
describe("InstallConfig", function () {
|
|
const assert = chai.assert;
|
|
|
|
let server: TestServer;
|
|
let dbManager: HomeDBManager;
|
|
let homeUrl: string;
|
|
|
|
const chimpy = configForUser("Chimpy");
|
|
const kiwi = configForUser("Kiwi");
|
|
const support = configForUser("Support");
|
|
const anonymous = configForUser("Anonymous");
|
|
|
|
const chimpyEmail = "chimpy@getgrist.com";
|
|
|
|
let oldEnv: testUtils.EnvironmentSnapshot;
|
|
|
|
testUtils.setTmpLogLevel("error");
|
|
|
|
before(async function () {
|
|
oldEnv = new testUtils.EnvironmentSnapshot();
|
|
process.env.GRIST_DEFAULT_EMAIL = chimpyEmail;
|
|
server = new TestServer(this);
|
|
homeUrl = await server.start(["home"]);
|
|
dbManager = server.dbManager;
|
|
});
|
|
|
|
after(async function () {
|
|
oldEnv.restore();
|
|
await server.stop();
|
|
});
|
|
|
|
describe("GET /api/install/configs/:key", async function () {
|
|
let config: Config;
|
|
|
|
before(async function () {
|
|
await dbManager.connection.transaction(async (manager) => {
|
|
config = new Config();
|
|
config.key = "audit_log_streaming_destinations";
|
|
config.value = [
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
];
|
|
await manager.save(config);
|
|
});
|
|
});
|
|
|
|
after(async function () {
|
|
await dbManager.connection.transaction((manager) =>
|
|
manager.createQueryBuilder().delete().from(Config).execute()
|
|
);
|
|
});
|
|
|
|
it("returns 200 on success", async function () {
|
|
const resp = await axios.get(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 200);
|
|
assert.deepEqual(resp.data, {
|
|
id: 1,
|
|
key: "audit_log_streaming_destinations",
|
|
value: [
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
],
|
|
createdAt: config.createdAt.toISOString(),
|
|
updatedAt: config.updatedAt.toISOString(),
|
|
});
|
|
});
|
|
|
|
it("returns 400 if key is invalid", async function () {
|
|
const resp = await axios.get(
|
|
`${homeUrl}/api/install/configs/invalid`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 400);
|
|
assert.deepEqual(resp.data, {
|
|
error: "Invalid config key",
|
|
details: {
|
|
userError: 'Error: value is not "audit_log_streaming_destinations"',
|
|
},
|
|
});
|
|
});
|
|
|
|
it("returns 403 if user isn't an install admin", async function () {
|
|
for (const user of [kiwi, anonymous]) {
|
|
const resp = await axios.get(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
user
|
|
);
|
|
assert.equal(resp.status, 403);
|
|
assert.deepEqual(resp.data, { error: "Access denied" });
|
|
}
|
|
|
|
const resp = await axios.get(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
support
|
|
);
|
|
assert.equal(resp.status, 200);
|
|
});
|
|
|
|
it("returns 404 if key doesn't exist", async function () {
|
|
await dbManager.connection.transaction((manager) =>
|
|
manager.remove(config)
|
|
);
|
|
|
|
const resp = await axios.get(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 404);
|
|
assert.deepEqual(resp.data, {
|
|
error: "config not found",
|
|
});
|
|
});
|
|
});
|
|
|
|
describe("PUT /api/install/configs/:key", async function () {
|
|
after(async function () {
|
|
await dbManager.connection.transaction((manager) =>
|
|
manager.createQueryBuilder().delete().from(Config).execute()
|
|
);
|
|
});
|
|
|
|
function testCreateOrUpdate({ status }: { status: 200 | 201 }) {
|
|
return async function () {
|
|
const resp1 = await axios.put(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
[
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
],
|
|
chimpy
|
|
);
|
|
assert.equal(resp1.status, status);
|
|
assert.deepEqual(omit(resp1.data, "createdAt", "updatedAt"), {
|
|
id: 2,
|
|
key: "audit_log_streaming_destinations",
|
|
value: [
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
],
|
|
});
|
|
assert.hasAllKeys(resp1.data, [
|
|
"id",
|
|
"key",
|
|
"value",
|
|
"createdAt",
|
|
"updatedAt",
|
|
]);
|
|
|
|
const resp2 = await axios.get(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp2.status, 200);
|
|
assert.deepEqual(resp2.data, resp1.data);
|
|
};
|
|
}
|
|
|
|
it(
|
|
"returns 201 if resource was created",
|
|
testCreateOrUpdate({ status: 201 })
|
|
);
|
|
|
|
it(
|
|
"returns 200 if resource was updated",
|
|
testCreateOrUpdate({ status: 200 })
|
|
);
|
|
|
|
it("returns 400 if key invalid", async function () {
|
|
const resp = await axios.put(
|
|
`${homeUrl}/api/install/configs/invalid`,
|
|
"invalid",
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 400);
|
|
assert.deepEqual(resp.data, {
|
|
error: "Invalid config key",
|
|
details: {
|
|
userError: 'Error: value is not "audit_log_streaming_destinations"',
|
|
},
|
|
});
|
|
});
|
|
|
|
it("returns 400 if body is invalid", async function () {
|
|
let resp = await axios.put(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
"invalid",
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 400);
|
|
assert.deepEqual(resp.data, {
|
|
error: "Invalid config value",
|
|
details: {
|
|
userError: "Error: value is not an array",
|
|
},
|
|
});
|
|
|
|
resp = await axios.put(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
["invalid"],
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 400);
|
|
assert.deepEqual(resp.data, {
|
|
error: "Invalid config value",
|
|
details: {
|
|
userError:
|
|
"Error: value[0] is not a AuditLogStreamingDestination; value[0] is not an object",
|
|
},
|
|
});
|
|
});
|
|
|
|
it("returns 403 if user isn't an install admin", async function () {
|
|
for (const user of [kiwi, anonymous]) {
|
|
const resp = await axios.put(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
[
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
],
|
|
user
|
|
);
|
|
assert.equal(resp.status, 403);
|
|
assert.deepEqual(resp.data, { error: "Access denied" });
|
|
}
|
|
|
|
const resp = await axios.put(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
[
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
],
|
|
support
|
|
);
|
|
assert.equal(resp.status, 200);
|
|
});
|
|
});
|
|
|
|
describe("DELETE /api/install/configs/:key", async function () {
|
|
before(async function () {
|
|
await dbManager.connection.transaction(async (manager) => {
|
|
const config = new Config();
|
|
config.key = "audit_log_streaming_destinations";
|
|
config.value = [
|
|
{
|
|
id: "4e9f3c26-d069-43f2-8388-1f0f906c0ca3",
|
|
name: "splunk",
|
|
url: "https://hec.example.com:8088/services/collector/event",
|
|
token: "Splunk B5A79AAD-D822-46CC-80D1-819F80D7BFB0",
|
|
},
|
|
];
|
|
await manager.save(config);
|
|
});
|
|
});
|
|
|
|
after(async function () {
|
|
await dbManager.connection.transaction((manager) =>
|
|
manager.createQueryBuilder().delete().from(Config).execute()
|
|
);
|
|
});
|
|
|
|
it("returns 200 on success", async function () {
|
|
let resp = await axios.delete(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 200);
|
|
assert.equal(resp.data, null);
|
|
|
|
resp = await axios.get(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 404);
|
|
assert.deepEqual(resp.data, {
|
|
error: "config not found",
|
|
});
|
|
});
|
|
|
|
it("returns 400 if key is invalid", async function () {
|
|
const resp = await axios.delete(
|
|
`${homeUrl}/api/install/configs/invalid`,
|
|
chimpy
|
|
);
|
|
assert.equal(resp.status, 400);
|
|
assert.deepEqual(resp.data, {
|
|
error: "Invalid config key",
|
|
details: {
|
|
userError: 'Error: value is not "audit_log_streaming_destinations"',
|
|
},
|
|
});
|
|
});
|
|
|
|
it("returns 403 if user isn't an install admin", async function () {
|
|
for (const user of [kiwi, anonymous]) {
|
|
const resp = await axios.delete(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
user
|
|
);
|
|
assert.equal(resp.status, 403);
|
|
assert.deepEqual(resp.data, { error: "Access denied" });
|
|
}
|
|
});
|
|
|
|
it("returns 404 if key doesn't exist", async function () {
|
|
const resp = await axios.delete(
|
|
`${homeUrl}/api/install/configs/audit_log_streaming_destinations`,
|
|
support
|
|
);
|
|
assert.equal(resp.status, 404);
|
|
assert.deepEqual(resp.data, {
|
|
error: "config not found",
|
|
});
|
|
});
|
|
});
|
|
});
|