Skip to content

Commit 3a2f2b0

Browse files
committed
Merge branch 'main' into lilac/some-org-scopes-roles-e2e-tests
2 parents a9b68f2 + a322339 commit 3a2f2b0

File tree

109 files changed

+2526
-906
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

109 files changed

+2526
-906
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ endif
116116

117117
clean:
118118
rm -rf build/ site/build/ site/out/
119-
mkdir -p build/ site/out/bin/
119+
mkdir -p build/
120120
git restore site/out/
121121
.PHONY: clean
122122

cli/gitauth/vscode.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,14 @@ func OverrideVSCodeConfigs(fs afero.Fs) error {
3232
filepath.Join(xdg.DataHome, "code-server", "Machine", "settings.json"),
3333
// vscode-remote's default configuration path.
3434
filepath.Join(home, ".vscode-server", "data", "Machine", "settings.json"),
35+
// vscode-insiders' default configuration path.
36+
filepath.Join(home, ".vscode-insiders-server", "data", "Machine", "settings.json"),
37+
// cursor default configuration path.
38+
filepath.Join(home, ".cursor-server", "data", "Machine", "settings.json"),
39+
// windsurf default configuration path.
40+
filepath.Join(home, ".windsurf-server", "data", "Machine", "settings.json"),
41+
// vscodium default configuration path.
42+
filepath.Join(home, ".vscodium-server", "data", "Machine", "settings.json"),
3543
} {
3644
_, err := fs.Stat(configPath)
3745
if err != nil {

cli/root.go

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1213,9 +1213,14 @@ func wrapTransportWithVersionMismatchCheck(rt http.RoundTripper, inv *serpent.In
12131213
return
12141214
}
12151215
upgradeMessage := defaultUpgradeMessage(semver.Canonical(serverVersion))
1216-
serverInfo, err := getBuildInfo(inv.Context())
1217-
if err == nil && serverInfo.UpgradeMessage != "" {
1218-
upgradeMessage = serverInfo.UpgradeMessage
1216+
if serverInfo, err := getBuildInfo(inv.Context()); err == nil {
1217+
switch {
1218+
case serverInfo.UpgradeMessage != "":
1219+
upgradeMessage = serverInfo.UpgradeMessage
1220+
// The site-local `install.sh` was introduced in v2.19.0
1221+
case serverInfo.DashboardURL != "" && semver.Compare(semver.MajorMinor(serverVersion), "v2.19") >= 0:
1222+
upgradeMessage = fmt.Sprintf("download %s with: 'curl -fsSL %s/install.sh | sh'", serverVersion, serverInfo.DashboardURL)
1223+
}
12191224
}
12201225
fmtWarningText := "version mismatch: client %s, server %s\n%s"
12211226
fmtWarn := pretty.Sprint(cliui.DefaultStyles.Warn, fmtWarningText)

cli/server.go

Lines changed: 126 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -172,6 +172,17 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
172172
groupAllowList[group] = true
173173
}
174174

175+
secondaryClaimsSrc := coderd.MergedClaimsSourceUserInfo
176+
if !vals.OIDC.IgnoreUserInfo && vals.OIDC.UserInfoFromAccessToken {
177+
return nil, xerrors.Errorf("to use 'oidc-access-token-claims', 'oidc-ignore-userinfo' must be set to 'false'")
178+
}
179+
if vals.OIDC.IgnoreUserInfo {
180+
secondaryClaimsSrc = coderd.MergedClaimsSourceNone
181+
}
182+
if vals.OIDC.UserInfoFromAccessToken {
183+
secondaryClaimsSrc = coderd.MergedClaimsSourceAccessToken
184+
}
185+
175186
return &coderd.OIDCConfig{
176187
OAuth2Config: useCfg,
177188
Provider: oidcProvider,
@@ -187,7 +198,7 @@ func createOIDCConfig(ctx context.Context, logger slog.Logger, vals *codersdk.De
187198
NameField: vals.OIDC.NameField.String(),
188199
EmailField: vals.OIDC.EmailField.String(),
189200
AuthURLParams: vals.OIDC.AuthURLParams.Value,
190-
IgnoreUserInfo: vals.OIDC.IgnoreUserInfo.Value(),
201+
SecondaryClaims: secondaryClaimsSrc,
191202
SignInText: vals.OIDC.SignInText.String(),
192203
SignupsDisabledText: vals.OIDC.SignupsDisabledText.String(),
193204
IconURL: vals.OIDC.IconURL.String(),
@@ -677,24 +688,6 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
677688
}
678689
}
679690

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-
698691
// As OIDC clients can be confidential or public,
699692
// we should only check for a client id being set.
700693
// The underlying library handles the case of no
@@ -782,6 +775,20 @@ func (r *RootCmd) Server(newAPI func(context.Context, *coderd.Options) (*coderd.
782775
return xerrors.Errorf("set deployment id: %w", err)
783776
}
784777

778+
githubOAuth2ConfigParams, err := getGithubOAuth2ConfigParams(ctx, options.Database, vals)
779+
if err != nil {
780+
return xerrors.Errorf("get github oauth2 config params: %w", err)
781+
}
782+
if githubOAuth2ConfigParams != nil {
783+
options.GithubOAuth2Config, err = configureGithubOAuth2(
784+
oauthInstrument,
785+
githubOAuth2ConfigParams,
786+
)
787+
if err != nil {
788+
return xerrors.Errorf("configure github oauth2: %w", err)
789+
}
790+
}
791+
785792
options.RuntimeConfig = runtimeconfig.NewManager()
786793

787794
// This should be output before the logs start streaming.
@@ -1832,25 +1839,101 @@ func configureCAPool(tlsClientCAFile string, tlsConfig *tls.Config) error {
18321839
return nil
18331840
}
18341841

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

18641947
endpoint := xgithub.Endpoint
1865-
if enterpriseBaseURL != "" {
1866-
enterpriseURL, err := url.Parse(enterpriseBaseURL)
1948+
if params.enterpriseBaseURL != "" {
1949+
enterpriseURL, err := url.Parse(params.enterpriseBaseURL)
18671950
if err != nil {
18681951
return nil, xerrors.Errorf("parse enterprise base url: %w", err)
18691952
}
@@ -1882,8 +1965,8 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
18821965
}
18831966

18841967
instrumentedOauth := instrument.NewGithub("github-login", &oauth2.Config{
1885-
ClientID: clientID,
1886-
ClientSecret: clientSecret,
1968+
ClientID: params.clientID,
1969+
ClientSecret: params.clientSecret,
18871970
Endpoint: endpoint,
18881971
RedirectURL: redirectURL.String(),
18891972
Scopes: []string{
@@ -1895,17 +1978,17 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
18951978

18961979
createClient := func(client *http.Client, source promoauth.Oauth2Source) (*github.Client, error) {
18971980
client = instrumentedOauth.InstrumentHTTPClient(client, source)
1898-
if enterpriseBaseURL != "" {
1899-
return github.NewEnterpriseClient(enterpriseBaseURL, "", client)
1981+
if params.enterpriseBaseURL != "" {
1982+
return github.NewEnterpriseClient(params.enterpriseBaseURL, "", client)
19001983
}
19011984
return github.NewClient(client), nil
19021985
}
19031986

19041987
var deviceAuth *externalauth.DeviceAuth
1905-
if deviceFlow {
1988+
if params.deviceFlow {
19061989
deviceAuth = &externalauth.DeviceAuth{
19071990
Config: instrumentedOauth,
1908-
ClientID: clientID,
1991+
ClientID: params.clientID,
19091992
TokenURL: endpoint.TokenURL,
19101993
Scopes: []string{"read:user", "read:org", "user:email"},
19111994
CodeURL: endpoint.DeviceAuthURL,
@@ -1914,9 +1997,9 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
19141997

19151998
return &coderd.GithubOAuth2Config{
19161999
OAuth2Config: instrumentedOauth,
1917-
AllowSignups: allowSignups,
1918-
AllowEveryone: allowEveryone,
1919-
AllowOrganizations: allowOrgs,
2000+
AllowSignups: params.allowSignups,
2001+
AllowEveryone: params.allowEveryone,
2002+
AllowOrganizations: params.allowOrgs,
19202003
AllowTeams: allowTeams,
19212004
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
19222005
api, err := createClient(client, promoauth.SourceGitAPIAuthUser)
@@ -1955,19 +2038,20 @@ func configureGithubOAuth2(instrument *promoauth.Factory, accessURL *url.URL, cl
19552038
team, _, err := api.Teams.GetTeamMembershipBySlug(ctx, org, teamSlug, username)
19562039
return team, err
19572040
},
1958-
DeviceFlowEnabled: deviceFlow,
2041+
DeviceFlowEnabled: params.deviceFlow,
19592042
ExchangeDeviceCode: func(ctx context.Context, deviceCode string) (*oauth2.Token, error) {
1960-
if !deviceFlow {
2043+
if !params.deviceFlow {
19612044
return nil, xerrors.New("device flow is not enabled")
19622045
}
19632046
return deviceAuth.ExchangeDeviceCode(ctx, deviceCode)
19642047
},
19652048
AuthorizeDevice: func(ctx context.Context) (*codersdk.ExternalAuthDevice, error) {
1966-
if !deviceFlow {
2049+
if !params.deviceFlow {
19672050
return nil, xerrors.New("device flow is not enabled")
19682051
}
19692052
return deviceAuth.AuthorizeDevice(ctx)
19702053
},
2054+
DefaultProviderConfigured: params.clientID == GithubOAuth2DefaultProviderClientID,
19712055
}, nil
19722056
}
19732057

0 commit comments

Comments
 (0)