Skip to content

Commit a8f6869

Browse files
committed
server
1 parent e26e6cb commit a8f6869

File tree

2 files changed

+120
-42
lines changed

2 files changed

+120
-42
lines changed

cli/server.go

Lines changed: 114 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -677,24 +677,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
677677
}
678678
}
679679

680-
if vals.OAuth2.Github.ClientSecret != "" || vals.OAuth2.Github.DeviceFlow.Value() {
681-
options.GithubOAuth2Config, err = configureGithubOAuth2(
682-
oauthInstrument,
683-
vals.AccessURL.Value(),
684-
vals.OAuth2.Github.ClientID.String(),
685-
vals.OAuth2.Github.ClientSecret.String(),
686-
vals.OAuth2.Github.DeviceFlow.Value(),
687-
vals.OAuth2.Github.AllowSignups.Value(),
688-
vals.OAuth2.Github.AllowEveryone.Value(),
689-
vals.OAuth2.Github.AllowedOrgs,
690-
vals.OAuth2.Github.AllowedTeams,
691-
vals.OAuth2.Github.EnterpriseBaseURL.String(),
692-
)
693-
if err != nil {
694-
return xerrors.Errorf("configure github oauth2: %w", err)
695-
}
696-
}
697-
698680
// As OIDC clients can be confidential or public,
699681
// we should only check for a client id being set.
700682
// The underlying library handles the case of no
@@ -782,6 +764,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
782764
return xerrors.Errorf("set deployment id: %w", err)
783765
}
784766

767+
githubOAuth2ConfigParams, err := getGithubOAuth2ConfigParams(ctx, options.Database, vals)
768+
if err != nil {
769+
return xerrors.Errorf("get github oauth2 config params: %w", err)
770+
}
771+
if githubOAuth2ConfigParams != nil {
772+
options.GithubOAuth2Config, err = configureGithubOAuth2(
773+
oauthInstrument,
774+
githubOAuth2ConfigParams,
775+
)
776+
if err != nil {
777+
return xerrors.Errorf("configure github oauth2: %w", err)
778+
}
779+
}
780+
785781
options.RuntimeConfig = runtimeconfig.NewManager()
786782

787783
// This should be output before the logs start streaming.
@@ -1832,25 +1828,101 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
18321828
return nil
18331829
}
18341830

