Skip to content

Commit 5f88243

Browse files
authored
Fix db migration that adds support for OIDC in the data warehouse (#1471)
Four new columns were added to the `wh_user_dimension` table to support OIDC. The data type for the columns was originally `wh_dim_text` which is a domain type with a `not null` restriction. Adding the new columns caused the migration to fail if the `wh_user_dimension` table had any rows in it because the existing rows would violate the `not null` restriction. Closes #1469
1 parent b0dbd01 commit 5f88243

File tree

3 files changed

+231
-8
lines changed

3 files changed

+231
-8
lines changed

internal/db/schema/migrations/postgres/14/01_wh_user_dimension_oidc.up.sql

Lines changed: 33 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,39 @@
11
begin;
22

33
alter table wh_user_dimension
4-
add column auth_method_external_id wh_dim_text,
5-
add column auth_account_external_id wh_dim_text,
6-
add column auth_account_full_name wh_dim_text,
7-
add column auth_account_email wh_dim_text
4+
add column auth_method_external_id text,
5+
add column auth_account_external_id text,
6+
add column auth_account_full_name text,
7+
add column auth_account_email text
8+
;
9+
10+
update wh_user_dimension
11+
set auth_method_type =
12+
case when auth_method_id like 'ampw_%' then 'password auth method'
13+
when auth_method_id like 'amoidc_%' then 'oidc auth method'
14+
else 'Unknown' end,
15+
auth_account_type =
16+
case when auth_account_id like 'acctpw_%' then 'password auth account'
17+
when auth_account_id like 'acctoidc_%' then 'oidc auth account'
18+
else 'Unknown' end,
19+
auth_method_external_id =
20+
case when auth_method_id like 'ampw_%' then 'Not Applicable'
21+
else 'Unknown' end,
22+
auth_account_external_id =
23+
case when auth_method_id like 'ampw_%' then 'Not Applicable'
24+
else 'Unknown' end,
25+
auth_account_full_name =
26+
case when auth_method_id like 'ampw_%' then 'Not Applicable'
27+
else 'Unknown' end,
28+
auth_account_email =
29+
case when auth_method_id like 'ampw_%' then 'Not Applicable'
30+
else 'Unknown' end;
31+
32+
alter table wh_user_dimension
33+
alter column auth_method_external_id type wh_dim_text,
34+
alter column auth_account_external_id type wh_dim_text,
35+
alter column auth_account_full_name type wh_dim_text,
36+
alter column auth_account_email type wh_dim_text
837
;
938

1039
drop view whx_user_dimension_source;
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
package migration
2+
3+
import (
4+
"context"
5+
"database/sql"
6+
"testing"
7+
8+
"github.com/hashicorp/boundary/internal/auth/oidc"
9+
"github.com/hashicorp/boundary/internal/authtoken"
10+
"github.com/hashicorp/boundary/internal/db"
11+
"github.com/hashicorp/boundary/internal/db/schema"
12+
"github.com/hashicorp/boundary/internal/docker"
13+
"github.com/hashicorp/boundary/internal/host/static"
14+
"github.com/hashicorp/boundary/internal/iam"
15+
"github.com/hashicorp/boundary/internal/kms"
16+
"github.com/hashicorp/boundary/internal/session"
17+
"github.com/hashicorp/boundary/internal/target"
18+
wrapping "github.com/hashicorp/go-kms-wrapping"
19+
"github.com/jinzhu/gorm"
20+
"github.com/stretchr/testify/assert"
21+
"github.com/stretchr/testify/require"
22+
)
23+
24+
func TestMigrations_UserDimension(t *testing.T) {
25+
const (
26+
priorMigration = 13001
27+
currentMigration = 14001
28+
)
29+
30+
t.Parallel()
31+
assert, require := assert.New(t), require.New(t)
32+
ctx := context.Background()
33+
dialect := "postgres"
34+
35+
c, u, _, err := docker.StartDbInDocker(dialect)
36+
require.NoError(err)
37+
t.Cleanup(func() {
38+
require.NoError(c())
39+
})
40+
d, err := sql.Open(dialect, u)
41+
require.NoError(err)
42+
43+
// migration to the prior migration (before the one we want to test)
44+
oState := schema.TestCloneMigrationStates(t)
45+
nState := schema.TestCreatePartialMigrationState(oState["postgres"], priorMigration)
46+
oState["postgres"] = nState
47+
48+
m, err := schema.NewManager(ctx, dialect, d, schema.WithMigrationStates(oState))
49+
require.NoError(err)
50+
51+
assert.NoError(m.RollForward(ctx))
52+
state, err := m.CurrentState(ctx)
53+
require.NoError(err)
54+
assert.Equal(priorMigration, state.DatabaseSchemaVersion)
55+
assert.False(state.Dirty)
56+
57+
// okay, now we can seed the database with test data
58+
conn, err := gorm.Open(dialect, u)
59+
require.NoError(err)
60+
61+
rw := db.New(conn)
62+
wrapper := db.TestWrapper(t)
63+
64+
org, prj := iam.TestScopes(t, iam.TestRepo(t, conn, wrapper))
65+
require.NotNil(prj)
66+
assert.NotEmpty(prj.GetPublicId())
67+
68+
hc := static.TestCatalogs(t, conn, prj.GetPublicId(), 1)[0]
69+
hs := static.TestSets(t, conn, hc.GetPublicId(), 1)[0]
70+
h := static.TestHosts(t, conn, hc.GetPublicId(), 1)[0]
71+
static.TestSetMembers(t, conn, hs.GetPublicId(), []*static.Host{h})
72+
73+
tar := target.TestTcpTarget(t, conn, prj.GetPublicId(), "test", target.WithHostSources([]string{hs.GetPublicId()}))
74+
var sessions []*session.Session
75+
76+
kmsCache := kms.TestKms(t, conn, wrapper)
77+
databaseWrapper, err := kmsCache.GetWrapper(ctx, org.GetPublicId(), kms.KeyPurposeDatabase)
78+
require.NoError(err)
79+
80+
{
81+
at := authtoken.TestAuthToken(t, conn, kmsCache, org.GetPublicId())
82+
uId := at.GetIamUserId()
83+
84+
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
85+
UserId: uId,
86+
HostId: h.GetPublicId(),
87+
TargetId: tar.GetPublicId(),
88+
HostSetId: hs.GetPublicId(),
89+
AuthTokenId: at.GetPublicId(),
90+
ScopeId: prj.GetPublicId(),
91+
Endpoint: "tcp://127.0.0.1:22",
92+
})
93+
sessions = append(sessions, sess)
94+
}
95+
96+
{
97+
at := testOidcAuthToken(t, conn, kmsCache, databaseWrapper, org.GetPublicId())
98+
uId := at.GetIamUserId()
99+
100+
sess := session.TestSession(t, conn, wrapper, session.ComposedOf{
101+
UserId: uId,
102+
HostId: h.GetPublicId(),
103+
TargetId: tar.GetPublicId(),
104+
HostSetId: hs.GetPublicId(),
105+
AuthTokenId: at.GetPublicId(),
106+
ScopeId: prj.GetPublicId(),
107+
Endpoint: "tcp://127.0.0.1:22",
108+
})
109+
sessions = append(sessions, sess)
110+
}
111+
112+
sessionRepo, err := session.NewRepository(rw, rw, kmsCache)
113+
require.NoError(err)
114+
115+
count, err := sessionRepo.TerminateCompletedSessions(ctx)
116+
assert.NoError(err)
117+
assert.Zero(count)
118+
119+
for _, sess := range sessions {
120+
// call TerminateSession
121+
_, err = sessionRepo.TerminateSession(ctx, sess.GetPublicId(), 1, session.ClosedByUser)
122+
assert.NoError(err)
123+
}
124+
125+
// now we're ready for the migration we want to test.
126+
oState = schema.TestCloneMigrationStates(t)
127+
nState = schema.TestCreatePartialMigrationState(oState["postgres"], currentMigration)
128+
oState["postgres"] = nState
129+
130+
m, err = schema.NewManager(ctx, dialect, d, schema.WithMigrationStates(oState))
131+
require.NoError(err)
132+
133+
assert.NoError(m.RollForward(ctx))
134+
state, err = m.CurrentState(ctx)
135+
require.NoError(err)
136+
assert.Equal(currentMigration, state.DatabaseSchemaVersion)
137+
assert.False(state.Dirty)
138+
}
139+
140+
func testOidcAuthToken(t *testing.T, conn *gorm.DB, kms *kms.Kms, wrapper wrapping.Wrapper, scopeId string) *authtoken.AuthToken {
141+
t.Helper()
142+
143+
authMethod := oidc.TestAuthMethod(
144+
t, conn, wrapper, scopeId, oidc.ActivePrivateState,
145+
"alice-rp", "fido",
146+
oidc.WithIssuer(oidc.TestConvertToUrls(t, "https://www.alice.com")[0]),
147+
oidc.WithSigningAlgs(oidc.RS256),
148+
oidc.WithApiUrl(oidc.TestConvertToUrls(t, "https://www.alice.com/callback")[0]),
149+
)
150+
acct := oidc.TestAccount(t, conn, authMethod, "test-subject")
151+
152+
ctx := context.Background()
153+
rw := db.New(conn)
154+
iamRepo, err := iam.NewRepository(rw, rw, kms)
155+
require.NoError(t, err)
156+
157+
u := iam.TestUser(t, iamRepo, scopeId, iam.WithAccountIds(acct.PublicId))
158+
159+
repo, err := authtoken.NewRepository(rw, rw, kms)
160+
require.NoError(t, err)
161+
162+
at, err := repo.CreateAuthToken(ctx, u, acct.GetPublicId())
163+
require.NoError(t, err)
164+
return at
165+
}

internal/db/schema/postgres_migration.gen.go

Lines changed: 33 additions & 4 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)