mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: enable GitHub OAuth2 login by default on new deployments (#16662)
Third and final PR to address https://github.com/coder/coder/issues/16230. This PR enables GitHub OAuth2 login by default on new deployments. Combined with https://github.com/coder/coder/pull/16629, this will allow the first admin user to sign up with GitHub rather than email and password. We take care not to enable the default on deployments that would upgrade to a Coder version with this change. To disable the default provider an admin can set the `CODER_OAUTH2_GITHUB_DEFAULT_PROVIDER` env variable to false.
This commit is contained in:
16
coderd/apidoc/docs.go
generated
16
coderd/apidoc/docs.go
generated
@ -10331,7 +10331,7 @@ const docTemplate = `{
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"github": {
|
||||
"$ref": "#/definitions/codersdk.AuthMethod"
|
||||
"$ref": "#/definitions/codersdk.GithubAuthMethod"
|
||||
},
|
||||
"oidc": {
|
||||
"$ref": "#/definitions/codersdk.OIDCAuthMethod"
|
||||
@ -11857,6 +11857,17 @@ const docTemplate = `{
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.GithubAuthMethod": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_provider_configured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -12519,6 +12530,9 @@ const docTemplate = `{
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"default_provider_enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device_flow": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
16
coderd/apidoc/swagger.json
generated
16
coderd/apidoc/swagger.json
generated
@ -9189,7 +9189,7 @@
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"github": {
|
||||
"$ref": "#/definitions/codersdk.AuthMethod"
|
||||
"$ref": "#/definitions/codersdk.GithubAuthMethod"
|
||||
},
|
||||
"oidc": {
|
||||
"$ref": "#/definitions/codersdk.OIDCAuthMethod"
|
||||
@ -10642,6 +10642,17 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.GithubAuthMethod": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"default_provider_configured": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"enabled": {
|
||||
"type": "boolean"
|
||||
}
|
||||
}
|
||||
},
|
||||
"codersdk.Group": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
@ -11255,6 +11266,9 @@
|
||||
"client_secret": {
|
||||
"type": "string"
|
||||
},
|
||||
"default_provider_enable": {
|
||||
"type": "boolean"
|
||||
},
|
||||
"device_flow": {
|
||||
"type": "boolean"
|
||||
},
|
||||
|
@ -1845,6 +1845,13 @@ func (q *querier) GetNotificationsSettings(ctx context.Context) (string, error)
|
||||
return q.db.GetNotificationsSettings(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceDeploymentConfig); err != nil {
|
||||
return false, err
|
||||
}
|
||||
return q.db.GetOAuth2GithubDefaultEligible(ctx)
|
||||
}
|
||||
|
||||
func (q *querier) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
if err := q.authorizeContext(ctx, policy.ActionRead, rbac.ResourceOauth2App); err != nil {
|
||||
return database.OAuth2ProviderApp{}, err
|
||||
@ -4435,6 +4442,13 @@ func (q *querier) UpsertNotificationsSettings(ctx context.Context, value string)
|
||||
return q.db.UpsertNotificationsSettings(ctx, value)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceDeploymentConfig); err != nil {
|
||||
return err
|
||||
}
|
||||
return q.db.UpsertOAuth2GithubDefaultEligible(ctx, eligible)
|
||||
}
|
||||
|
||||
func (q *querier) UpsertOAuthSigningKey(ctx context.Context, value string) error {
|
||||
if err := q.authorizeContext(ctx, policy.ActionUpdate, rbac.ResourceSystem); err != nil {
|
||||
return err
|
||||
|
@ -4405,6 +4405,12 @@ func (s *MethodTestSuite) TestSystemFunctions() {
|
||||
Value: "value",
|
||||
}).Asserts(rbac.ResourceSystem, policy.ActionUpdate)
|
||||
}))
|
||||
s.Run("GetOAuth2GithubDefaultEligible", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args().Asserts(rbac.ResourceDeploymentConfig, policy.ActionRead).Errors(sql.ErrNoRows)
|
||||
}))
|
||||
s.Run("UpsertOAuth2GithubDefaultEligible", s.Subtest(func(db database.Store, check *expects) {
|
||||
check.Args(true).Asserts(rbac.ResourceDeploymentConfig, policy.ActionUpdate)
|
||||
}))
|
||||
}
|
||||
|
||||
func (s *MethodTestSuite) TestNotifications() {
|
||||
|
@ -254,6 +254,7 @@ type data struct {
|
||||
announcementBanners []byte
|
||||
healthSettings []byte
|
||||
notificationsSettings []byte
|
||||
oauth2GithubDefaultEligible *bool
|
||||
applicationName string
|
||||
logoURL string
|
||||
appSecurityKey string
|
||||
@ -3515,6 +3516,16 @@ func (q *FakeQuerier) GetNotificationsSettings(_ context.Context) (string, error
|
||||
return string(q.notificationsSettings), nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2GithubDefaultEligible(_ context.Context) (bool, error) {
|
||||
q.mutex.RLock()
|
||||
defer q.mutex.RUnlock()
|
||||
|
||||
if q.oauth2GithubDefaultEligible == nil {
|
||||
return false, sql.ErrNoRows
|
||||
}
|
||||
return *q.oauth2GithubDefaultEligible, nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) GetOAuth2ProviderAppByID(_ context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
@ -11154,6 +11165,14 @@ func (q *FakeQuerier) UpsertNotificationsSettings(_ context.Context, data string
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertOAuth2GithubDefaultEligible(_ context.Context, eligible bool) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
||||
q.oauth2GithubDefaultEligible = &eligible
|
||||
return nil
|
||||
}
|
||||
|
||||
func (q *FakeQuerier) UpsertOAuthSigningKey(_ context.Context, value string) error {
|
||||
q.mutex.Lock()
|
||||
defer q.mutex.Unlock()
|
||||
|
@ -871,6 +871,13 @@ func (m queryMetricsStore) GetNotificationsSettings(ctx context.Context) (string
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2GithubDefaultEligible(ctx)
|
||||
m.queryLatencies.WithLabelValues("GetOAuth2GithubDefaultEligible").Observe(time.Since(start).Seconds())
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
start := time.Now()
|
||||
r0, r1 := m.s.GetOAuth2ProviderAppByID(ctx, id)
|
||||
@ -2817,6 +2824,13 @@ func (m queryMetricsStore) UpsertNotificationsSettings(ctx context.Context, valu
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertOAuth2GithubDefaultEligible(ctx, eligible)
|
||||
m.queryLatencies.WithLabelValues("UpsertOAuth2GithubDefaultEligible").Observe(time.Since(start).Seconds())
|
||||
return r0
|
||||
}
|
||||
|
||||
func (m queryMetricsStore) UpsertOAuthSigningKey(ctx context.Context, value string) error {
|
||||
start := time.Now()
|
||||
r0 := m.s.UpsertOAuthSigningKey(ctx, value)
|
||||
|
@ -1762,6 +1762,21 @@ func (mr *MockStoreMockRecorder) GetNotificationsSettings(ctx any) *gomock.Call
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetNotificationsSettings", reflect.TypeOf((*MockStore)(nil).GetNotificationsSettings), ctx)
|
||||
}
|
||||
|
||||
// GetOAuth2GithubDefaultEligible mocks base method.
|
||||
func (m *MockStore) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetOAuth2GithubDefaultEligible", ctx)
|
||||
ret0, _ := ret[0].(bool)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetOAuth2GithubDefaultEligible indicates an expected call of GetOAuth2GithubDefaultEligible.
|
||||
func (mr *MockStoreMockRecorder) GetOAuth2GithubDefaultEligible(ctx any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetOAuth2GithubDefaultEligible", reflect.TypeOf((*MockStore)(nil).GetOAuth2GithubDefaultEligible), ctx)
|
||||
}
|
||||
|
||||
// GetOAuth2ProviderAppByID mocks base method.
|
||||
func (m *MockStore) GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (database.OAuth2ProviderApp, error) {
|
||||
m.ctrl.T.Helper()
|
||||
@ -5936,6 +5951,20 @@ func (mr *MockStoreMockRecorder) UpsertNotificationsSettings(ctx, value any) *go
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertNotificationsSettings", reflect.TypeOf((*MockStore)(nil).UpsertNotificationsSettings), ctx, value)
|
||||
}
|
||||
|
||||
// UpsertOAuth2GithubDefaultEligible mocks base method.
|
||||
func (m *MockStore) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "UpsertOAuth2GithubDefaultEligible", ctx, eligible)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// UpsertOAuth2GithubDefaultEligible indicates an expected call of UpsertOAuth2GithubDefaultEligible.
|
||||
func (mr *MockStoreMockRecorder) UpsertOAuth2GithubDefaultEligible(ctx, eligible any) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UpsertOAuth2GithubDefaultEligible", reflect.TypeOf((*MockStore)(nil).UpsertOAuth2GithubDefaultEligible), ctx, eligible)
|
||||
}
|
||||
|
||||
// UpsertOAuthSigningKey mocks base method.
|
||||
func (m *MockStore) UpsertOAuthSigningKey(ctx context.Context, value string) error {
|
||||
m.ctrl.T.Helper()
|
||||
|
@ -185,6 +185,7 @@ type sqlcQuerier interface {
|
||||
GetNotificationTemplateByID(ctx context.Context, id uuid.UUID) (NotificationTemplate, error)
|
||||
GetNotificationTemplatesByKind(ctx context.Context, kind NotificationTemplateKind) ([]NotificationTemplate, error)
|
||||
GetNotificationsSettings(ctx context.Context) (string, error)
|
||||
GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error)
|
||||
GetOAuth2ProviderAppByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderApp, error)
|
||||
GetOAuth2ProviderAppCodeByID(ctx context.Context, id uuid.UUID) (OAuth2ProviderAppCode, error)
|
||||
GetOAuth2ProviderAppCodeByPrefix(ctx context.Context, secretPrefix []byte) (OAuth2ProviderAppCode, error)
|
||||
@ -553,6 +554,7 @@ type sqlcQuerier interface {
|
||||
// Insert or update notification report generator logs with recent activity.
|
||||
UpsertNotificationReportGeneratorLog(ctx context.Context, arg UpsertNotificationReportGeneratorLogParams) error
|
||||
UpsertNotificationsSettings(ctx context.Context, value string) error
|
||||
UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error
|
||||
UpsertOAuthSigningKey(ctx context.Context, value string) error
|
||||
UpsertProvisionerDaemon(ctx context.Context, arg UpsertProvisionerDaemonParams) (ProvisionerDaemon, error)
|
||||
UpsertRuntimeConfig(ctx context.Context, arg UpsertRuntimeConfigParams) error
|
||||
|
@ -8100,6 +8100,23 @@ func (q *sqlQuerier) GetNotificationsSettings(ctx context.Context) (string, erro
|
||||
return notifications_settings, err
|
||||
}
|
||||
|
||||
const getOAuth2GithubDefaultEligible = `-- name: GetOAuth2GithubDefaultEligible :one
|
||||
SELECT
|
||||
CASE
|
||||
WHEN value = 'true' THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
FROM site_configs
|
||||
WHERE key = 'oauth2_github_default_eligible'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) GetOAuth2GithubDefaultEligible(ctx context.Context) (bool, error) {
|
||||
row := q.db.QueryRowContext(ctx, getOAuth2GithubDefaultEligible)
|
||||
var column_1 bool
|
||||
err := row.Scan(&column_1)
|
||||
return column_1, err
|
||||
}
|
||||
|
||||
const getOAuthSigningKey = `-- name: GetOAuthSigningKey :one
|
||||
SELECT value FROM site_configs WHERE key = 'oauth_signing_key'
|
||||
`
|
||||
@ -8243,6 +8260,28 @@ func (q *sqlQuerier) UpsertNotificationsSettings(ctx context.Context, value stri
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertOAuth2GithubDefaultEligible = `-- name: UpsertOAuth2GithubDefaultEligible :exec
|
||||
INSERT INTO site_configs (key, value)
|
||||
VALUES (
|
||||
'oauth2_github_default_eligible',
|
||||
CASE
|
||||
WHEN $1::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
)
|
||||
ON CONFLICT (key) DO UPDATE
|
||||
SET value = CASE
|
||||
WHEN $1::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
WHERE site_configs.key = 'oauth2_github_default_eligible'
|
||||
`
|
||||
|
||||
func (q *sqlQuerier) UpsertOAuth2GithubDefaultEligible(ctx context.Context, eligible bool) error {
|
||||
_, err := q.db.ExecContext(ctx, upsertOAuth2GithubDefaultEligible, eligible)
|
||||
return err
|
||||
}
|
||||
|
||||
const upsertOAuthSigningKey = `-- name: UpsertOAuthSigningKey :exec
|
||||
INSERT INTO site_configs (key, value) VALUES ('oauth_signing_key', $1)
|
||||
ON CONFLICT (key) DO UPDATE set value = $1 WHERE site_configs.key = 'oauth_signing_key'
|
||||
|
@ -107,3 +107,27 @@ ON CONFLICT (key) DO UPDATE SET value = $2 WHERE site_configs.key = $1;
|
||||
DELETE FROM site_configs
|
||||
WHERE site_configs.key = $1;
|
||||
|
||||
-- name: GetOAuth2GithubDefaultEligible :one
|
||||
SELECT
|
||||
CASE
|
||||
WHEN value = 'true' THEN TRUE
|
||||
ELSE FALSE
|
||||
END
|
||||
FROM site_configs
|
||||
WHERE key = 'oauth2_github_default_eligible';
|
||||
|
||||
-- name: UpsertOAuth2GithubDefaultEligible :exec
|
||||
INSERT INTO site_configs (key, value)
|
||||
VALUES (
|
||||
'oauth2_github_default_eligible',
|
||||
CASE
|
||||
WHEN sqlc.arg(eligible)::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
)
|
||||
ON CONFLICT (key) DO UPDATE
|
||||
SET value = CASE
|
||||
WHEN sqlc.arg(eligible)::bool THEN 'true'
|
||||
ELSE 'false'
|
||||
END
|
||||
WHERE site_configs.key = 'oauth2_github_default_eligible';
|
||||
|
@ -765,6 +765,8 @@ type GithubOAuth2Config struct {
|
||||
AllowEveryone bool
|
||||
AllowOrganizations []string
|
||||
AllowTeams []GithubOAuth2Team
|
||||
|
||||
DefaultProviderConfigured bool
|
||||
}
|
||||
|
||||
func (c *GithubOAuth2Config) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
||||
@ -806,7 +808,10 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) {
|
||||
Password: codersdk.AuthMethod{
|
||||
Enabled: !api.DeploymentValues.DisablePasswordAuth.Value(),
|
||||
},
|
||||
Github: codersdk.AuthMethod{Enabled: api.GithubOAuth2Config != nil},
|
||||
Github: codersdk.GithubAuthMethod{
|
||||
Enabled: api.GithubOAuth2Config != nil,
|
||||
DefaultProviderConfigured: api.GithubOAuth2Config != nil && api.GithubOAuth2Config.DefaultProviderConfigured,
|
||||
},
|
||||
OIDC: codersdk.OIDCAuthMethod{
|
||||
AuthMethod: codersdk.AuthMethod{Enabled: api.OIDCConfig != nil},
|
||||
SignInText: signInText,
|
||||
|
Reference in New Issue
Block a user