1835-
// TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
1836-
//
1831+
const (
1832+
// Client ID for https://github.com/apps/coder
1833+
GithubOAuth2DefaultProviderClientID = "Iv1.6a2b4b4aec4f4fe7"
1834+
GithubOAuth2DefaultProviderAllowEveryone = true
1835+
GithubOAuth2DefaultProviderDeviceFlow = true
1836+
)
1837+
1838+
type githubOAuth2ConfigParams struct {
1839+
accessURL *url.URL
1840+
clientID string
1841+
clientSecret string
1842+
deviceFlow bool
1843+
allowSignups bool
1844+
allowEveryone bool
1845+
allowOrgs []string
1846+
rawTeams []string
1847+
enterpriseBaseURL string
1848+
}
1849+
1850+
func getGithubOAuth2ConfigParams(ctx context.Context, db database.Store, vals *codersdk.DeploymentValues) (*githubOAuth2ConfigParams, error) {
1851+
params := githubOAuth2ConfigParams{
1852+
accessURL: vals.AccessURL.Value(),
1853+
clientID: vals.OAuth2.Github.ClientID.String(),
1854+
clientSecret: vals.OAuth2.Github.ClientSecret.String(),
1855+
deviceFlow: vals.OAuth2.Github.DeviceFlow.Value(),
1856+
allowSignups: vals.OAuth2.Github.AllowSignups.Value(),
1857+
allowEveryone: vals.OAuth2.Github.AllowEveryone.Value(),
1858+
allowOrgs: vals.OAuth2.Github.AllowedOrgs.Value(),
1859+
rawTeams: vals.OAuth2.Github.AllowedTeams.Value(),
1860+
enterpriseBaseURL: vals.OAuth2.Github.EnterpriseBaseURL.String(),
1861+
}
1862+
1863+
// If the user manually configured the GitHub OAuth2 provider,
1864+
// we won't add the default configuration.
1865+
if params.clientID != "" || params.clientSecret != "" || params.enterpriseBaseURL != "" {
1866+
return &params, nil
1867+
}
1868+
1869+
// Check if the user manually disabled the default GitHub OAuth2 provider.
1870+
if !vals.OAuth2.Github.DefaultProviderEnable.Value() {
1871+
return nil, nil //nolint:nilnil
1872+
}
1873+
1874+
// Check if the deployment is eligible for the default GitHub OAuth2 provider.
1875+
// We want to enable it only for new deployments, and avoid enabling it
1876+
// if a deployment was upgraded from an older version.
1877+
// nolint:gocritic // Requires system privileges
1878+
defaultEligible, err := db.GetOAuth2GithubDefaultEligible(dbauthz.AsSystemRestricted(ctx))
1879+
if err != nil && !errors.Is(err, sql.ErrNoRows) {
1880+
return nil, xerrors.Errorf("get github default eligible: %w", err)
1881+
}
1882+
defaultEligibleNotSet := errors.Is(err, sql.ErrNoRows)
1883+
1884+
if defaultEligibleNotSet {
1885+
// nolint:gocritic // User count requires system privileges
1886+
userCount, err := db.GetUserCount(dbauthz.AsSystemRestricted(ctx))
1887+
if err != nil {
1888+
return nil, xerrors.Errorf("get user count: %w", err)
1889+
}
1890+
// We check if a deployment is new by checking if it has any users.
1891+
defaultEligible = userCount == 0
1892+
// nolint:gocritic // Requires system privileges
1893+
if err := db.UpsertOAuth2GithubDefaultEligible(dbauthz.AsSystemRestricted(ctx), defaultEligible); err != nil {
1894+
return nil, xerrors.Errorf("upsert github default eligible: %w", err)
1895+
}
1896+
}
1897+
1898+
if !defaultEligible {
1899+
return nil, nil //nolint:nilnil
1900+
}
1901+
1902+
params.clientID = GithubOAuth2DefaultProviderClientID
1903+
params.allowEveryone = GithubOAuth2DefaultProviderAllowEveryone
1904+
params.deviceFlow = GithubOAuth2DefaultProviderDeviceFlow
1905+
1906+
return &params, nil
1907+
}
1908+
18371909
//nolint:revive // Ignore flag-parameter: parameter 'allowEveryone' seems to be a control flag, avoid control coupling (revive)
1838-
func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, clientID, clientSecret string, deviceFlow, allowSignups, allowEveryone bool, allowOrgs []string, rawTeams []string, enterpriseBaseURL string) (*coderd.GithubOAuth2Config, error) {
1839-
redirectURL, err := accessURL.Parse("/api/v2/users/oauth2/github/callback")
1910+
func configureGithubOAuth2(instrument *promoauth.Factory, params *githubOAuth2ConfigParams) (*coderd.GithubOAuth2Config, error) {
1911+
redirectURL, err := params.accessURL.Parse("/api/v2/users/oauth2/github/callback")
18401912
if err != nil {
18411913
return nil, xerrors.Errorf("parse github oauth callback url: %w", err)
18421914
}
1843-
if allowEveryone && len(allowOrgs) > 0 {
1915+
if params.allowEveryone && len(params.allowOrgs) > 0 {
18441916
return nil, xerrors.New("allow everyone and allowed orgs cannot be used together")
18451917
}
1846-
if allowEveryone && len(rawTeams) > 0 {
1918+
if params.allowEveryone && len(params.rawTeams) > 0 {
18471919
return nil, xerrors.New("allow everyone and allowed teams cannot be used together")
18481920
}
1849-
if !allowEveryone && len(allowOrgs) == 0 {
1921+
if !params.allowEveryone && len(params.allowOrgs) == 0 {
18501922
return nil, xerrors.New("allowed orgs is empty: must specify at least one org or allow everyone")
18511923
}
1852-
allowTeams := make([]coderd.GithubOAuth2Team, 0, len(rawTeams))
1853-
for _, rawTeam := range rawTeams {
1924+
allowTeams := make([]coderd.GithubOAuth2Team, 0, len(params.rawTeams))
1925+
for _, rawTeam := range params.rawTeams {
18541926
parts := strings.SplitN(rawTeam, "/", 2)
18551927
if len(parts) != 2 {
18561928
return nil, xerrors.Errorf("github team allowlist is formatted incorrectly. got %s; wanted <organization>/<team>", rawTeam)
@@ -1862,8 +1934,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
18621934
}
18631935

18641936
endpoint := xgithub.Endpoint
1865-
if enterpriseBaseURL != "" {
1866-
enterpriseURL, err := url.Parse(enterpriseBaseURL)
1937+
if params.enterpriseBaseURL != "" {
1938+
enterpriseURL, err := url.Parse(params.enterpriseBaseURL)
18671939
if err != nil {
18681940
return nil, xerrors.Errorf("parse enterprise base url: %w", err)
18691941
}
@@ -1882,8 +1954,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
18821954
}
18831955

18841956
instrumentedOauth := instrument.NewGithub("github-login", &oauth2.Config{
1885-
ClientID: clientID,
1886-
ClientSecret: clientSecret,
1957+
ClientID: params.clientID,
1958+
ClientSecret: params.clientSecret,
18871959
Endpoint: endpoint,
18881960
RedirectURL: redirectURL.String(),
18891961
Scopes: []string{
@@ -1895,17 +1967,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
18951967

18961968
createClient := func(client *http.Client, source promoauth.Oauth2Source) (*github.Client, error) {
18971969
client = instrumentedOauth.InstrumentHTTPClient(client, source)
1898-
if enterpriseBaseURL != "" {
1899-
return github.NewEnterpriseClient(enterpriseBaseURL, "", client)
1970+
if params.enterpriseBaseURL != "" {
1971+
return github.NewEnterpriseClient(params.enterpriseBaseURL, "", client)
19001972
}
19011973
return github.NewClient(client), nil
19021974
}
19031975

19041976
var deviceAuth *externalauth.DeviceAuth
1905-
if deviceFlow {
1977+
if params.deviceFlow {
19061978
deviceAuth = &externalauth.DeviceAuth{
19071979
Config: instrumentedOauth,
1908-
ClientID: clientID,
1980+
ClientID: params.clientID,
19091981
TokenURL: endpoint.TokenURL,
19101982
Scopes: []string{"read:user", "read:org", "user:email"},
19111983
CodeURL: endpoint.DeviceAuthURL,
@@ -1914,9 +1986,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
19141986

19151987
return &coderd.GithubOAuth2Config{
19161988
OAuth2Config: instrumentedOauth,
1917-
AllowSignups: allowSignups,
1918-
AllowEveryone: allowEveryone,
1919-
AllowOrganizations: allowOrgs,
1989+
AllowSignups: params.allowSignups,
1990+
AllowEveryone: params.allowEveryone,
1991+
AllowOrganizations: params.allowOrgs,
19201992
AllowTeams: allowTeams,
19211993
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
19221994
api, err := createClient(client, promoauth.SourceGitAPIAuthUser)
@@ -1955,19 +2027,20 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
19552027
team, _, err := api.Teams.GetTeamMembershipBySlug(ctx, org, teamSlug, username)
19562028
return team, err
19572029
},
1958-
DeviceFlowEnabled: deviceFlow,
2030+
DeviceFlowEnabled: params.deviceFlow,
19592031
ExchangeDeviceCode: func(ctx context.Context, deviceCode string) (*oauth2.Token, error) {
1960-
if !deviceFlow {
2032+
if !params.deviceFlow {
19612033
return nil, xerrors.New("device flow is not enabled")
19622034
}
19632035
return deviceAuth.ExchangeDeviceCode(ctx, deviceCode)
19642036
},
19652037
AuthorizeDevice: func(ctx context.Context) (*codersdk.ExternalAuthDevice, error) {
1966-
if !deviceFlow {
2038+
if !params.deviceFlow {
19672039
return nil, xerrors.New("device flow is not enabled")
19682040
}
19692041
return deviceAuth.AuthorizeDevice(ctx)
19702042
},
2043+
DefaultProviderConfigured: params.clientID == GithubOAuth2DefaultProviderClientID,
19712044
}, nil
19722045
}
19732046

coderd/userauth.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -757,6 +757,8 @@ type GithubOAuth2Config struct {
757757
AllowEveryone bool
758758
AllowOrganizations []string
759759
AllowTeams []GithubOAuth2Team
760+
761+
DefaultProviderConfigured bool
760762
}
761763

762764
func (c *GithubOAuth2Config) Exchange(ctx context.Context, code string, opts ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
@@ -798,7 +800,10 @@ func (api *API) userAuthMethods(rw http.ResponseWriter, r *http.Request) {
798800
Password: codersdk.AuthMethod{
799801
Enabled: !api.DeploymentValues.DisablePasswordAuth.Value(),
800802
},
801-
Github: codersdk.AuthMethod{Enabled: api.GithubOAuth2Config != nil},
803+
Github: codersdk.GithubAuthMethod{
804+
Enabled: api.GithubOAuth2Config != nil,
805+
DefaultProviderConfigured: api.GithubOAuth2Config != nil && api.GithubOAuth2Config.DefaultProviderConfigured,
806+
},
802807
OIDC: codersdk.OIDCAuthMethod{
803808
AuthMethod: codersdk.AuthMethod{Enabled: api.OIDCConfig != nil},
804809
SignInText: signInText,

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy