Skip to content

feat: add support for optional external auth providers #12021

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 35 commits into from
Feb 21, 2024
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
f9feb97
frontend hacking
aslilac Feb 9, 2024
179adae
pipe through all of the protobuf stuff
aslilac Feb 9, 2024
bdf6142
db migration
aslilac Feb 9, 2024
751400b
💅
aslilac Feb 12, 2024
5068d88
Merge branch 'main' into optional-external-auth
aslilac Feb 12, 2024
eaddb5b
🧹
aslilac Feb 12, 2024
eba3d12
tests!
aslilac Feb 12, 2024
ebb518b
🧪
aslilac Feb 12, 2024
29be417
official release
aslilac Feb 12, 2024
48267a4
add default first in up migration
aslilac Feb 12, 2024
e870667
manually default
aslilac Feb 12, 2024
d58c088
connect with correct version
aslilac Feb 13, 2024
757ea0f
Merge branch 'main' into optional-external-auth
aslilac Feb 14, 2024
79ad9da
land the backend changes separately
aslilac Feb 14, 2024
6746f88
fix migration order
aslilac Feb 14, 2024
cb7ab9a
version.go is not generated
aslilac Feb 14, 2024
3bacc70
it could just all go away
aslilac Feb 15, 2024
28a878d
Revert "it could just all go away"
aslilac Feb 15, 2024
b514292
Merge branch 'main' into optional-external-auth
aslilac Feb 15, 2024
892a79b
change tested version
aslilac Feb 15, 2024
c5e63eb
add `database.ExternalAuthProviders` type
aslilac Feb 15, 2024
6f933b8
Merge branch 'main' into optional-external-auth
aslilac Feb 16, 2024
ea81a1f
`drop` migration functions
aslilac Feb 16, 2024
2376357
`make gen`
aslilac Feb 16, 2024
b9471a5
Merge branch 'main' into optional-external-auth
aslilac Feb 16, 2024
b2b9e95
🆘
aslilac Feb 16, 2024
b159156
Merge branch 'main' into optional-external-auth
aslilac Feb 20, 2024
04579de
no such file
aslilac Feb 20, 2024
7ff56b1
bump version again
aslilac Feb 20, 2024
16ba268
update test
aslilac Feb 20, 2024
bdb875a
Merge branch 'main' into optional-external-auth
aslilac Feb 21, 2024
328202a
fix migration numbers
aslilac Feb 21, 2024
a475ec0
Merge branch 'main' into optional-external-auth
aslilac Feb 21, 2024
353e6a3
`COMMENT ON VIEW`
aslilac Feb 21, 2024
1021bc4
`make gen`
aslilac Feb 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
add database.ExternalAuthProviders type
  • Loading branch information
aslilac committed Feb 15, 2024
commit c5e63eb5648c83ec671ded3868bf2a65864c6516
5 changes: 5 additions & 0 deletions coderd/database/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
37 changes: 22 additions & 15 deletions coderd/provisionerdserver/provisionerdserver.go
Original file line number Diff line number Diff line change
Expand Up @@ -501,16 +501,16 @@ func (s *server) acquireProtoJob(ctx context.Context, job database.ProvisionerJo
return nil, failJob(fmt.Sprintf("get workspace build parameters: %s", err))
}

externalAuthProviderResources := []*sdkproto.ExternalAuthProviderResource{}
err = json.Unmarshal(templateVersion.ExternalAuthProviders, &externalAuthProviderResources)
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 := []*sdkproto.ExternalAuthProvider{}
for _, p := range externalAuthProviderResources {
externalAuthProviders := make([]*sdkproto.ExternalAuthProvider, 0, len(dbExternalAuthProviders))
for _, p := range dbExternalAuthProviders {
link, err := s.Database.GetExternalAuthLink(ctx, database.GetExternalAuthLinkParams{
ProviderID: p.Id,
ProviderID: p.ID,
UserID: owner.ID,
})
if errors.Is(err, sql.ErrNoRows) {
Expand All @@ -521,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.Id {
if c.ID != p.ID {
continue
}
config = c
Expand All @@ -530,21 +530,21 @@ 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.Id),
slog.F("provider_id", p.ID),
slog.F("template_version_id", templateVersion.ID),
slog.F("workspace_id", workspaceBuild.WorkspaceID))
continue
}

link, valid, err := config.RefreshToken(ctx, s.Database, link)
if err != nil {
return nil, failJob(fmt.Sprintf("refresh external auth link %q: %s", p.Id, 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,
Id: p.ID,
AccessToken: link.OAuthAccessToken,
})
}
Expand Down Expand Up @@ -1153,16 +1153,23 @@ func (s *server) CompleteJob(ctx context.Context, completed *proto.CompletedJob)
}
}

