@@ -677,24 +677,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
677
677
}
678
678
}
679
679
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
-
698
680
// As OIDC clients can be confidential or public,
699
681
// we should only check for a client id being set.
700
682
// The underlying library handles the case of no
@@ -782,6 +764,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
782
764
return xerrors .Errorf ("set deployment id: %w" , err )
783
765
}
784
766
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
+
785
781
options .RuntimeConfig = runtimeconfig .NewManager ()
786
782
787
783
// This should be output before the logs start streaming.
@@ -1832,25 +1828,95 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
1832
1828
return nil
1833
1829
}
1834
1830
1835
- // TODO: convert the argument list to a struct, it's easy to mix up the order of the arguments
1836
- //
1831
+ type githubOAuth2ConfigParams struct {
1832
+ accessURL * url.URL
1833
+ clientID string
1834
+ clientSecret string
1835
+ deviceFlow bool
1836
+ allowSignups bool
1837
+ allowEveryone bool
1838
+ allowOrgs []string
1839
+ rawTeams []string
1840
+ enterpriseBaseURL string
1841
+ }
1842
+
1843
+ func getGithubOAuth2ConfigParams (ctx context.Context , db database.Store , vals * codersdk.DeploymentValues ) (* githubOAuth2ConfigParams , error ) {
1844
+ params := githubOAuth2ConfigParams {
1845
+ accessURL : vals .AccessURL .Value (),
1846
+ clientID : vals .OAuth2 .Github .ClientID .String (),
1847
+ clientSecret : vals .OAuth2 .Github .ClientSecret .String (),
1848
+ deviceFlow : vals .OAuth2 .Github .DeviceFlow .Value (),
1849
+ allowSignups : vals .OAuth2 .Github .AllowSignups .Value (),
1850
+ allowEveryone : vals .OAuth2 .Github .AllowEveryone .Value (),
1851
+ allowOrgs : vals .OAuth2 .Github .AllowedOrgs .Value (),
1852
+ rawTeams : vals .OAuth2 .Github .AllowedTeams .Value (),
1853
+ enterpriseBaseURL : vals .OAuth2 .Github .EnterpriseBaseURL .String (),
1854
+ }
1855
+
1856
+ // If the user manually configured the GitHub OAuth2 provider,
1857
+ // we won't add the default configuration.
1858
+ if params .clientID != "" || params .clientSecret != "" || params .enterpriseBaseURL != "" {
1859
+ return & params , nil
1860
+ }
1861
+
1862
+ // Check if the user manually disabled the default GitHub OAuth2 provider.
1863
+ if ! vals .OAuth2 .Github .DefaultProvider .Value () {
1864
+ return nil , nil //nolint:nilnil
1865
+ }
1866
+
1867
+ // Check if the deployment is eligible for the default GitHub OAuth2 provider.
1868
+ // We want to enable it only for new deployments, and avoid enabling it
1869
+ // if a deployment was upgraded from an older version.
1870
+ // nolint:gocritic // Requires system privileges
1871
+ defaultEligible , err := db .GetOAuth2GithubDefaultEligible (dbauthz .AsSystemRestricted (ctx ))
1872
+ if err != nil && ! errors .Is (err , sql .ErrNoRows ) {
1873
+ return nil , xerrors .Errorf ("get github default eligible: %w" , err )
1874
+ }
1875
+ defaultEligibleNotSet := errors .Is (err , sql .ErrNoRows )
1876
+
1877
+ if defaultEligibleNotSet {
1878
+ // nolint:gocritic // User count requires system privileges
1879
+ userCount , err := db .GetUserCount (dbauthz .AsSystemRestricted (ctx ))
1880
+ if err != nil {
1881
+ return nil , xerrors .Errorf ("get user count: %w" , err )
1882
+ }
1883
+ // We check if a deployment is new by checking if it has any users.
1884
+ defaultEligible = userCount == 0
1885
+ // nolint:gocritic // Requires system privileges
1886
+ if err := db .UpsertOAuth2GithubDefaultEligible (dbauthz .AsSystemRestricted (ctx ), defaultEligible ); err != nil {
1887
+ return nil , xerrors .Errorf ("upsert github default eligible: %w" , err )
1888
+ }
1889
+ }
1890
+
1891
+ if ! defaultEligible {
1892
+ return nil , nil //nolint:nilnil
1893
+ }
1894
+
1895
+ // TODO: before merging, change this to use Coder's client ID instead of my dev one.
1896
+ params .clientID = "Iv23liSBHklRMBNx5lk9"
1897
+ params .allowEveryone = true
1898
+ params .deviceFlow = true
1899
+
1900
+ return & params , nil
1901
+ }
1902
+
1837
1903
//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" )
1904
+ func configureGithubOAuth2 (instrument * promoauth.Factory , params * githubOAuth2ConfigParams ) (* coderd.GithubOAuth2Config , error ) {
1905
+ redirectURL , err := params . accessURL .Parse ("/api/v2/users/oauth2/github/callback" )
1840
1906
if err != nil {
1841
1907
return nil , xerrors .Errorf ("parse github oauth callback url: %w" , err )
1842
1908
}
1843
- if allowEveryone && len (allowOrgs ) > 0 {
1909
+ if params . allowEveryone && len (params . allowOrgs ) > 0 {
1844
1910
return nil , xerrors .New ("allow everyone and allowed orgs cannot be used together" )
1845
1911
}
1846
- if allowEveryone && len (rawTeams ) > 0 {
1912
+ if params . allowEveryone && len (params . rawTeams ) > 0 {
1847
1913
return nil , xerrors .New ("allow everyone and allowed teams cannot be used together" )
1848
1914
}
1849
- if ! allowEveryone && len (allowOrgs ) == 0 {
1915
+ if ! params . allowEveryone && len (params . allowOrgs ) == 0 {
1850
1916
return nil , xerrors .New ("allowed orgs is empty: must specify at least one org or allow everyone" )
1851
1917
}
1852
- allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (rawTeams ))
1853
- for _ , rawTeam := range rawTeams {
1918
+ allowTeams := make ([]coderd.GithubOAuth2Team , 0 , len (params . rawTeams ))
1919
+ for _ , rawTeam := range params . rawTeams {
1854
1920
parts := strings .SplitN (rawTeam , "/" , 2 )
1855
1921
if len (parts ) != 2 {
1856
1922
return nil , xerrors .Errorf ("github team allowlist is formatted incorrectly. got %s; wanted <organization>/<team>" , rawTeam )
@@ -1862,8 +1928,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1862
1928
}
1863
1929
1864
1930
endpoint := xgithub .Endpoint
1865
- if enterpriseBaseURL != "" {
1866
- enterpriseURL , err := url .Parse (enterpriseBaseURL )
1931
+ if params . enterpriseBaseURL != "" {
1932
+ enterpriseURL , err := url .Parse (params . enterpriseBaseURL )
1867
1933
if err != nil {
1868
1934
return nil , xerrors .Errorf ("parse enterprise base url: %w" , err )
1869
1935
}
@@ -1882,8 +1948,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1882
1948
}
1883
1949
1884
1950
instrumentedOauth := instrument .NewGithub ("github-login" , & oauth2.Config {
1885
- ClientID : clientID ,
1886
- ClientSecret : clientSecret ,
1951
+ ClientID : params . clientID ,
1952
+ ClientSecret : params . clientSecret ,
1887
1953
Endpoint : endpoint ,
1888
1954
RedirectURL : redirectURL .String (),
1889
1955
Scopes : []string {
@@ -1895,17 +1961,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1895
1961
1896
1962
createClient := func (client * http.Client , source promoauth.Oauth2Source ) (* github.Client , error ) {
1897
1963
client = instrumentedOauth .InstrumentHTTPClient (client , source )
1898
- if enterpriseBaseURL != "" {
1899
- return github .NewEnterpriseClient (enterpriseBaseURL , "" , client )
1964
+ if params . enterpriseBaseURL != "" {
1965
+ return github .NewEnterpriseClient (params . enterpriseBaseURL , "" , client )
1900
1966
}
1901
1967
return github .NewClient (client ), nil
1902
1968
}
1903
1969
1904
1970
var deviceAuth * externalauth.DeviceAuth
1905
- if deviceFlow {
1971
+ if params . deviceFlow {
1906
1972
deviceAuth = & externalauth.DeviceAuth {
1907
1973
Config : instrumentedOauth ,
1908
- ClientID : clientID ,
1974
+ ClientID : params . clientID ,
1909
1975
TokenURL : endpoint .TokenURL ,
1910
1976
Scopes : []string {"read:user" , "read:org" , "user:email" },
1911
1977
CodeURL : endpoint .DeviceAuthURL ,
@@ -1914,9 +1980,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1914
1980
1915
1981
return & coderd.GithubOAuth2Config {
1916
1982
OAuth2Config : instrumentedOauth ,
1917
- AllowSignups : allowSignups ,
1918
- AllowEveryone : allowEveryone ,
1919
- AllowOrganizations : allowOrgs ,
1983
+ AllowSignups : params . allowSignups ,
1984
+ AllowEveryone : params . allowEveryone ,
1985
+ AllowOrganizations : params . allowOrgs ,
1920
1986
AllowTeams : allowTeams ,
1921
1987
AuthenticatedUser : func (ctx context.Context , client * http.Client ) (* github.User , error ) {
1922
1988
api , err := createClient (client , promoauth .SourceGitAPIAuthUser )
@@ -1955,15 +2021,15 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
1955
2021
team , _ , err := api .Teams .GetTeamMembershipBySlug (ctx , org , teamSlug , username )
1956
2022
return team , err
1957
2023
},
1958
- DeviceFlowEnabled : deviceFlow ,
2024
+ DeviceFlowEnabled : params . deviceFlow ,
1959
2025
ExchangeDeviceCode : func (ctx context.Context , deviceCode string ) (* oauth2.Token , error ) {
1960
- if ! deviceFlow {
2026
+ if ! params . deviceFlow {
1961
2027
return nil , xerrors .New ("device flow is not enabled" )
1962
2028
}
1963
2029
return deviceAuth .ExchangeDeviceCode (ctx , deviceCode )
1964
2030
},
1965
2031
AuthorizeDevice : func (ctx context.Context ) (* codersdk.ExternalAuthDevice , error ) {
1966
- if ! deviceFlow {
2032
+ if ! params . deviceFlow {
1967
2033
return nil , xerrors .New ("device flow is not enabled" )
1968
2034
}
1969
2035
return deviceAuth .AuthorizeDevice (ctx )
0 commit comments