mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat: add support for optional external auth providers (#12021)
This commit is contained in:
Generated
+3
@@ -11828,6 +11828,9 @@ const docTemplate = `{
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
Generated
+3
@@ -10709,6 +10709,9 @@
|
||||
"id": {
|
||||
"type": "string"
|
||||
},
|
||||
"optional": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"type": {
|
||||
"type": "string"
|
||||
}
|
||||
|
||||
+1
-1
@@ -1247,7 +1247,7 @@ func (api *API) CreateInMemoryProvisionerDaemon(dialCtx context.Context, name st
|
||||
Tags: provisionersdk.MutateTags(uuid.Nil, nil),
|
||||
LastSeenAt: sql.NullTime{Time: dbtime.Now(), Valid: true},
|
||||
Version: buildinfo.Version(),
|
||||
APIVersion: proto.VersionCurrent.String(),
|
||||
APIVersion: proto.CurrentVersion.String(),
|
||||
})
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to create in-memory provisioner daemon: %w", err)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
Generated
+1
-1
@@ -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
|
||||
);
|
||||
|
||||
@@ -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;
|
||||
@@ -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;
|
||||
+15
-15
@@ -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"`
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -501,10 +501,16 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
|
||||
return nil, failJob(fmt.Sprintf("get workspace build parameters: %s", err))
|
||||
}
|
||||
|
||||
externalAuthProviders := []*sdkproto.ExternalAuthProvider{}
|
||||
for _, p := range templateVersion.ExternalAuthProviders {
|
||||
dbExternalAuthProviders := []database.ExternalAuthProvider{}
|
||||
err = json.Unmarshal(templateVersion.ExternalAuthProviders, &dbExternalAuthProviders)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to deserialize external_auth_providers value: %w", err)
|
||||
}
|
||||
|
||||
externalAuthProviders := make([]*sdkproto.ExternalAuthProvider, 0, len(dbExternalAuthProviders))
|
||||
for _, p := range dbExternalAuthProviders {
|
||||
link, err := s.Database.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{
|
||||
ProviderID: p,
|
||||
ProviderID: p.ID,
|
||||
UserID: owner.ID,
|
||||
})
|
||||
if errors.Is(err, sql.ErrNoRows) {
|
||||
@@ -515,7 +521,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
|
||||
}
|
||||
var config *externalauth.Config
|
||||
for _, c := range s.ExternalAuthConfigs {
|
||||
if c.ID != p {
|
||||
if c.ID != p.ID {
|
||||
continue
|
||||
}
|
||||
config = c
|
||||
@@ -524,7 +530,7 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
|
||||
// We weren't able to find a matching config for the ID!
|
||||
if config == nil {
|
||||
s.Logger.Warn(ctx, "workspace build job is missing external auth provider",
|
||||
slog.F("provider_id", p),
|
||||
slog.F("provider_id", p.ID),
|
||||
slog.F("template_version_id", templateVersion.ID),
|
||||
slog.F("workspace_id", workspaceBuild.WorkspaceID))
|
||||
continue
|
||||
@@ -532,13 +538,13 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
|
||||
|
||||
link, valid, err := config.RefreshToken(ctx, s.Database, link)
|
||||
if err != nil {
|
||||
return nil, failJob(fmt.Sprintf("refresh external auth link %q: %s", p, err))
|
||||
return nil, failJob(fmt.Sprintf("refresh external auth link %q: %s", p.ID, err))
|
||||
}
|
||||
if !valid {
|
||||
continue
|
||||
}
|
||||
externalAuthProviders = append(externalAuthProviders, &sdkproto.ExternalAuthProvider{
|
||||
Id: p,
|
||||
Id: p.ID,
|
||||
AccessToken: link.OAuthAccessToken,
|
||||
})
|
||||
}
|
||||
@@ -1133,23 +1139,49 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
|
||||
for _, externalAuthProvider := range jobType.TemplateImport.ExternalAuthProviders {
|
||||
contains := false
|
||||
for _, configuredProvider := range s.ExternalAuthConfigs {
|
||||
if configuredProvider.ID == externalAuthProvider {
|
||||
if configuredProvider.ID == externalAuthProvider.Id {
|
||||
contains = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !contains {
|
||||
completedError = sql.NullString{
|
||||
String: fmt.Sprintf("external auth provider %q is not configured", externalAuthProvider),
|
||||
String: fmt.Sprintf("external auth provider %q is not configured", externalAuthProvider.Id),
|
||||
Valid: true,
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to `ExternalAuthProvidersNames` if it was specified and `ExternalAuthProviders`
|
||||
// was not. Gives us backwards compatibility with custom provisioners that haven't been
|
||||
// updated to use the new field yet.
|
||||
var externalAuthProviders []database.ExternalAuthProvider
|
||||
if providersLen := len(jobType.TemplateImport.ExternalAuthProviders); providersLen > 0 {
|
||||
externalAuthProviders = make([]database.ExternalAuthProvider, 0, providersLen)
|
||||
for _, provider := range jobType.TemplateImport.ExternalAuthProviders {
|
||||
externalAuthProviders = append(externalAuthProviders, database.ExternalAuthProvider{
|
||||
ID: provider.Id,
|
||||
Optional: provider.Optional,
|
||||
})
|
||||
}
|
||||
} else if namesLen := len(jobType.TemplateImport.ExternalAuthProvidersNames); namesLen > 0 {
|
||||
externalAuthProviders = make([]database.ExternalAuthProvider, 0, namesLen)
|
||||
for _, providerID := range jobType.TemplateImport.ExternalAuthProvidersNames {
|
||||
externalAuthProviders = append(externalAuthProviders, database.ExternalAuthProvider{
|
||||
ID: providerID,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
externalAuthProvidersMessage, err := json.Marshal(externalAuthProviders)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("failed to serialize external_auth_providers value: %w", err)
|
||||
}
|
||||
|
||||
err = s.Database.UpdateTemplateVersionExternalAuthProvidersByJobID(ctx, database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams{
|
||||
JobID: jobID,
|
||||
ExternalAuthProviders: jobType.TemplateImport.ExternalAuthProviders,
|
||||
ExternalAuthProviders: json.RawMessage(externalAuthProvidersMessage),
|
||||
UpdatedAt: dbtime.Now(),
|
||||
})
|
||||
if err != nil {
|
||||
|
||||
@@ -172,11 +172,14 @@ func TestAcquireJob(t *testing.T) {
|
||||
// create an API key with an expiration within the bounds of the
|
||||
// deployment config.
|
||||
dv := &codersdk.DeploymentValues{MaxTokenLifetime: clibase.Duration(time.Hour)}
|
||||
gitAuthProvider := "github"
|
||||
gitAuthProvider := &sdkproto.ExternalAuthProviderResource{
|
||||
Id: "github",
|
||||
}
|
||||
|
||||
srv, db, ps, _ := setup(t, false, &overrides{
|
||||
deploymentValues: dv,
|
||||
externalAuthConfigs: []*externalauth.Config{{
|
||||
ID: gitAuthProvider,
|
||||
ID: gitAuthProvider.Id,
|
||||
InstrumentedOAuth2Config: &testutil.OAuth2Config{},
|
||||
}},
|
||||
})
|
||||
@@ -191,7 +194,7 @@ func TestAcquireJob(t *testing.T) {
|
||||
OAuthAccessToken: "access-token",
|
||||
})
|
||||
dbgen.ExternalAuthLink(t, db, database.ExternalAuthLink{
|
||||
ProviderID: gitAuthProvider,
|
||||
ProviderID: gitAuthProvider.Id,
|
||||
UserID: user.ID,
|
||||
})
|
||||
template := dbgen.Template(t, db, database.Template{
|
||||
@@ -207,9 +210,14 @@ func TestAcquireJob(t *testing.T) {
|
||||
},
|
||||
JobID: uuid.New(),
|
||||
})
|
||||
err := db.UpdateTemplateVersionExternalAuthProvidersByJobID(ctx, database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams{
|
||||
externalAuthProviders, err := json.Marshal([]database.ExternalAuthProvider{{
|
||||
ID: gitAuthProvider.Id,
|
||||
Optional: gitAuthProvider.Optional,
|
||||
}})
|
||||
require.NoError(t, err)
|
||||
err = db.UpdateTemplateVersionExternalAuthProvidersByJobID(ctx, database.UpdateTemplateVersionExternalAuthProvidersByJobIDParams{
|
||||
JobID: version.JobID,
|
||||
ExternalAuthProviders: []string{gitAuthProvider},
|
||||
ExternalAuthProviders: json.RawMessage(externalAuthProviders),
|
||||
UpdatedAt: dbtime.Now(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
@@ -321,7 +329,7 @@ func TestAcquireJob(t *testing.T) {
|
||||
},
|
||||
},
|
||||
ExternalAuthProviders: []*sdkproto.ExternalAuthProvider{{
|
||||
Id: gitAuthProvider,
|
||||
Id: gitAuthProvider.Id,
|
||||
AccessToken: "access_token",
|
||||
}},
|
||||
Metadata: &sdkproto.Metadata{
|
||||
@@ -949,8 +957,10 @@ func TestCompleteJob(t *testing.T) {
|
||||
Name: "hello",
|
||||
Type: "aws_instance",
|
||||
}},
|
||||
StopResources: []*sdkproto.Resource{},
|
||||
ExternalAuthProviders: []string{"github"},
|
||||
StopResources: []*sdkproto.Resource{},
|
||||
ExternalAuthProviders: []*sdkproto.ExternalAuthProviderResource{{
|
||||
Id: "github",
|
||||
}},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1002,7 +1012,7 @@ func TestCompleteJob(t *testing.T) {
|
||||
Type: "aws_instance",
|
||||
}},
|
||||
StopResources: []*sdkproto.Resource{},
|
||||
ExternalAuthProviders: []string{"github"},
|
||||
ExternalAuthProviders: []*sdkproto.ExternalAuthProviderResource{{Id: "github"}},
|
||||
},
|
||||
},
|
||||
})
|
||||
@@ -1776,7 +1786,7 @@ func setup(t *testing.T, ignoreLogErrors bool, ov *overrides) (proto.DRPCProvisi
|
||||
Tags: database.StringMap{},
|
||||
LastSeenAt: sql.NullTime{},
|
||||
Version: buildinfo.Version(),
|
||||
APIVersion: proto.VersionCurrent.String(),
|
||||
APIVersion: proto.CurrentVersion.String(),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
@@ -289,19 +289,28 @@ func (api *API) templateVersionExternalAuth(rw http.ResponseWriter, r *http.Requ
|
||||
templateVersion = httpmw.TemplateVersionParam(r)
|
||||
)
|
||||
|
||||
rawProviders := templateVersion.ExternalAuthProviders
|
||||
var rawProviders []database.ExternalAuthProvider
|
||||
err := json.Unmarshal(templateVersion.ExternalAuthProviders, &rawProviders)
|
||||
if err != nil {
|
||||
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
|
||||
Message: "Internal error reading auth config from database",
|
||||
Detail: err.Error(),
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
providers := make([]codersdk.TemplateVersionExternalAuth, 0)
|
||||
for _, rawProvider := range rawProviders {
|
||||
var config *externalauth.Config
|
||||
for _, provider := range api.ExternalAuthConfigs {
|
||||
if provider.ID == rawProvider {
|
||||
if provider.ID == rawProvider.ID {
|
||||
config = provider
|
||||
break
|
||||
}
|
||||
}
|
||||
if config == nil {
|
||||
httpapi.Write(ctx, rw, http.StatusNotFound, codersdk.Response{
|
||||
Message: fmt.Sprintf("The template version references a Git auth provider %q that no longer exists.", rawProvider),
|
||||
Message: fmt.Sprintf("The template version references a Git auth provider %q that no longer exists.", rawProvider.ID),
|
||||
Detail: "You'll need to update the template version to use a different provider.",
|
||||
})
|
||||
return
|
||||
@@ -323,6 +332,7 @@ func (api *API) templateVersionExternalAuth(rw http.ResponseWriter, r *http.Requ
|
||||
AuthenticateURL: redirectURL.String(),
|
||||
DisplayName: config.DisplayName,
|
||||
DisplayIcon: config.DisplayIcon,
|
||||
Optional: rawProvider.Optional,
|
||||
}
|
||||
|
||||
authLink, err := api.Database.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{
|
||||
|
||||
@@ -347,7 +347,7 @@ func TestTemplateVersionsExternalAuth(t *testing.T) {
|
||||
ProvisionPlan: []*proto.Response{{
|
||||
Type: &proto.Response_Plan{
|
||||
Plan: &proto.PlanComplete{
|
||||
ExternalAuthProviders: []string{"github"},
|
||||
ExternalAuthProviders: []*proto.ExternalAuthProviderResource{{Id: "github", Optional: true}},
|
||||
},
|
||||
},
|
||||
}},
|
||||
@@ -373,6 +373,7 @@ func TestTemplateVersionsExternalAuth(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
require.Len(t, providers, 1)
|
||||
require.True(t, providers[0].Authenticated)
|
||||
require.True(t, providers[0].Optional)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user