feat: 🎸 Add sqlite support for credential stores (#2967)
✅ Closes: https://hashicorp.atlassian.net/browse/ICU-17416
This commit is contained in:
1 parent
2154251c30
commit
9e612cf83d
13 files changed
+90
-11
No files matched your search
@@ -49,10 +49,24 @@ export const modelMapping = {
|
||||
scope_id: 'scope.scope_id',
|
||||
created_time: 'created_time',
|
||||
},
|
||||
'credential-store': {
|
||||
id: 'id',
|
||||
type: 'type',
|
||||
name: 'name',
|
||||
description: 'description',
|
||||
scope_id: 'scope.scope_id',
|
||||
created_time: 'created_time',
|
||||
},
|
||||
};
|
||||
|
||||
// A list of tables that we support searching using FTS5 in SQLite.
|
||||
export const searchTables = new Set(['target', 'alias', 'role', 'user']);
|
||||
export const searchTables = new Set([
|
||||
'target',
|
||||
'alias',
|
||||
'role',
|
||||
'user',
|
||||
'credential-store',
|
||||
]);
|
||||
|
||||
export default class SqliteDbService extends Service {
|
||||
// =attributes
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
import { modelMapping } from 'api/services/sqlite';
|
||||
import { searchTables } from 'api/services/sqlite';
|
||||
import { typeOf } from '@ember/utils';
|
||||
import { underscore } from '@ember/string';
|
||||
|
||||
/**
|
||||
* Takes a POJO representing a filter query and builds a SQL query.
|
||||
@@ -29,9 +30,10 @@ export function generateSQLExpressions(
|
||||
let { search, filters, sort = {} } = query;
|
||||
const conditions = [];
|
||||
const parameters = [];
|
||||
const tableName = underscore(resource);
|
||||
|
||||
addFilterConditions(filters, parameters, conditions);
|
||||
addSearchConditions(search, resource, parameters, conditions);
|
||||
addFilterConditions({ filters, parameters, conditions });
|
||||
addSearchConditions({ search, resource, tableName, parameters, conditions });
|
||||
|
||||
const orderByClause = constructOrderByClause(resource, sort);
|
||||
|
||||
@@ -42,7 +44,7 @@ export function generateSQLExpressions(
|
||||
parameters.push(pageSize, (page - 1) * pageSize);
|
||||
}
|
||||
|
||||
const selectClause = `SELECT ${select ? select.join(', ') : '*'} FROM "${resource}"`;
|
||||
const selectClause = `SELECT ${select ? select.join(', ') : '*'} FROM "${tableName}"`;
|
||||
|
||||
return {
|
||||
// Replace any empty newlines or leading whitespace on each line to be consistent with formatting
|
||||
@@ -58,7 +60,7 @@ export function generateSQLExpressions(
|
||||
};
|
||||
}
|
||||
|
||||
function addFilterConditions(filters, parameters, conditions) {
|
||||
function addFilterConditions({ filters, parameters, conditions }) {
|
||||
if (!filters) {
|
||||
return;
|
||||
}
|
||||
@@ -105,7 +107,13 @@ function addFilterConditions(filters, parameters, conditions) {
|
||||
}
|
||||
}
|
||||
|
||||
function addSearchConditions(search, resource, parameters, conditions) {
|
||||
function addSearchConditions({
|
||||
search,
|
||||
resource,
|
||||
tableName,
|
||||
parameters,
|
||||
conditions,
|
||||
}) {
|
||||
if (!search) {
|
||||
return;
|
||||
}
|
||||
@@ -117,7 +125,7 @@ function addSearchConditions(search, resource, parameters, conditions) {
|
||||
// much more efficient with FTS queries when using rowids or MATCH (or both).
|
||||
// We could have also used a join here but a subquery is simpler.
|
||||
conditions.push(
|
||||
`rowid IN (SELECT rowid FROM ${resource}_fts WHERE ${resource}_fts MATCH ?)`,
|
||||
`rowid IN (SELECT rowid FROM ${tableName}_fts WHERE ${tableName}_fts MATCH ?)`,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
* SPDX-License-Identifier: BUSL-1.1
|
||||
*/
|
||||
|
||||
import { underscore } from '@ember/string';
|
||||
|
||||
export const CLEAR_DB = `
|
||||
PRAGMA writable_schema = 1;
|
||||
DELETE FROM sqlite_master;
|
||||
@@ -158,6 +160,41 @@ CREATE TRIGGER IF NOT EXISTS user_ad AFTER DELETE ON user BEGIN
|
||||
VALUES('delete', old.rowid, old.id, old.name, old.description, old.scope_id, old.created_time);
|
||||
END;`;
|
||||
|
||||
const createCredentialStoreTables = `
|
||||
CREATE TABLE IF NOT EXISTS credential_store (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
type TEXT NOT NULL,
|
||||
name TEXT,
|
||||
description TEXT,
|
||||
scope_id TEXT NOT NULL,
|
||||
created_time TEXT NOT NULL,
|
||||
data TEXT NOT NULL
|
||||
);
|
||||
CREATE INDEX IF NOT EXISTS idx_credential_store_scope_id_created_time ON credential_store(scope_id, created_time DESC);
|
||||
|
||||
CREATE VIRTUAL TABLE IF NOT EXISTS credential_store_fts USING fts5(
|
||||
id,
|
||||
type,
|
||||
name,
|
||||
description,
|
||||
scope_id,
|
||||
created_time,
|
||||
content='',
|
||||
);
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS credential_store_ai AFTER INSERT ON credential_store BEGIN
|
||||
INSERT INTO credential_store_fts(
|
||||
id, type, name, description, scope_id, created_time
|
||||
) VALUES (
|
||||
new.id, new.type, new.name, new.description, new.scope_id, new.created_time
|
||||
);
|
||||
END;
|
||||
|
||||
CREATE TRIGGER IF NOT EXISTS credential_store_ad AFTER DELETE ON credential_store BEGIN
|
||||
INSERT INTO credential_store_fts(credential_store_fts, rowid, id, type, name, description, scope_id, created_time)
|
||||
VALUES('delete', old.rowid, old.id, old.type, old.name, old.description, old.scope_id, old.created_time);
|
||||
END;`;
|
||||
|
||||
export const CREATE_TABLES = (version) => `
|
||||
BEGIN;
|
||||
|
||||
@@ -172,6 +209,7 @@ ${createTargetTables}
|
||||
${createAliasTables}
|
||||
${createRoleTables}
|
||||
${createUserTables}
|
||||
${createCredentialStoreTables}
|
||||
|
||||
CREATE TABLE IF NOT EXISTS "group" (
|
||||
id TEXT NOT NULL PRIMARY KEY,
|
||||
@@ -199,8 +237,8 @@ export const INSERT_STATEMENTS = (resource, items, modelMapping) => {
|
||||
const numColumns = Array(columns.length + 1);
|
||||
const placeholders = `(${numColumns.fill('?').join(', ')})`;
|
||||
|
||||
return `REPLACE INTO "${resource}" (${columns.join(', ')}, data) VALUES ${items.map(() => placeholders).join(', ')};`;
|
||||
return `REPLACE INTO "${underscore(resource)}" (${columns.join(', ')}, data) VALUES ${items.map(() => placeholders).join(', ')};`;
|
||||
};
|
||||
|
||||
export const DELETE_STATEMENT = (resource, ids) =>
|
||||
`DELETE FROM "${resource}" WHERE id IN (${ids.map(() => '?').join(',')})`;
|
||||
`DELETE FROM "${underscore(resource)}" WHERE id IN (${ids.map(() => '?').join(',')})`;
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { setupTest } from 'dummy/tests/helpers';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { modelMapping, searchTables } from 'api/services/sqlite';
|
||||
import { underscore } from '@ember/string';
|
||||
|
||||
const supportedModels = Object.keys(modelMapping);
|
||||
const supportedFtsTables = [...searchTables];
|
||||
@@ -22,7 +23,7 @@ module('Unit | Service | sqlite', function (hooks) {
|
||||
const service = this.owner.lookup('service:sqlite');
|
||||
|
||||
const tableColumns = await service.fetchResource({
|
||||
sql: `SELECT * FROM pragma_table_info('${resource}')`,
|
||||
sql: `SELECT * FROM pragma_table_info('${underscore(resource)}')`,
|
||||
});
|
||||
|
||||
const columnNames = tableColumns.map((column) => column.name);
|
||||
@@ -42,7 +43,7 @@ module('Unit | Service | sqlite', function (hooks) {
|
||||
const service = this.owner.lookup('service:sqlite');
|
||||
|
||||
const tableColumns = await service.fetchResource({
|
||||
sql: `SELECT * FROM pragma_table_info('${resource}_fts')`,
|
||||
sql: `SELECT * FROM pragma_table_info('${underscore(resource)}_fts')`,
|
||||
});
|
||||
|
||||
const columnNames = tableColumns.map((column) => column.name);
|
||||
|
||||
@@ -8,6 +8,7 @@ import { visit, currentURL, click, fillIn } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import { Response } from 'miragejs';
|
||||
import * as selectors from './selectors';
|
||||
@@ -17,6 +18,7 @@ import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
module('Acceptance | credential-stores | create', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let featuresService;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { visit, currentURL, click, fillIn } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import { Response } from 'miragejs';
|
||||
import * as selectors from './selectors';
|
||||
@@ -19,6 +20,7 @@ module(
|
||||
function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let getCredentialsCount;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { visit, click } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import { Response } from 'miragejs';
|
||||
import { resolve, reject } from 'rsvp';
|
||||
@@ -19,6 +20,7 @@ import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
module('Acceptance | credential-stores | delete', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
let getStaticCredentialStoresCount;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { visit, currentURL, click, fillIn } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import { Response } from 'miragejs';
|
||||
import * as selectors from './selectors';
|
||||
@@ -17,6 +18,7 @@ import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
module('Acceptance | credential-stores | update', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
|
||||
const instances = {
|
||||
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { visit, click, currentURL } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { Response } from 'miragejs';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import * as commonSelectors from 'admin/tests/helpers/selectors';
|
||||
@@ -18,6 +19,7 @@ import { setRunOptions } from 'ember-a11y-testing/test-support';
|
||||
module('Acceptance | targets | brokered credential sources', function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
|
||||
let getCredentialLibraryCount;
|
||||
let getCredentialCount;
|
||||
|
||||
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { visit, click, currentURL } from '@ember/test-helpers';
|
||||
import { setupApplicationTest } from 'admin/tests/helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { Response } from 'miragejs';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import { TYPE_TARGET_SSH } from 'api/models/target';
|
||||
@@ -21,6 +22,7 @@ module(
|
||||
function (hooks) {
|
||||
setupApplicationTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
|
||||
let getCredentialLibraryCount;
|
||||
let getCredentialCount;
|
||||
|
||||
@@ -8,6 +8,7 @@ import { setupTest } from 'ember-qunit';
|
||||
import { setupIntl } from 'ember-intl/test-support';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupIndexedDb } from 'api/test-support/helpers/indexed-db';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { waitUntil, visit } from '@ember/test-helpers';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
import {
|
||||
@@ -20,6 +21,7 @@ module(
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
setupIndexedDb(hooks);
|
||||
setupIntl(hooks, 'en-us');
|
||||
|
||||
|
||||
+2
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { currentURL, visit } from '@ember/test-helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
|
||||
module(
|
||||
@@ -14,6 +15,7 @@ module(
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
|
||||
let store;
|
||||
let controller;
|
||||
|
||||
+2
@@ -7,6 +7,7 @@ import { module, test } from 'qunit';
|
||||
import { setupTest } from 'ember-qunit';
|
||||
import { currentURL, visit } from '@ember/test-helpers';
|
||||
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
|
||||
import { setupSqlite } from 'api/test-support/helpers/sqlite';
|
||||
import { authenticateSession } from 'ember-simple-auth/test-support';
|
||||
|
||||
module(
|
||||
@@ -14,6 +15,7 @@ module(
|
||||
function (hooks) {
|
||||
setupTest(hooks);
|
||||
setupMirage(hooks);
|
||||
setupSqlite(hooks);
|
||||
|
||||
let store;
|
||||
let controller;
|
||||
|
||||
Reference in new issue
Block a user