externalAuthProviders := jobType.TemplateImport.ExternalAuthProviders
// 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.
namesLen := len(jobType.TemplateImport.ExternalAuthProvidersNames)
if len(externalAuthProviders) == 0 && namesLen > 0 {
externalAuthProviders = make([]*sdkproto.ExternalAuthProviderResource, 0, namesLen)
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, &sdkproto.ExternalAuthProviderResource{
Id: providerID,
externalAuthProviders = append(externalAuthProviders, database.ExternalAuthProvider{
ID: providerID,
})
}
}
Expand Down
5 changes: 4 additions & 1 deletion coderd/provisionerdserver/provisionerdserver_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,10 @@ func TestAcquireJob(t *testing.T) {
},
JobID: uuid.New(),
})
externalAuthProviders, err := json.Marshal([]*sdkproto.ExternalAuthProviderResource{gitAuthProvider})
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,
Expand Down
6 changes: 3 additions & 3 deletions coderd/templateversions.go
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ func (api *API) templateVersionExternalAuth(rw http.ResponseWriter, r *http.Requ
templateVersion = httpmw.TemplateVersionParam(r)
)

var rawProviders []*sdkproto.ExternalAuthProviderResource
var rawProviders []database.ExternalAuthProvider
err := json.Unmarshal(templateVersion.ExternalAuthProviders, &rawProviders)
if err != nil {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Expand All @@ -303,14 +303,14 @@ func (api *API) templateVersionExternalAuth(rw http.ResponseWriter, r *http.Requ
for _, rawProvider := range rawProviders {
var config *externalauth.Config
for _, provider := range api.ExternalAuthConfigs {
if provider.ID == rawProvider.Id {
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
Expand Down
3 changes: 2 additions & 1 deletion coderd/templateversions_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -347,7 +347,7 @@ func TestTemplateVersionsExternalAuth(t *testing.T) {
ProvisionPlan: []*proto.Response{{
Type: &proto.Response_Plan{
Plan: &proto.PlanComplete{
ExternalAuthProviders: []*proto.ExternalAuthProviderResource{{Id: "github"}},
ExternalAuthProviders: []*proto.ExternalAuthProviderResource{{Id: "github", Optional: true}},
},
},
}},
Expand All @@ -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)
})
}

Expand Down
15 changes: 11 additions & 4 deletions provisionerd/runner/runner.go
Original file line number Diff line number Diff line change
Expand Up @@ -564,14 +564,21 @@ func (r *Runner) runTemplateImport(ctx context.Context) (*proto.CompletedJob, *p
return nil, r.failedJobf("template import provision for stop: %s", err)
}

// For backwards compatibility with older versions of coderd
externalAuthProviderNames := make([]string, 0, len(startProvision.ExternalAuthProviders))
for _, it := range startProvision.ExternalAuthProviders {
externalAuthProviderNames = append(externalAuthProviderNames, it.Id)
}

return &proto.CompletedJob{
JobId: r.job.JobId,
Type: &proto.CompletedJob_TemplateImport_{
TemplateImport: &proto.CompletedJob_TemplateImport{
StartResources: startProvision.Resources,
StopResources: stopProvision.Resources,
RichParameters: startProvision.Parameters,
ExternalAuthProviders: startProvision.ExternalAuthProviders,
StartResources: startProvision.Resources,
StopResources: stopProvision.Resources,
RichParameters: startProvision.Parameters,
ExternalAuthProvidersNames: externalAuthProviderNames,
ExternalAuthProviders: startProvision.ExternalAuthProviders,
},
},
}, nil
Expand Down