login-credentials without RBAC (#173)
Co-authored-by: Michael Hoennig <michael@hoennig.de> Reviewed-on: https://dev.hostsharing.net/hostsharing/hs.hsadmin.ng/pulls/173 Reviewed-by: Marc Sandlus <marc.sandlus@hostsharing.net>
This commit is contained in:
@@ -91,13 +91,16 @@ end; $$;
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-global-historization-tx-create-historicization endDelimiter:--//
|
||||
--changeset michael.hoennig:hs-global-historization-tx-create-historicization runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
|
||||
create or replace procedure base.tx_create_historicization(baseTable varchar)
|
||||
create or replace procedure base.tx_create_historicization(
|
||||
basetable varchar -- format 'schemaname.tablename'
|
||||
)
|
||||
language plpgsql as $$
|
||||
declare
|
||||
baseSchemaName varchar;
|
||||
baseTableName varchar;
|
||||
createHistTableSql varchar;
|
||||
createTriggerSQL varchar;
|
||||
viewName varchar;
|
||||
@@ -106,14 +109,19 @@ declare
|
||||
baseCols varchar;
|
||||
begin
|
||||
|
||||
-- determine schema and pure table name
|
||||
SELECT split_part(basetable, '.', 1),
|
||||
split_part(basetable, '.', 2)
|
||||
INTO baseSchemaName, baseTableName;
|
||||
|
||||
-- create the history table
|
||||
createHistTableSql = '' ||
|
||||
'CREATE TABLE ' || baseTable || '_ex (' ||
|
||||
'CREATE TABLE ' || basetable || '_ex (' ||
|
||||
' version_id serial PRIMARY KEY,' ||
|
||||
' txid xid8 NOT NULL REFERENCES base.tx_context(txid),' ||
|
||||
' trigger_op base.tx_operation NOT NULL,' ||
|
||||
' alive boolean not null,' ||
|
||||
' LIKE ' || baseTable ||
|
||||
' LIKE ' || basetable ||
|
||||
' EXCLUDING CONSTRAINTS' ||
|
||||
' EXCLUDING STATISTICS' ||
|
||||
')';
|
||||
@@ -121,12 +129,12 @@ begin
|
||||
execute createHistTableSql;
|
||||
|
||||
-- create the historical view
|
||||
viewName = baseTable || '_hv';
|
||||
exVersionsTable = baseTable || '_ex';
|
||||
viewName = basetable || '_hv';
|
||||
exVersionsTable = basetable || '_ex';
|
||||
baseCols = (select string_agg(quote_ident(column_name), ', ')
|
||||
from information_schema.columns
|
||||
where table_schema = 'public'
|
||||
and table_name = baseTable);
|
||||
where table_schema = baseSchemaName
|
||||
and table_name = baseTableName);
|
||||
|
||||
createViewSQL = format(
|
||||
'CREATE OR REPLACE VIEW %1$s AS' ||
|
||||
@@ -152,7 +160,7 @@ begin
|
||||
|
||||
-- "-9-" to put the trigger execution after any alphabetically lesser tx-triggers
|
||||
createTriggerSQL = 'CREATE TRIGGER tx_9_historicize_tg' ||
|
||||
' AFTER INSERT OR DELETE OR UPDATE ON ' || baseTable ||
|
||||
' AFTER INSERT OR DELETE OR UPDATE ON ' || basetable ||
|
||||
' FOR EACH ROW EXECUTE PROCEDURE base.tx_historicize_tf()';
|
||||
execute createTriggerSQL;
|
||||
|
||||
|
@@ -0,0 +1,25 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:base-array-functions-WITHOUT-NULL-VALUES endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE OR REPLACE FUNCTION base.without_null_values(arr anyarray)
|
||||
RETURNS anyarray
|
||||
AS $$
|
||||
SELECT array_agg(e) FROM unnest(arr) AS e WHERE e IS NOT NULL
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:base-array-functions-ADD-IF-NOT-NULLCREATE OR REPLACE FUNCTION add_if_not_null(anyarray, anyelement)
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
CREATE OR REPLACE FUNCTION base.add_if_not_null(arr anyarray, val anyelement)
|
||||
RETURNS anyarray
|
||||
AS $$
|
||||
SELECT CASE WHEN val IS NULL THEN arr ELSE arr || val END
|
||||
$$ LANGUAGE sql;
|
||||
|
||||
|
@@ -536,7 +536,7 @@ $$;
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:rbac-base-GRANTS endDelimiter:--//
|
||||
--changeset michael.hoennig:rbac-base-GRANTS endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
Table to store grants / role- or permission assignments to subjects or roles.
|
||||
|
@@ -2,7 +2,7 @@
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:rbac-context-DETERMINE endDelimiter:--//
|
||||
--changeset michael.hoennig:rbac-context-DETERMINE runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create or replace function rbac.determineCurrentSubjectUuid(currentSubject varchar)
|
||||
|
@@ -148,12 +148,12 @@ commit;
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:rbac-global-GUEST-ROLE endDelimiter:--//
|
||||
--changeset michael.hoennig:rbac-global-GUEST-ROLE runOnChange:true validCheckSum:ANY endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
/*
|
||||
A rbac.Global guest role.
|
||||
*/
|
||||
create or replace function rbac.globalglobalGuest(assumed boolean = true)
|
||||
create or replace function rbac.global_GUEST(assumed boolean = true)
|
||||
returns rbac.RoleDescriptor
|
||||
returns null on null input
|
||||
stable -- leakproof
|
||||
@@ -161,10 +161,17 @@ create or replace function rbac.globalglobalGuest(assumed boolean = true)
|
||||
select 'rbac.global', (select uuid from rbac.object where objectTable = 'rbac.global'), 'GUEST'::rbac.RoleType, assumed;
|
||||
$$;
|
||||
|
||||
begin transaction;
|
||||
call base.defineContext('creating role:rbac.global#global:guest', null, null, null);
|
||||
select rbac.createRole(rbac.globalglobalGuest());
|
||||
commit;
|
||||
do language plpgsql $$
|
||||
begin
|
||||
call base.defineContext('creating role:rbac.global#global:guest', null, null, null);
|
||||
begin
|
||||
perform rbac.createRole(rbac.global_GUEST());
|
||||
exception
|
||||
when unique_violation then
|
||||
null; -- ignore if it already exists from prev execution of this changeset
|
||||
end;
|
||||
end;
|
||||
$$;
|
||||
--//
|
||||
|
||||
|
||||
|
@@ -0,0 +1,19 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
-- In a separate file to avoid changed checksums in the existing changsets.
|
||||
-- I presume it's a bug in Liquibase that other changeset checksums are changed by new changesets in the same file
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-office-person-TEST-DATA-GENERATION-FOR-CREDENTIALS context:!without-test-data endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
do language plpgsql $$
|
||||
begin
|
||||
call hs_office.person_create_test_data('NP', null,'Hostmaster', 'Alex');
|
||||
call hs_office.person_create_test_data('NP', null, 'Hostmaster', 'Fran');
|
||||
call hs_office.person_create_test_data('NP', null, 'User', 'Drew');
|
||||
call hs_office.person_create_test_data('NP', null, 'User', 'Test');
|
||||
end;
|
||||
$$;
|
||||
--//
|
||||
|
@@ -0,0 +1,8 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-credentials-SCHEMA endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE SCHEMA hs_credentials;
|
||||
--//
|
@@ -0,0 +1,91 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-credentials-CREDENTIALS-TABLE endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create table hs_credentials.credentials
|
||||
(
|
||||
uuid uuid PRIMARY KEY references rbac.subject (uuid) initially deferred,
|
||||
version int not null default 0,
|
||||
|
||||
person_uuid uuid not null references hs_office.person(uuid),
|
||||
|
||||
active bool,
|
||||
global_uid int unique, -- w/o
|
||||
global_gid int unique, -- w/o
|
||||
onboarding_token text, -- w/o
|
||||
|
||||
two_factor_auth text,
|
||||
phone_password text,
|
||||
email_address text,
|
||||
sms_number text
|
||||
);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-credentials-context-CONTEXT-TABLE endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create table hs_credentials.context
|
||||
(
|
||||
uuid uuid PRIMARY KEY,
|
||||
version int not null default 0,
|
||||
|
||||
type varchar(16),
|
||||
qualifier varchar(80),
|
||||
|
||||
unique (type, qualifier)
|
||||
);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-credentials-CONTEXT-IMMUTABLE-TRIGGER endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE OR REPLACE FUNCTION hs_credentials.prevent_context_update()
|
||||
RETURNS TRIGGER AS $$
|
||||
BEGIN
|
||||
RAISE EXCEPTION 'Updates to hs_credentials.context are not allowed.';
|
||||
END;
|
||||
$$ LANGUAGE plpgsql;
|
||||
|
||||
-- Trigger to enforce immutability
|
||||
CREATE TRIGGER context_immutable_trigger
|
||||
BEFORE UPDATE ON hs_credentials.context
|
||||
FOR EACH ROW EXECUTE FUNCTION hs_credentials.prevent_context_update();
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs_credentials-CONTEXT-MAPPING endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
create table hs_credentials.context_mapping
|
||||
(
|
||||
uuid uuid PRIMARY KEY DEFAULT uuid_generate_v4(),
|
||||
credentials_uuid uuid references hs_credentials.credentials(uuid) ON DELETE CASCADE,
|
||||
context_uuid uuid references hs_credentials.context(uuid) ON DELETE RESTRICT
|
||||
);
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs-hs_credentials-JOURNALS endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
call base.create_journal('hs_credentials.context_mapping');
|
||||
call base.create_journal('hs_credentials.context');
|
||||
call base.create_journal('hs_credentials.credentials');
|
||||
--//
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs_credentials-HISTORICIZATION endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
call base.tx_create_historicization('hs_credentials.context_mapping');
|
||||
call base.tx_create_historicization('hs_credentials.context');
|
||||
call base.tx_create_historicization('hs_credentials.credentials');
|
||||
--//
|
@@ -0,0 +1,41 @@
|
||||
### rbac credentialsContext
|
||||
|
||||
This code generated was by RbacViewMermaidFlowchartGenerator, do not amend manually.
|
||||
|
||||
```mermaid
|
||||
%%{init:{'flowchart':{'htmlLabels':false}}}%%
|
||||
flowchart TB
|
||||
|
||||
subgraph credentialsContext["`**credentialsContext**`"]
|
||||
direction TB
|
||||
style credentialsContext fill:#dd4901,stroke:#274d6e,stroke-width:8px
|
||||
|
||||
subgraph credentialsContext:roles[ ]
|
||||
style credentialsContext:roles fill:#dd4901,stroke:white
|
||||
|
||||
role:credentialsContext:OWNER[[credentialsContext:OWNER]]
|
||||
role:credentialsContext:ADMIN[[credentialsContext:ADMIN]]
|
||||
role:credentialsContext:REFERRER[[credentialsContext:REFERRER]]
|
||||
end
|
||||
|
||||
subgraph credentialsContext:permissions[ ]
|
||||
style credentialsContext:permissions fill:#dd4901,stroke:white
|
||||
|
||||
perm:credentialsContext:INSERT{{credentialsContext:INSERT}}
|
||||
perm:credentialsContext:UPDATE{{credentialsContext:UPDATE}}
|
||||
perm:credentialsContext:DELETE{{credentialsContext:DELETE}}
|
||||
perm:credentialsContext:SELECT{{credentialsContext:SELECT}}
|
||||
end
|
||||
end
|
||||
|
||||
%% granting roles to roles
|
||||
role:credentialsContext:OWNER ==> role:credentialsContext:ADMIN
|
||||
role:credentialsContext:ADMIN ==> role:credentialsContext:REFERRER
|
||||
|
||||
%% granting permissions to roles
|
||||
role:rbac.global:ADMIN ==> perm:credentialsContext:INSERT
|
||||
role:rbac.global:ADMIN ==> perm:credentialsContext:UPDATE
|
||||
role:rbac.global:ADMIN ==> perm:credentialsContext:DELETE
|
||||
role:rbac.global:REFERRER ==> perm:credentialsContext:SELECT
|
||||
|
||||
```
|
@@ -0,0 +1,68 @@
|
||||
--liquibase formatted sql
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
--changeset michael.hoennig:hs_credentials-credentials-TEST-DATA context:!without-test-data endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
|
||||
do language plpgsql $$
|
||||
|
||||
declare
|
||||
superuserAlexSubjectUuid uuid;
|
||||
personAlexUuid uuid;
|
||||
superuserFranSubjectUuid uuid;
|
||||
personFranUuid uuid;
|
||||
|
||||
context_HSADMIN_prod hs_credentials.context;
|
||||
context_SSH_internal hs_credentials.context;
|
||||
context_MATRIX_internal hs_credentials.context;
|
||||
|
||||
begin
|
||||
call base.defineContext('creating booking-project test-data', null, 'superuser-alex@hostsharing.net', 'rbac.global#global:ADMIN');
|
||||
|
||||
superuserAlexSubjectUuid = (SELECT uuid FROM rbac.subject WHERE name='superuser-alex@hostsharing.net');
|
||||
personAlexUuid = (SELECT uuid FROM hs_office.person WHERE givenName='Alex');
|
||||
superuserFranSubjectUuid = (SELECT uuid FROM rbac.subject WHERE name='superuser-fran@hostsharing.net');
|
||||
personFranUuid = (SELECT uuid FROM hs_office.person WHERE givenName='Fran');
|
||||
|
||||
-- Add test contexts
|
||||
INSERT INTO hs_credentials.context (uuid, type, qualifier) VALUES
|
||||
('11111111-1111-1111-1111-111111111111', 'HSADMIN', 'prod')
|
||||
RETURNING * INTO context_HSADMIN_prod;
|
||||
INSERT INTO hs_credentials.context (uuid, type, qualifier) VALUES
|
||||
('22222222-2222-2222-2222-222222222222', 'SSH', 'internal')
|
||||
RETURNING * INTO context_SSH_internal;
|
||||
INSERT INTO hs_credentials.context (uuid, type, qualifier) VALUES
|
||||
('33333333-3333-3333-3333-333333333333', 'MATRIX', 'internal')
|
||||
RETURNING * INTO context_MATRIX_internal;
|
||||
|
||||
-- grant general access to public credential contexts
|
||||
-- TODO_impl: RBAC rules for _rv do not yet work properly
|
||||
-- call rbac.grantPermissiontoRole(
|
||||
-- rbac.createPermission(context_HSADMIN_prod.uuid, 'SELECT'),
|
||||
-- rbac.global_GUEST());
|
||||
-- call rbac.grantPermissiontoRole(
|
||||
-- rbac.createPermission(context_SSH_internal.uuid, 'SELECT'),
|
||||
-- rbac.global_ADMIN());
|
||||
-- call rbac.grantPermissionToRole(
|
||||
-- rbac.createPermission(context_MATRIX_internal.uuid, 'SELECT'),
|
||||
-- rbac.global_ADMIN());
|
||||
-- call rbac.grantRoleToRole(hs_credentials.context_REFERRER(context_SSH_internal), rbac.global_ADMIN());
|
||||
-- call rbac.grantRoleToRole(hs_credentials.context_REFERRER(context_MATRIX_internal), rbac.global_ADMIN());
|
||||
|
||||
-- Add test credentials (linking to assumed rbac.subject UUIDs)
|
||||
INSERT INTO hs_credentials.credentials (uuid, version, person_uuid, active, global_uid, global_gid, onboarding_token, two_factor_auth, phone_password, email_address, sms_number) VALUES
|
||||
( superuserAlexSubjectUuid, 0, personAlexUuid, true, 1001, 1001, 'token-abc', 'otp-secret-1', 'phone-pw-1', 'alex@example.com', '111-222-3333'),
|
||||
( superuserFranSubjectUuid, 0, personFranUuid, true, 1002, 1002, 'token-def', 'otp-secret-2', 'phone-pw-2', 'fran@example.com', '444-555-6666');
|
||||
|
||||
-- Map credentials to contexts
|
||||
INSERT INTO hs_credentials.context_mapping (credentials_uuid, context_uuid) VALUES
|
||||
(superuserAlexSubjectUuid, '11111111-1111-1111-1111-111111111111'), -- HSADMIN context
|
||||
(superuserFranSubjectUuid, '11111111-1111-1111-1111-111111111111'), -- HSADMIN context
|
||||
(superuserAlexSubjectUuid, '22222222-2222-2222-2222-222222222222'), -- SSH context
|
||||
(superuserFranSubjectUuid, '22222222-2222-2222-2222-222222222222'), -- SSH context
|
||||
(superuserAlexSubjectUuid, '33333333-3333-3333-3333-333333333333'), -- MATRIX context
|
||||
(superuserFranSubjectUuid, '33333333-3333-3333-3333-333333333333'); -- MATRIX context
|
||||
|
||||
end; $$;
|
||||
--//
|
@@ -4,5 +4,5 @@
|
||||
-- ============================================================================
|
||||
--changeset timotheus.pokorra:hs-integration-SCHEMA endDelimiter:--//
|
||||
-- ----------------------------------------------------------------------------
|
||||
CREATE SCHEMA hs_integration;
|
||||
CREATE SCHEMA IF NOT EXISTS hs_integration;
|
||||
--//
|
@@ -27,6 +27,8 @@ databaseChangeLog:
|
||||
file: db/changelog/0-base/020-audit-log.sql
|
||||
- include:
|
||||
file: db/changelog/0-base/030-historization.sql
|
||||
- include:
|
||||
file: db/changelog/0-base/040-array-functions.sql
|
||||
- include:
|
||||
file: db/changelog/0-base/090-log-slow-queries-extensions.sql
|
||||
|
||||
@@ -100,6 +102,9 @@ databaseChangeLog:
|
||||
- include:
|
||||
file: db/changelog/5-hs-office/502-person/5028-hs-office-person-test-data.sql
|
||||
context: "!without-test-data"
|
||||
- include:
|
||||
file: db/changelog/5-hs-office/502-person/5028-hs-office-person-test-data-for-credentials.sql
|
||||
context: "!without-test-data"
|
||||
- include:
|
||||
file: db/changelog/5-hs-office/503-relation/5030-hs-office-relation.sql
|
||||
- include:
|
||||
@@ -212,19 +217,30 @@ databaseChangeLog:
|
||||
file: db/changelog/9-hs-global/9000-statistics.sql
|
||||
context: "!only-office"
|
||||
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/950-credentials/9500-hs-credentials-schema.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/950-credentials/9510-hs-credentials.sql
|
||||
# TODO_impl: RBAC rules for _rv do not yet work properly
|
||||
# - include:
|
||||
# file: db/changelog/9-hs-global/950-credentials/9513-hs-credentials-rbac.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/950-credentials/9519-hs-credentials-test-data.sql
|
||||
context: "!without-test-data"
|
||||
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/960-integrations/9600-hs-integration-schema.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/960-integrations/9610-integration-kimai.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/960-integrations/9620-integration-znuny.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/960-integrations/9630-integration-mlmmj.sql
|
||||
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9800-cleanup.sql
|
||||
context: "without-test-data"
|
||||
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9100-hs-integration-schema.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9110-integration-kimai.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9120-integration-znuny.sql
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9130-integration-mlmmj.sql
|
||||
|
||||
- include:
|
||||
file: db/changelog/9-hs-global/9999-liquibase-migration-test.sql
|
||||
context: liquibase-migration-test
|
||||
|
Reference in New Issue
Block a user