feat: add support for optional external auth providers (#12021)

This commit is contained in:
Kayla Washburn-Love
2024-02-21 11:18:38 -07:00
committed by GitHub
parent 78c9f82719
commit 475c3650ca
39 changed files with 1495 additions and 727 deletions

View File

@ -928,8 +928,7 @@ func (s *MethodTestSuite) TestTemplate() {
JobID: jobID,
})
check.Args(database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams{
JobID: jobID,
ExternalAuthProviders: []string{},
JobID: jobID,
}).Asserts(t1, rbac.ActionUpdate).Returns()
}))
s.Run("GetTemplateInsights", s.Subtest(func(db database.Store, check *expects) {

View File

@ -219,7 +219,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
CreatedAt: now.Add(-14 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-7 * 24 * time.Hour).Add(time.Minute)},
Version: "1.0.0",
APIVersion: proto.VersionCurrent.String(),
APIVersion: proto.CurrentVersion.String(),
})
require.NoError(t, err)
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@ -230,7 +230,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
CreatedAt: now.Add(-8 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-8 * 24 * time.Hour).Add(time.Hour)},
Version: "1.0.0",
APIVersion: proto.VersionCurrent.String(),
APIVersion: proto.CurrentVersion.String(),
})
require.NoError(t, err)
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@ -243,7 +243,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
},
CreatedAt: now.Add(-9 * 24 * time.Hour),
Version: "1.0.0",
APIVersion: proto.VersionCurrent.String(),
APIVersion: proto.CurrentVersion.String(),
})
require.NoError(t, err)
_, err = db.UpsertProvisionerDaemon(ctx, database.UpsertProvisionerDaemonParams{
@ -257,7 +257,7 @@ func TestDeleteOldProvisionerDaemons(t *testing.T) {
CreatedAt: now.Add(-6 * 24 * time.Hour),
LastSeenAt: sql.NullTime{Valid: true, Time: now.Add(-6 * 24 * time.Hour)},
Version: "1.0.0",
APIVersion: proto.VersionCurrent.String(),
APIVersion: proto.CurrentVersion.String(),
})
require.NoError(t, err)

View File

@ -821,7 +821,7 @@ CREATE TABLE template_versions (
readme character varying(1048576) NOT NULL,
job_id uuid NOT NULL,
created_by uuid NOT NULL,
external_auth_providers text[],
external_auth_providers jsonb DEFAULT '[]'::jsonb NOT NULL,
message character varying(1048576) DEFAULT ''::character varying NOT NULL,
archived boolean DEFAULT false NOT NULL
);

View File

@ -0,0 +1,61 @@
-- We cannot alter the column type while a view depends on it, so we drop it and recreate it.
DROP VIEW template_version_with_user;
-- Does the opposite of `migrate_external_auth_providers_to_jsonb`
-- eg. `'[{"id": "github"}, {"id": "gitlab"}]'::jsonb` would become `'{github,gitlab}'::text[]`
CREATE OR REPLACE FUNCTION revert_migrate_external_auth_providers_to_jsonb(jsonb)
RETURNS text[]
LANGUAGE plpgsql
AS $$
DECLARE
result text[];
BEGIN
SELECT
array_agg(id::text) INTO result
FROM (
SELECT
jsonb_array_elements($1) ->> 'id' AS id) AS external_auth_provider_ids;
RETURN result;
END;
$$;
-- Remove the non-null constraint and default
ALTER TABLE template_versions
ALTER COLUMN external_auth_providers DROP DEFAULT;
ALTER TABLE template_versions
ALTER COLUMN external_auth_providers DROP NOT NULL;
-- Update the column type and migrate the values
ALTER TABLE template_versions
ALTER COLUMN external_auth_providers TYPE text[]
USING revert_migrate_external_auth_providers_to_jsonb(external_auth_providers);
-- Recreate `template_version_with_user` as described in dump.sql
CREATE VIEW template_version_with_user AS
SELECT
template_versions.id,
template_versions.template_id,
template_versions.organization_id,
template_versions.created_at,
template_versions.updated_at,
template_versions.name,
template_versions.readme,
template_versions.job_id,
template_versions.created_by,
template_versions.external_auth_providers,
template_versions.message,
template_versions.archived,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username
FROM (public.template_versions
LEFT JOIN visible_users ON (template_versions.created_by = visible_users.id));
COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.';
-- Cleanup
DROP FUNCTION revert_migrate_external_auth_providers_to_jsonb;

View File

@ -0,0 +1,63 @@
-- We cannot alter the column type while a view depends on it, so we drop it and recreate it.
DROP VIEW template_version_with_user;
-- Turns the list of provider names into JSONB with the type `Array<{ id: string; optional?: boolean }>`
-- eg. `'{github,gitlab}'::text[]` would become `'[{"id": "github"}, {"id": "gitlab"}]'::jsonb`
CREATE OR REPLACE FUNCTION migrate_external_auth_providers_to_jsonb(text[])
RETURNS jsonb
LANGUAGE plpgsql
AS $$
DECLARE
result jsonb;
BEGIN
SELECT
jsonb_agg(jsonb_build_object('id', value::text)) INTO result
FROM
unnest($1) AS value;
RETURN result;
END;
$$;
-- Update the column type and migrate the values
ALTER TABLE template_versions
ALTER COLUMN external_auth_providers TYPE jsonb
USING migrate_external_auth_providers_to_jsonb(external_auth_providers);
-- Make the column non-nullable to make the types nicer on the Go side
UPDATE template_versions
SET external_auth_providers = '[]'::jsonb
WHERE external_auth_providers IS NULL;
ALTER TABLE template_versions
ALTER COLUMN external_auth_providers SET DEFAULT '[]'::jsonb;
ALTER TABLE template_versions
ALTER COLUMN external_auth_providers SET NOT NULL;
-- Recreate `template_version_with_user` as described in dump.sql
CREATE VIEW template_version_with_user AS
SELECT
template_versions.id,
template_versions.template_id,
template_versions.organization_id,
template_versions.created_at,
template_versions.updated_at,
template_versions.name,
template_versions.readme,
template_versions.job_id,
template_versions.created_by,
template_versions.external_auth_providers,
template_versions.message,
template_versions.archived,
COALESCE(visible_users.avatar_url, ''::text) AS created_by_avatar_url,
COALESCE(visible_users.username, ''::text) AS created_by_username
FROM (public.template_versions
LEFT JOIN visible_users ON (template_versions.created_by = visible_users.id));
COMMENT ON VIEW template_version_with_user IS 'Joins in the username + avatar url of the created by user.';
-- Cleanup
DROP FUNCTION migrate_external_auth_providers_to_jsonb;

View File

@ -2080,20 +2080,20 @@ type TemplateTable struct {
// Joins in the username + avatar url of the created by user.
type TemplateVersion struct {
ID uuid.UUID `db:"id" json:"id"`
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Readme string `db:"readme" json:"readme"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
ExternalAuthProviders []string `db:"external_auth_providers" json:"external_auth_providers"`
Message string `db:"message" json:"message"`
Archived bool `db:"archived" json:"archived"`
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
ID uuid.UUID `db:"id" json:"id"`
TemplateID uuid.NullUUID `db:"template_id" json:"template_id"`
OrganizationID uuid.UUID `db:"organization_id" json:"organization_id"`
CreatedAt time.Time `db:"created_at" json:"created_at"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
Name string `db:"name" json:"name"`
Readme string `db:"readme" json:"readme"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"`
Message string `db:"message" json:"message"`
Archived bool `db:"archived" json:"archived"`
CreatedByAvatarURL string `db:"created_by_avatar_url" json:"created_by_avatar_url"`
CreatedByUsername string `db:"created_by_username" json:"created_by_username"`
}
type TemplateVersionParameter struct {
@ -2143,7 +2143,7 @@ type TemplateVersionTable struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
CreatedBy uuid.UUID `db:"created_by" json:"created_by"`
// IDs of External auth providers for a specific template version
ExternalAuthProviders []string `db:"external_auth_providers" json:"external_auth_providers"`
ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"`
// Message describing the changes in this version of the template, similar to a Git commit message. Like a commit message, this should be a short, high-level description of the changes in this version of the template. This message is immutable and should not be updated after the fact.
Message string `db:"message" json:"message"`
Archived bool `db:"archived" json:"archived"`

View File

@ -6870,7 +6870,7 @@ func (q *sqlQuerier) GetPreviousTemplateVersion(ctx context.Context, arg GetPrev
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -6901,7 +6901,7 @@ func (q *sqlQuerier) GetTemplateVersionByID(ctx context.Context, id uuid.UUID) (
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -6932,7 +6932,7 @@ func (q *sqlQuerier) GetTemplateVersionByJobID(ctx context.Context, jobID uuid.U
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -6969,7 +6969,7 @@ func (q *sqlQuerier) GetTemplateVersionByTemplateIDAndName(ctx context.Context,
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -7006,7 +7006,7 @@ func (q *sqlQuerier) GetTemplateVersionsByIDs(ctx context.Context, ids []uuid.UU
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -7100,7 +7100,7 @@ func (q *sqlQuerier) GetTemplateVersionsByTemplateID(ctx context.Context, arg Ge
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -7142,7 +7142,7 @@ func (q *sqlQuerier) GetTemplateVersionsCreatedAfter(ctx context.Context, create
&i.Readme,
&i.JobID,
&i.CreatedBy,
pq.Array(&i.ExternalAuthProviders),
&i.ExternalAuthProviders,
&i.Message,
&i.Archived,
&i.CreatedByAvatarURL,
@ -7292,13 +7292,13 @@ WHERE
`
type UpdateTemplateVersionExternalAuthProvidersByJobIDParams struct {
JobID uuid.UUID `db:"job_id" json:"job_id"`
ExternalAuthProviders []string `db:"external_auth_providers" json:"external_auth_providers"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
JobID uuid.UUID `db:"job_id" json:"job_id"`
ExternalAuthProviders json.RawMessage `db:"external_auth_providers" json:"external_auth_providers"`
UpdatedAt time.Time `db:"updated_at" json:"updated_at"`
}
func (q *sqlQuerier) UpdateTemplateVersionExternalAuthProvidersByJobID(ctx context.Context, arg UpdateTemplateVersionExternalAuthProvidersByJobIDParams) error {
_, err := q.db.ExecContext(ctx, updateTemplateVersionExternalAuthProvidersByJobID, arg.JobID, pq.Array(arg.ExternalAuthProviders), arg.UpdatedAt)
_, err := q.db.ExecContext(ctx, updateTemplateVersionExternalAuthProvidersByJobID, arg.JobID, arg.ExternalAuthProviders, arg.UpdatedAt)
return err
}

View File

@ -64,6 +64,11 @@ func (t TemplateACL) Value() (driver.Value, error) {
return json.Marshal(t)
}
type ExternalAuthProvider struct {
ID string `json:"id"`
Optional bool `json:"optional,omitempty"`
}
type StringMap map[string]string
func (m *StringMap) Scan(src interface{}) error {