oidc schema changes. (#874)
This commit is contained in:
13 files changed
+1101
-2
No files matched your search
@@ -669,3 +669,234 @@ set id = null;
|
||||
assert.Equal(orig.CreateTime, found.CreateTime)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDomain_wt_url(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table if not exists test_table_wt_url (
|
||||
id bigint generated always as identity primary key,
|
||||
url wt_url
|
||||
);
|
||||
`
|
||||
insert = `
|
||||
insert into test_table_wt_url(url)
|
||||
values ($1)
|
||||
returning id;
|
||||
`
|
||||
)
|
||||
|
||||
conn, _ := TestSetup(t, "postgres")
|
||||
db := conn.DB()
|
||||
|
||||
if _, err := db.Exec(createTable); err != nil {
|
||||
t.Fatalf("query: \n%s\n error: %s", createTable, err)
|
||||
}
|
||||
|
||||
failTests := []struct {
|
||||
value string
|
||||
exception string
|
||||
}{
|
||||
{"http://", ""},
|
||||
{"htt://", "wt_url_invalid_protocol"},
|
||||
{"ht", "wt_url_invalid_protocol"},
|
||||
{"http://" + strings.Repeat("a", 4001), "wt_url_too_long"},
|
||||
}
|
||||
for _, tt := range failTests {
|
||||
t.Run(tt.value, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
t.Logf("insert value: %q", tt.value)
|
||||
_, err := db.Query(insert, tt.value)
|
||||
if tt.exception != "" {
|
||||
require.Error(err)
|
||||
assert.Containsf(err.Error(), tt.exception, "missing %s from err: %s", tt.exception, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomain_wt_email(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table if not exists test_table_wt_email (
|
||||
id bigint generated always as identity primary key,
|
||||
email wt_email
|
||||
);
|
||||
`
|
||||
insert = `
|
||||
insert into test_table_wt_email(email)
|
||||
values ($1)
|
||||
returning id;
|
||||
`
|
||||
)
|
||||
|
||||
conn, _ := TestSetup(t, "postgres")
|
||||
db := conn.DB()
|
||||
|
||||
if _, err := db.Exec(createTable); err != nil {
|
||||
t.Fatalf("query: \n%s\n error: %s", createTable, err)
|
||||
}
|
||||
|
||||
failTests := []struct {
|
||||
value string
|
||||
exception string
|
||||
}{
|
||||
{" ", "wt_email_too_short"},
|
||||
{"1" + strings.Repeat("1", 320), "wt_email_too_long"},
|
||||
{"alice@bob.com", ""},
|
||||
}
|
||||
for _, tt := range failTests {
|
||||
t.Run(tt.value, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
t.Logf("insert value: %q", tt.value)
|
||||
_, err := db.Query(insert, tt.value)
|
||||
if tt.exception != "" {
|
||||
require.Error(err)
|
||||
assert.Containsf(err.Error(), tt.exception, "missing %s from err: %s", tt.exception, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomain_wt_full_name(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table if not exists test_table_wt_full_name (
|
||||
id bigint generated always as identity primary key,
|
||||
full_name wt_full_name
|
||||
);
|
||||
`
|
||||
insert = `
|
||||
insert into test_table_wt_full_name(full_name)
|
||||
values ($1)
|
||||
returning id;
|
||||
`
|
||||
)
|
||||
|
||||
conn, _ := TestSetup(t, "postgres")
|
||||
db := conn.DB()
|
||||
|
||||
if _, err := db.Exec(createTable); err != nil {
|
||||
t.Fatalf("query: \n%s\n error: %s", createTable, err)
|
||||
}
|
||||
|
||||
failTests := []struct {
|
||||
value string
|
||||
exception string
|
||||
}{
|
||||
{" ", "wt_full_name_too_short"},
|
||||
{"1" + strings.Repeat("1", 512), "wt_full_name_too_long"},
|
||||
{"alice and bob's dinner", ""},
|
||||
}
|
||||
for _, tt := range failTests {
|
||||
t.Run(tt.value, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
t.Logf("insert value: %q", tt.value)
|
||||
_, err := db.Query(insert, tt.value)
|
||||
if tt.exception != "" {
|
||||
require.Error(err)
|
||||
assert.Containsf(err.Error(), tt.exception, "missing %s from err: %s", tt.exception, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomain_wt_name(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table if not exists test_table_wt_name (
|
||||
id bigint generated always as identity primary key,
|
||||
name wt_name
|
||||
);
|
||||
`
|
||||
insert = `
|
||||
insert into test_table_wt_name(name)
|
||||
values ($1)
|
||||
returning id;
|
||||
`
|
||||
)
|
||||
|
||||
conn, _ := TestSetup(t, "postgres")
|
||||
db := conn.DB()
|
||||
|
||||
if _, err := db.Exec(createTable); err != nil {
|
||||
t.Fatalf("query: \n%s\n error: %s", createTable, err)
|
||||
}
|
||||
|
||||
failTests := []struct {
|
||||
value string
|
||||
exception string
|
||||
}{
|
||||
{" ", "wt_name_too_short"},
|
||||
{"1" + strings.Repeat("1", 512), "wt_name_too_long"},
|
||||
{"alice and bob's dinner", ""},
|
||||
}
|
||||
for _, tt := range failTests {
|
||||
t.Run(tt.value, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
t.Logf("insert value: %q", tt.value)
|
||||
_, err := db.Query(insert, tt.value)
|
||||
if tt.exception != "" {
|
||||
require.Error(err)
|
||||
assert.Containsf(err.Error(), tt.exception, "missing %s from err: %s", tt.exception, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestDomain_wt_description(t *testing.T) {
|
||||
const (
|
||||
createTable = `
|
||||
create table if not exists test_table_wt_description (
|
||||
id bigint generated always as identity primary key,
|
||||
description wt_description
|
||||
);
|
||||
`
|
||||
insert = `
|
||||
insert into test_table_wt_description(description)
|
||||
values ($1)
|
||||
returning id;
|
||||
`
|
||||
)
|
||||
|
||||
conn, _ := TestSetup(t, "postgres")
|
||||
db := conn.DB()
|
||||
|
||||
if _, err := db.Exec(createTable); err != nil {
|
||||
t.Fatalf("query: \n%s\n error: %s", createTable, err)
|
||||
}
|
||||
|
||||
failTests := []struct {
|
||||
value string
|
||||
exception string
|
||||
}{
|
||||
{" ", "wt_description_too_short"},
|
||||
{"1" + strings.Repeat("1", 1024), "wt_description_too_long"},
|
||||
{"alice and bob's dinner has delicious pancakes", ""},
|
||||
}
|
||||
for _, tt := range failTests {
|
||||
t.Run(tt.value, func(t *testing.T) {
|
||||
assert, require := assert.New(t), require.New(t)
|
||||
|
||||
t.Logf("insert value: %q", tt.value)
|
||||
_, err := db.Query(insert, tt.value)
|
||||
if tt.exception != "" {
|
||||
require.Error(err)
|
||||
assert.Containsf(err.Error(), tt.exception, "missing %s from err: %s", tt.exception, err.Error())
|
||||
return
|
||||
}
|
||||
require.NoError(err)
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
begin;
|
||||
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,58 @@
|
||||
begin;
|
||||
|
||||
-- wt_email defines a type for email which must be less than 320 chars and only
|
||||
-- contain lower case values. The type is defined to allow nulls and not be
|
||||
-- unique, which can be overriden as needed when used in tables.
|
||||
create domain wt_email as text
|
||||
constraint wt_email_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_email_too_long
|
||||
check (length(trim(value)) < 320);
|
||||
comment on domain wt_email is
|
||||
'standard column for email addresses';
|
||||
|
||||
-- wt_full_name defines a type for a person's full name which must be less than
|
||||
-- 512 chars. The type is defined to allow nulls and not be unique, which can
|
||||
-- be overriden as needed when used in tables.
|
||||
create domain wt_full_name text
|
||||
constraint wt_full_name_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_full_name_too_long
|
||||
check(length(trim(value)) <= 512); -- gotta pick some upper limit.
|
||||
comment on domain wt_full_name is
|
||||
'standard column for the full name of a person';
|
||||
|
||||
-- wt_url defines a type for URLs which must be longer that 3 chars and
|
||||
-- less than 4k chars. It's defined to allow nulls, which can be overriden as
|
||||
-- needed when used in tables.
|
||||
create domain wt_url as text
|
||||
constraint wt_url_too_short
|
||||
check (length(trim(value)) > 3)
|
||||
constraint wt_url_too_long
|
||||
check (length(trim(value)) < 4000)
|
||||
constraint wt_url_invalid_protocol
|
||||
check (value ~ 'https?:\/\/*');
|
||||
comment on domain wt_url is
|
||||
'standard column for URLs';
|
||||
|
||||
-- wt_name defines a type for resource names that must be less than 128 chars.
|
||||
-- It's defined to allow nulls.
|
||||
create domain wt_name as text
|
||||
constraint wt_name_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_name_too_long
|
||||
check (length(trim(value)) < 128);
|
||||
comment on domain wt_name is
|
||||
'standard column for resource names';
|
||||
|
||||
-- wt_description defines a type for resource descriptionss that must be less
|
||||
-- than 1024 chars. It's defined to allow nulls.
|
||||
create domain wt_description as text
|
||||
constraint wt_description_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_description_too_long
|
||||
check (length(trim(value)) < 1024);
|
||||
comment on domain wt_description is
|
||||
'standard column for resource descriptions';
|
||||
|
||||
commit;
|
||||
@@ -62,4 +62,4 @@ create trigger
|
||||
before insert on kms_oidc_key_version
|
||||
for each row execute procedure kms_version_column('oidc_key_id');
|
||||
|
||||
commit;
|
||||
commit;
|
||||
@@ -0,0 +1,3 @@
|
||||
begin;
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,71 @@
|
||||
begin;
|
||||
|
||||
-- auth_oidc_method_state_enum entries define the possible oidc auth method
|
||||
-- states.
|
||||
create table auth_oidc_method_state_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_oidc_method_states_allowed
|
||||
check (
|
||||
name in ('inactive', 'active-private', 'active-public', 'stopping')
|
||||
)
|
||||
);
|
||||
|
||||
-- populate the values of auth_oidc_method_state_enm
|
||||
insert into auth_oidc_method_state_enm(name)
|
||||
values
|
||||
('inactive'),
|
||||
('active-private'),
|
||||
('active-public'),
|
||||
('stopping');
|
||||
|
||||
-- define the immutable fields for auth_oidc_method_state_enm (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_method_state_enm
|
||||
for each row execute procedure immutable_columns('name');
|
||||
|
||||
|
||||
-- auth_oidc_signing_alg entries define the supported oidc auth method
|
||||
-- signing algorithms.
|
||||
create table auth_oidc_signing_alg_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_auth_oidc_signing_algs_allowed
|
||||
check (
|
||||
name in (
|
||||
'RS256',
|
||||
'RS384',
|
||||
'RS512',
|
||||
'ES256',
|
||||
'ES384',
|
||||
'ES512',
|
||||
'PS256',
|
||||
'PS384',
|
||||
'PS512',
|
||||
'EdDSA')
|
||||
)
|
||||
);
|
||||
|
||||
-- populate the values of auth_oidc_signing_alg
|
||||
insert into auth_oidc_signing_alg_enm (name)
|
||||
values
|
||||
('RS256'),
|
||||
('RS384'),
|
||||
('RS512'),
|
||||
('ES256'),
|
||||
('ES384'),
|
||||
('ES512'),
|
||||
('PS256'),
|
||||
('PS384'),
|
||||
('PS512'),
|
||||
('EdDSA')
|
||||
;
|
||||
|
||||
-- define the immutable fields for auth_oidc_signing_alg (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_signing_alg_enm
|
||||
for each row execute procedure immutable_columns('name');
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,3 @@
|
||||
begin;
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,193 @@
|
||||
begin;
|
||||
|
||||
-- auth_oidc_method entries are the current oidc auth methods configured for
|
||||
-- existing scopes.
|
||||
create table auth_oidc_method (
|
||||
public_id wt_public_id
|
||||
primary key,
|
||||
scope_id wt_scope_id
|
||||
not null,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
state text not null
|
||||
references auth_oidc_method_state_enm(name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
discovery_url wt_url not null, -- oidc discovery URL without any .well-known component
|
||||
client_id text not null -- oidc client identifier issued by the oidc provider.
|
||||
constraint client_id_not_empty
|
||||
check(length(trim(client_id)) > 0),
|
||||
client_secret bytea not null, -- encrypted oidc client secret issued by the oidc provider.
|
||||
key_id wt_private_id not null -- key used to encrypt entries via wrapping wrapper.
|
||||
references kms_oidc_key_version(private_id)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
max_age int not null -- the allowable elapsed time in secs since the last time the user was authenticated. zero is allowed and should force the user to be re-authenticated.
|
||||
constraint max_age_equal_or_greater_than_zero
|
||||
check(max_age >= 0),
|
||||
foreign key (scope_id, public_id)
|
||||
references auth_method (scope_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
unique(scope_id, name),
|
||||
unique(scope_id, public_id),
|
||||
unique(scope_id, discovery_url, client_id) -- a client_id must be unique for a provider within a scope.
|
||||
);
|
||||
|
||||
-- auth_oidc_signing_alg entries are the signing algorithms allowed for an oidc
|
||||
-- auth method. There must be at least one allowed alg for each oidc auth method.
|
||||
create table auth_oidc_signing_alg (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
signing_alg_name text
|
||||
references auth_oidc_signing_alg_enm(name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
primary key(oidc_method_id, signing_alg_name)
|
||||
);
|
||||
|
||||
-- auth_oidc_callback_url entries are the callback URLs allowed for a specific
|
||||
-- oidc auth method. There must be at least one callback url for each oidc auth
|
||||
-- method.
|
||||
create table auth_oidc_callback_url (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
callback_url wt_url not null
|
||||
);
|
||||
|
||||
-- auth_oidc_aud_claim entries are the audience claims for a specific oidc auth
|
||||
-- method. There can be 0 or more for each parent oidc auth method. If an auth
|
||||
-- method has any aud claims, an ID token must contain one of them to be valid.
|
||||
create table auth_oidc_aud_claim (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
aud_claim text not null
|
||||
constraint aud_claim_must_not_be_empty
|
||||
check(length(trim(aud_claim)) > 0)
|
||||
constraint aud_claim_must_be_less_than_1024_chars
|
||||
check(length(trim(aud_claim)) < 1024),
|
||||
primary key(oidc_method_id, aud_claim)
|
||||
);
|
||||
|
||||
-- auth_oidc_certificate entries are optional PEM encoded x509 certificates.
|
||||
-- Each entry is a single certificate. An oidc auth method may have 0 or more
|
||||
-- of these optional x509s. If an auth method has any cert entries, they are
|
||||
-- used as trust anchors when connecting to the auth method's oidc provider
|
||||
-- (instead of the host system's cert chain).
|
||||
create table auth_oidc_certificate (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
certificate bytea not null,
|
||||
primary key(oidc_method_id, certificate)
|
||||
);
|
||||
|
||||
|
||||
create table auth_oidc_account (
|
||||
public_id wt_public_id
|
||||
primary key,
|
||||
auth_method_id wt_public_id
|
||||
not null,
|
||||
-- NOTE(mgaffney): The scope_id type is not wt_scope_id because the domain
|
||||
-- check is executed before the insert trigger which retrieves the scope_id
|
||||
-- causing an insert to fail.
|
||||
scope_id text not null,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
issuer_id wt_url not null, -- case-sensitive URL that maps to an id_token's iss claim
|
||||
subject_id text not null -- case-senstive string that maps to an id_token's sub claim
|
||||
constraint subject_id_must_not_be_empty
|
||||
check (
|
||||
length(trim(subject_id)) > 0
|
||||
)
|
||||
constraint subject_id_must_be_less_than_256_chars
|
||||
check(
|
||||
length(trim(subject_id)) <= 255 -- length limit per OIDC spec
|
||||
),
|
||||
full_name wt_full_name, -- may be null and maps to an id_token's name claim
|
||||
email wt_email, -- may be null and maps to the id_token's email claim
|
||||
foreign key (scope_id, auth_method_id)
|
||||
references auth_oidc_method (scope_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
foreign key (scope_id, auth_method_id, public_id)
|
||||
references auth_account (scope_id, auth_method_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
unique(auth_method_id, name),
|
||||
unique(auth_method_id, issuer_id, subject_id), -- subject must be unique for a provider within specific auth method
|
||||
unique(auth_method_id, public_id)
|
||||
);
|
||||
|
||||
-- auth_oidc_method column triggers
|
||||
create trigger
|
||||
insert_auth_method_subtype
|
||||
before insert on auth_oidc_method
|
||||
for each row execute procedure insert_auth_method_subtype();
|
||||
|
||||
create trigger
|
||||
update_time_column
|
||||
before
|
||||
update on auth_oidc_method
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_method
|
||||
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on auth_oidc_method
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on auth_oidc_method
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
-- auth_oidc_account column triggers
|
||||
create trigger
|
||||
update_time_column
|
||||
before
|
||||
update on auth_oidc_account
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_account
|
||||
for each row execute procedure immutable_columns('public_id', 'auth_method_id', 'scope_id', 'create_time', 'issuer_id', 'subject_id');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on auth_oidc_account
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on auth_oidc_account
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger
|
||||
insert_auth_account_subtype
|
||||
before insert on auth_oidc_account
|
||||
for each row execute procedure insert_auth_account_subtype();
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,3 @@
|
||||
begin;
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,31 @@
|
||||
begin;
|
||||
|
||||
-- auth_token_status_enm entries define the possible auth token
|
||||
-- states.
|
||||
create table auth_token_status_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_auth_token_states_allowed
|
||||
check (
|
||||
name in ('auth token pending','token issued', 'authentication failed', 'system error')
|
||||
)
|
||||
);
|
||||
|
||||
-- populate the values of auth_token_status_enm
|
||||
insert into auth_token_status_enm(name)
|
||||
values
|
||||
('auth token pending'),
|
||||
('token issued'),
|
||||
('authentication failed'),
|
||||
('system error');
|
||||
|
||||
|
||||
-- add the state column with a default to the auth_token table.
|
||||
alter table auth_token
|
||||
add column status text
|
||||
not null
|
||||
default 'token issued' -- safest default
|
||||
references auth_token_status_enm(name)
|
||||
on update cascade
|
||||
on delete restrict;
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,3 @@
|
||||
begin;
|
||||
|
||||
commit;
|
||||
@@ -0,0 +1,66 @@
|
||||
begin;
|
||||
|
||||
-- add the account_info_auth_method_id which determines which auth_method is
|
||||
-- designated as for "account info" in the user's scope.
|
||||
alter table iam_scope
|
||||
add column account_info_auth_method_id wt_public_id -- allowed to be null and is mutable of course.
|
||||
references auth_method(public_id)
|
||||
on update cascade
|
||||
on delete set null;
|
||||
|
||||
-- establish a compond fk, but there's no cascading of deletes or updates, since
|
||||
-- we only want to cascade changes to the account_info_auth_method_id portion of
|
||||
-- the compond fk and that is handled in a separate fk declaration.
|
||||
alter table iam_scope
|
||||
add foreign key (public_id, account_info_auth_method_id) references auth_method(scope_id, public_id);
|
||||
|
||||
-- iam_user_acct_info provides account info for users by determining which
|
||||
-- auth_method is designated as for "account info" in the user's scope via the
|
||||
-- scope's account_info_auth_method_id. Every sub-type of auth_account must be
|
||||
-- added to this view's union.
|
||||
create view iam_acct_info as
|
||||
select
|
||||
aa.iam_user_id,
|
||||
oa.subject_id as login_name,
|
||||
oa.full_name as full_name,
|
||||
oa.email as email
|
||||
from
|
||||
iam_scope s,
|
||||
auth_account aa,
|
||||
auth_oidc_account oa
|
||||
where
|
||||
aa.public_id = oa.public_id and
|
||||
aa.auth_method_id = s.account_info_auth_method_id
|
||||
union
|
||||
select
|
||||
aa.iam_user_id,
|
||||
pa.login_name,
|
||||
'' as full_name,
|
||||
'' as email
|
||||
from
|
||||
iam_scope s,
|
||||
auth_account aa,
|
||||
auth_password_account pa
|
||||
where
|
||||
aa.public_id = pa.public_id and
|
||||
aa.auth_method_id = s.account_info_auth_method_id;
|
||||
|
||||
-- iam_user_acct_info provides a simple way to retrieve entries that include
|
||||
-- both the iam_user fields with an outer join to the user's account info.
|
||||
create view iam_user_acct_info as
|
||||
select
|
||||
u.public_id,
|
||||
u.scope_id,
|
||||
u.name,
|
||||
u.description,
|
||||
u.create_time,
|
||||
u.update_time,
|
||||
u.version,
|
||||
i.login_name,
|
||||
i.full_name,
|
||||
i.email
|
||||
from
|
||||
iam_user u
|
||||
left outer join iam_acct_info i on u.public_id = i.iam_user_id;
|
||||
|
||||
commit;
|
||||
@@ -5,7 +5,7 @@ package schema
|
||||
func init() {
|
||||
migrationStates["postgres"] = migrationState{
|
||||
devMigration: true,
|
||||
binarySchemaVersion: 1082,
|
||||
binarySchemaVersion: 1086,
|
||||
upMigrations: map[int][]byte{
|
||||
1: []byte(`
|
||||
begin;
|
||||
@@ -4989,6 +4989,67 @@ create table server_tag (
|
||||
);
|
||||
|
||||
commit;
|
||||
`),
|
||||
1080: []byte(`
|
||||
begin;
|
||||
|
||||
-- wt_email defines a type for email which must be less than 320 chars and only
|
||||
-- contain lower case values. The type is defined to allow nulls and not be
|
||||
-- unique, which can be overriden as needed when used in tables.
|
||||
create domain wt_email as text
|
||||
constraint wt_email_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_email_too_long
|
||||
check (length(trim(value)) < 320);
|
||||
comment on domain wt_email is
|
||||
'standard column for email addresses';
|
||||
|
||||
-- wt_full_name defines a type for a person's full name which must be less than
|
||||
-- 512 chars. The type is defined to allow nulls and not be unique, which can
|
||||
-- be overriden as needed when used in tables.
|
||||
create domain wt_full_name text
|
||||
constraint wt_full_name_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_full_name_too_long
|
||||
check(length(trim(value)) <= 512); -- gotta pick some upper limit.
|
||||
comment on domain wt_full_name is
|
||||
'standard column for the full name of a person';
|
||||
|
||||
-- wt_url defines a type for URLs which must be longer that 3 chars and
|
||||
-- less than 4k chars. It's defined to allow nulls, which can be overriden as
|
||||
-- needed when used in tables.
|
||||
create domain wt_url as text
|
||||
constraint wt_url_too_short
|
||||
check (length(trim(value)) > 3)
|
||||
constraint wt_url_too_long
|
||||
check (length(trim(value)) < 4000)
|
||||
constraint wt_url_invalid_protocol
|
||||
check (value ~ 'https?:\/\/*');
|
||||
comment on domain wt_url is
|
||||
'standard column for URLs';
|
||||
|
||||
-- wt_name defines a type for resource names that must be less than 128 chars.
|
||||
-- It's defined to allow nulls.
|
||||
create domain wt_name as text
|
||||
constraint wt_name_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_name_too_long
|
||||
check (length(trim(value)) < 128);
|
||||
comment on domain wt_name is
|
||||
'standard column for resource names';
|
||||
|
||||
-- wt_description defines a type for resource descriptionss that must be less
|
||||
-- than 1024 chars. It's defined to allow nulls.
|
||||
create domain wt_description as text
|
||||
constraint wt_description_too_short
|
||||
check (length(trim(value)) > 0)
|
||||
constraint wt_description_too_long
|
||||
check (length(trim(value)) < 1024);
|
||||
comment on domain wt_description is
|
||||
'standard column for resource descriptions';
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
1082: []byte(`
|
||||
begin;
|
||||
@@ -5055,6 +5116,378 @@ create trigger
|
||||
before insert on kms_oidc_key_version
|
||||
for each row execute procedure kms_version_column('oidc_key_id');
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
1083: []byte(`
|
||||
begin;
|
||||
|
||||
-- auth_oidc_method_state_enum entries define the possible oidc auth method
|
||||
-- states.
|
||||
create table auth_oidc_method_state_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_oidc_method_states_allowed
|
||||
check (
|
||||
name in ('inactive', 'active-private', 'active-public', 'stopping')
|
||||
)
|
||||
);
|
||||
|
||||
-- populate the values of auth_oidc_method_state_enm
|
||||
insert into auth_oidc_method_state_enm(name)
|
||||
values
|
||||
('inactive'),
|
||||
('active-private'),
|
||||
('active-public'),
|
||||
('stopping');
|
||||
|
||||
-- define the immutable fields for auth_oidc_method_state_enm (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_method_state_enm
|
||||
for each row execute procedure immutable_columns('name');
|
||||
|
||||
|
||||
-- auth_oidc_signing_alg entries define the supported oidc auth method
|
||||
-- signing algorithms.
|
||||
create table auth_oidc_signing_alg_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_auth_oidc_signing_algs_allowed
|
||||
check (
|
||||
name in (
|
||||
'RS256',
|
||||
'RS384',
|
||||
'RS512',
|
||||
'ES256',
|
||||
'ES384',
|
||||
'ES512',
|
||||
'PS256',
|
||||
'PS384',
|
||||
'PS512',
|
||||
'EdDSA')
|
||||
)
|
||||
);
|
||||
|
||||
-- populate the values of auth_oidc_signing_alg
|
||||
insert into auth_oidc_signing_alg_enm (name)
|
||||
values
|
||||
('RS256'),
|
||||
('RS384'),
|
||||
('RS512'),
|
||||
('ES256'),
|
||||
('ES384'),
|
||||
('ES512'),
|
||||
('PS256'),
|
||||
('PS384'),
|
||||
('PS512'),
|
||||
('EdDSA')
|
||||
;
|
||||
|
||||
-- define the immutable fields for auth_oidc_signing_alg (all of them)
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_signing_alg_enm
|
||||
for each row execute procedure immutable_columns('name');
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
1084: []byte(`
|
||||
begin;
|
||||
|
||||
-- auth_oidc_method entries are the current oidc auth methods configured for
|
||||
-- existing scopes.
|
||||
create table auth_oidc_method (
|
||||
public_id wt_public_id
|
||||
primary key,
|
||||
scope_id wt_scope_id
|
||||
not null,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
state text not null
|
||||
references auth_oidc_method_state_enm(name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
discovery_url wt_url not null, -- oidc discovery URL without any .well-known component
|
||||
client_id text not null -- oidc client identifier issued by the oidc provider.
|
||||
constraint client_id_not_empty
|
||||
check(length(trim(client_id)) > 0),
|
||||
client_secret bytea not null, -- encrypted oidc client secret issued by the oidc provider.
|
||||
key_id wt_private_id not null -- key used to encrypt entries via wrapping wrapper.
|
||||
references kms_oidc_key_version(private_id)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
max_age int not null -- the allowable elapsed time in secs since the last time the user was authenticated. zero is allowed and should force the user to be re-authenticated.
|
||||
constraint max_age_equal_or_greater_than_zero
|
||||
check(max_age >= 0),
|
||||
foreign key (scope_id, public_id)
|
||||
references auth_method (scope_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
unique(scope_id, name),
|
||||
unique(scope_id, public_id),
|
||||
unique(scope_id, discovery_url, client_id) -- a client_id must be unique for a provider within a scope.
|
||||
);
|
||||
|
||||
-- auth_oidc_signing_alg entries are the signing algorithms allowed for an oidc
|
||||
-- auth method. There must be at least one allowed alg for each oidc auth method.
|
||||
create table auth_oidc_signing_alg (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
signing_alg_name text
|
||||
references auth_oidc_signing_alg_enm(name)
|
||||
on delete restrict
|
||||
on update cascade,
|
||||
primary key(oidc_method_id, signing_alg_name)
|
||||
);
|
||||
|
||||
-- auth_oidc_callback_url entries are the callback URLs allowed for a specific
|
||||
-- oidc auth method. There must be at least one callback url for each oidc auth
|
||||
-- method.
|
||||
create table auth_oidc_callback_url (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
callback_url wt_url not null
|
||||
);
|
||||
|
||||
-- auth_oidc_aud_claim entries are the audience claims for a specific oidc auth
|
||||
-- method. There can be 0 or more for each parent oidc auth method. If an auth
|
||||
-- method has any aud claims, an ID token must contain one of them to be valid.
|
||||
create table auth_oidc_aud_claim (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
aud_claim text not null
|
||||
constraint aud_claim_must_not_be_empty
|
||||
check(length(trim(aud_claim)) > 0)
|
||||
constraint aud_claim_must_be_less_than_1024_chars
|
||||
check(length(trim(aud_claim)) < 1024),
|
||||
primary key(oidc_method_id, aud_claim)
|
||||
);
|
||||
|
||||
-- auth_oidc_certificate entries are optional PEM encoded x509 certificates.
|
||||
-- Each entry is a single certificate. An oidc auth method may have 0 or more
|
||||
-- of these optional x509s. If an auth method has any cert entries, they are
|
||||
-- used as trust anchors when connecting to the auth method's oidc provider
|
||||
-- (instead of the host system's cert chain).
|
||||
create table auth_oidc_certificate (
|
||||
oidc_method_id wt_public_id
|
||||
references auth_oidc_method(public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
certificate bytea not null,
|
||||
primary key(oidc_method_id, certificate)
|
||||
);
|
||||
|
||||
|
||||
create table auth_oidc_account (
|
||||
public_id wt_public_id
|
||||
primary key,
|
||||
auth_method_id wt_public_id
|
||||
not null,
|
||||
-- NOTE(mgaffney): The scope_id type is not wt_scope_id because the domain
|
||||
-- check is executed before the insert trigger which retrieves the scope_id
|
||||
-- causing an insert to fail.
|
||||
scope_id text not null,
|
||||
name wt_name,
|
||||
description wt_description,
|
||||
create_time wt_timestamp,
|
||||
update_time wt_timestamp,
|
||||
version wt_version,
|
||||
issuer_id wt_url not null, -- case-sensitive URL that maps to an id_token's iss claim
|
||||
subject_id text not null -- case-senstive string that maps to an id_token's sub claim
|
||||
constraint subject_id_must_not_be_empty
|
||||
check (
|
||||
length(trim(subject_id)) > 0
|
||||
)
|
||||
constraint subject_id_must_be_less_than_256_chars
|
||||
check(
|
||||
length(trim(subject_id)) <= 255 -- length limit per OIDC spec
|
||||
),
|
||||
full_name wt_full_name, -- may be null and maps to an id_token's name claim
|
||||
email wt_email, -- may be null and maps to the id_token's email claim
|
||||
foreign key (scope_id, auth_method_id)
|
||||
references auth_oidc_method (scope_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
foreign key (scope_id, auth_method_id, public_id)
|
||||
references auth_account (scope_id, auth_method_id, public_id)
|
||||
on delete cascade
|
||||
on update cascade,
|
||||
unique(auth_method_id, name),
|
||||
unique(auth_method_id, issuer_id, subject_id), -- subject must be unique for a provider within specific auth method
|
||||
unique(auth_method_id, public_id)
|
||||
);
|
||||
|
||||
-- auth_oidc_method column triggers
|
||||
create trigger
|
||||
insert_auth_method_subtype
|
||||
before insert on auth_oidc_method
|
||||
for each row execute procedure insert_auth_method_subtype();
|
||||
|
||||
create trigger
|
||||
update_time_column
|
||||
before
|
||||
update on auth_oidc_method
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_method
|
||||
for each row execute procedure immutable_columns('public_id', 'scope_id', 'create_time');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on auth_oidc_method
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on auth_oidc_method
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
-- auth_oidc_account column triggers
|
||||
create trigger
|
||||
update_time_column
|
||||
before
|
||||
update on auth_oidc_account
|
||||
for each row execute procedure update_time_column();
|
||||
|
||||
create trigger
|
||||
immutable_columns
|
||||
before
|
||||
update on auth_oidc_account
|
||||
for each row execute procedure immutable_columns('public_id', 'auth_method_id', 'scope_id', 'create_time', 'issuer_id', 'subject_id');
|
||||
|
||||
create trigger
|
||||
default_create_time_column
|
||||
before
|
||||
insert on auth_oidc_account
|
||||
for each row execute procedure default_create_time();
|
||||
|
||||
create trigger
|
||||
update_version_column
|
||||
after update on auth_oidc_account
|
||||
for each row execute procedure update_version_column();
|
||||
|
||||
create trigger
|
||||
insert_auth_account_subtype
|
||||
before insert on auth_oidc_account
|
||||
for each row execute procedure insert_auth_account_subtype();
|
||||
|
||||
commit;
|
||||
|
||||
`),
|
||||
1085: []byte(`
|
||||
begin;
|
||||
|
||||
-- auth_token_status_enm entries define the possible auth token
|
||||
-- states.
|
||||
create table auth_token_status_enm (
|
||||
name text primary key
|
||||
constraint only_predefined_auth_token_states_allowed
|
||||
check (
|
||||
name in ('auth token pending','token issued', 'authentication failed', 'system error')
|
||||
)
|
||||
);
|
||||
|
||||
-- populate the values of auth_token_status_enm
|
||||
insert into auth_token_status_enm(name)
|
||||
values
|
||||
('auth token pending'),
|
||||
('token issued'),
|
||||
('authentication failed'),
|
||||
('system error');
|
||||
|
||||
|
||||
-- add the state column with a default to the auth_token table.
|
||||
alter table auth_token
|
||||
add column status text
|
||||
not null
|
||||
default 'token issued' -- safest default
|
||||
references auth_token_status_enm(name)
|
||||
on update cascade
|
||||
on delete restrict;
|
||||
|
||||
commit;
|
||||
`),
|
||||
1086: []byte(`
|
||||
begin;
|
||||
|
||||
-- add the account_info_auth_method_id which determines which auth_method is
|
||||
-- designated as for "account info" in the user's scope.
|
||||
alter table iam_scope
|
||||
add column account_info_auth_method_id wt_public_id -- allowed to be null and is mutable of course.
|
||||
references auth_method(public_id)
|
||||
on update cascade
|
||||
on delete set null;
|
||||
|
||||
-- establish a compond fk, but there's no cascading of deletes or updates, since
|
||||
-- we only want to cascade changes to the account_info_auth_method_id portion of
|
||||
-- the compond fk and that is handled in a separate fk declaration.
|
||||
alter table iam_scope
|
||||
add foreign key (public_id, account_info_auth_method_id) references auth_method(scope_id, public_id);
|
||||
|
||||
-- iam_user_acct_info provides account info for users by determining which
|
||||
-- auth_method is designated as for "account info" in the user's scope via the
|
||||
-- scope's account_info_auth_method_id. Every sub-type of auth_account must be
|
||||
-- added to this view's union.
|
||||
create view iam_acct_info as
|
||||
select
|
||||
aa.iam_user_id,
|
||||
oa.subject_id as login_name,
|
||||
oa.full_name as full_name,
|
||||
oa.email as email
|
||||
from
|
||||
iam_scope s,
|
||||
auth_account aa,
|
||||
auth_oidc_account oa
|
||||
where
|
||||
aa.public_id = oa.public_id and
|
||||
aa.auth_method_id = s.account_info_auth_method_id
|
||||
union
|
||||
select
|
||||
aa.iam_user_id,
|
||||
pa.login_name,
|
||||
'' as full_name,
|
||||
'' as email
|
||||
from
|
||||
iam_scope s,
|
||||
auth_account aa,
|
||||
auth_password_account pa
|
||||
where
|
||||
aa.public_id = pa.public_id and
|
||||
aa.auth_method_id = s.account_info_auth_method_id;
|
||||
|
||||
-- iam_user_acct_info provides a simple way to retrieve entries that include
|
||||
-- both the iam_user fields with an outer join to the user's account info.
|
||||
create view iam_user_acct_info as
|
||||
select
|
||||
u.public_id,
|
||||
u.scope_id,
|
||||
u.name,
|
||||
u.description,
|
||||
u.create_time,
|
||||
u.update_time,
|
||||
u.version,
|
||||
i.login_name,
|
||||
i.full_name,
|
||||
i.email
|
||||
from
|
||||
iam_user u
|
||||
left outer join iam_acct_info i on u.public_id = i.iam_user_id;
|
||||
|
||||
commit;
|
||||
`),
|
||||
},
|
||||
|
||||
Reference in new issue
Block a user