Skip to content

Commit eb005fa

Browse files
authored
Merge branch 'main' into ketang/template-delete-confirm
2 parents bdeec4b + daadb9a commit eb005fa

38 files changed

+574
-198
lines changed

.github/workflows/coder.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,7 @@ jobs:
388388
name: coder
389389
path: |
390390
./dist/*.zip
391+
./dist/*.exe
391392
./dist/*.tar.gz
392393
./dist/*.apk
393394
./dist/*.deb

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ test-postgres: test-clean test-postgres-docker
175175
DB=ci DB_FROM=$(shell go run scripts/migrate-ci/main.go) gotestsum --junitfile="gotests.xml" --packages="./..." -- \
176176
-covermode=atomic -coverprofile="gotests.coverage" -timeout=30m \
177177
-coverpkg=./...,github.com/coder/coder/codersdk \
178-
-count=2 -race -failfast
178+
-count=1 -race -failfast
179179
.PHONY: test-postgres
180180

181181
test-postgres-docker:

cli/cliflag/cliflag.go

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func StringArrayVarP(flagset *pflag.FlagSet, ptr *[]string, name string, shortha
4747
def = strings.Split(val, ",")
4848
}
4949
}
50-
flagset.StringArrayVarP(ptr, name, shorthand, def, usage)
50+
flagset.StringArrayVarP(ptr, name, shorthand, def, fmtUsage(usage, env))
5151
}
5252

5353
// Uint8VarP sets a uint8 flag on the given flag set.
@@ -102,9 +102,14 @@ func DurationVarP(flagset *pflag.FlagSet, ptr *time.Duration, name string, short
102102
}
103103

104104
func fmtUsage(u string, env string) string {
105-
if env == "" {
106-
return fmt.Sprintf("%s.", u)
105+
if env != "" {
106+
// Avoid double dotting.
107+
dot := "."
108+
if strings.HasSuffix(u, ".") {
109+
dot = ""
110+
}
111+
u = fmt.Sprintf("%s%s\nConsumes $%s", u, dot, env)
107112
}
108113

109-
return fmt.Sprintf("%s - consumes $%s.", u, env)
114+
return u
110115
}

cli/cliflag/cliflag_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func TestCliflag(t *testing.T) {
2424
require.NoError(t, err)
2525
require.Equal(t, def, got)
2626
require.Contains(t, flagset.FlagUsages(), usage)
27-
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf(" - consumes $%s", env))
27+
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf("Consumes $%s", env))
2828
})
2929

3030
t.Run("StringEnvVar", func(t *testing.T) {
@@ -48,7 +48,7 @@ func TestCliflag(t *testing.T) {
4848
require.NoError(t, err)
4949
require.Equal(t, def, got)
5050
require.Contains(t, flagset.FlagUsages(), usage)
51-
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf(" - consumes $%s", env))
51+
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf("Consumes $%s", env))
5252
})
5353

5454
t.Run("StringVarPEnvVar", func(t *testing.T) {
@@ -74,7 +74,7 @@ func TestCliflag(t *testing.T) {
7474
require.NoError(t, err)
7575
require.Equal(t, def, got)
7676
require.Contains(t, flagset.FlagUsages(), usage)
77-
require.NotContains(t, flagset.FlagUsages(), " - consumes")
77+
require.NotContains(t, flagset.FlagUsages(), "Consumes")
7878
})
7979

8080
t.Run("StringArrayDefault", func(t *testing.T) {
@@ -117,7 +117,7 @@ func TestCliflag(t *testing.T) {
117117
require.NoError(t, err)
118118
require.Equal(t, uint8(def), got)
119119
require.Contains(t, flagset.FlagUsages(), usage)
120-
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf(" - consumes $%s", env))
120+
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf("Consumes $%s", env))
121121
})
122122

123123
t.Run("IntEnvVar", func(t *testing.T) {
@@ -156,7 +156,7 @@ func TestCliflag(t *testing.T) {
156156
require.NoError(t, err)
157157
require.Equal(t, def, got)
158158
require.Contains(t, flagset.FlagUsages(), usage)
159-
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf(" - consumes $%s", env))
159+
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf("Consumes $%s", env))
160160
})
161161

162162
t.Run("BoolEnvVar", func(t *testing.T) {
@@ -195,7 +195,7 @@ func TestCliflag(t *testing.T) {
195195
require.NoError(t, err)
196196
require.Equal(t, def, got)
197197
require.Contains(t, flagset.FlagUsages(), usage)
198-
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf(" - consumes $%s", env))
198+
require.Contains(t, flagset.FlagUsages(), fmt.Sprintf("Consumes $%s", env))
199199
})
200200

201201
t.Run("DurationEnvVar", func(t *testing.T) {

cli/cliui/cliui.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ var Styles = struct {
2626
Checkmark,
2727
Code,
2828
Crossmark,
29+
DateTimeStamp,
2930
Error,
3031
Field,
3132
Keyword,
@@ -42,6 +43,7 @@ var Styles = struct {
4243
Checkmark: defaultStyles.Checkmark,
4344
Code: defaultStyles.Code,
4445
Crossmark: defaultStyles.Error.Copy().SetString("✘"),
46+
DateTimeStamp: defaultStyles.LabelDim,
4547
Error: defaultStyles.Error,
4648
Field: defaultStyles.Code.Copy().Foreground(lipgloss.AdaptiveColor{Light: "#000000", Dark: "#FFFFFF"}),
4749
Keyword: defaultStyles.Keyword,

cli/create.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -155,7 +155,7 @@ func create() *cobra.Command {
155155
return err
156156
}
157157

158-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been created!\n", cliui.Styles.Keyword.Render(workspace.Name))
158+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been created at %s!\n", cliui.Styles.Keyword.Render(workspace.Name), cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp)))
159159
return nil
160160
},
161161
}

cli/delete.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ func deleteWorkspace() *cobra.Command {
4848
return err
4949
}
5050

51-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been deleted!\n", cliui.Styles.Keyword.Render(workspace.Name))
51+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been deleted at %s!\n", cliui.Styles.Keyword.Render(workspace.Name), cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp)))
5252
return nil
5353
},
5454
}

cli/logout_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ func TestLogout(t *testing.T) {
4141
assert.NoFileExists(t, string(config.Session()))
4242
}()
4343

44-
pty.ExpectMatch("Are you sure you want to logout?")
44+
pty.ExpectMatch("Are you sure you want to log out?")
4545
pty.WriteLine("yes")
4646
pty.ExpectMatch("You are no longer logged in. You can log in using 'coder login <url>'.")
4747
<-logoutChan
@@ -186,7 +186,7 @@ func TestLogout(t *testing.T) {
186186
assert.Regexp(t, errRegex, err.Error())
187187
}()
188188

189-
pty.ExpectMatch("Are you sure you want to logout?")
189+
pty.ExpectMatch("Are you sure you want to log out?")
190190
pty.WriteLine("yes")
191191
<-logoutChan
192192
})

cli/root.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -348,12 +348,12 @@ func usageTemplate() string {
348348
349349
{{- if .HasAvailableLocalFlags}}
350350
{{usageHeader "Flags:"}}
351-
{{.LocalFlags.FlagUsages | trimTrailingWhitespaces}}
351+
{{.LocalFlags.FlagUsagesWrapped 100}}
352352
{{end}}
353353
354354
{{- if .HasAvailableInheritedFlags}}
355355
{{usageHeader "Global Flags:"}}
356-
{{.InheritedFlags.FlagUsages | trimTrailingWhitespaces}}
356+
{{.InheritedFlags.FlagUsagesWrapped 100}}
357357
{{end}}
358358
359359
{{- if .HasHelpSubCommands}}

cli/server.go

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ func server() *cobra.Command {
8282
oauth2GithubClientID string
8383
oauth2GithubClientSecret string
8484
oauth2GithubAllowedOrganizations []string
85+
oauth2GithubAllowedTeams []string
8586
oauth2GithubAllowSignups bool
8687
telemetryEnable bool
8788
telemetryURL string
@@ -264,7 +265,7 @@ func server() *cobra.Command {
264265
}
265266

266267
if oauth2GithubClientSecret != "" {
267-
options.GithubOAuth2Config, err = configureGithubOAuth2(accessURLParsed, oauth2GithubClientID, oauth2GithubClientSecret, oauth2GithubAllowSignups, oauth2GithubAllowedOrganizations)
268+
options.GithubOAuth2Config, err = configureGithubOAuth2(accessURLParsed, oauth2GithubClientID, oauth2GithubClientSecret, oauth2GithubAllowSignups, oauth2GithubAllowedOrganizations, oauth2GithubAllowedTeams)
268269
if err != nil {
269270
return xerrors.Errorf("configure github oauth2: %w", err)
270271
}
@@ -535,6 +536,8 @@ func server() *cobra.Command {
535536
"Specifies a client secret to use for oauth2 with GitHub.")
536537
cliflag.StringArrayVarP(root.Flags(), &oauth2GithubAllowedOrganizations, "oauth2-github-allowed-orgs", "", "CODER_OAUTH2_GITHUB_ALLOWED_ORGS", nil,
537538
"Specifies organizations the user must be a member of to authenticate with GitHub.")
539+
cliflag.StringArrayVarP(root.Flags(), &oauth2GithubAllowedTeams, "oauth2-github-allowed-teams", "", "CODER_OAUTH2_GITHUB_ALLOWED_TEAMS", nil,
540+
"Specifies teams inside organizations the user must be a member of to authenticate with GitHub. Formatted as: <organization-name>/<team-slug>.")
538541
cliflag.BoolVarP(root.Flags(), &oauth2GithubAllowSignups, "oauth2-github-allow-signups", "", "CODER_OAUTH2_GITHUB_ALLOW_SIGNUPS", false,
539542
"Specifies whether new users can sign up with GitHub.")
540543
cliflag.BoolVarP(root.Flags(), &telemetryEnable, "telemetry", "", "CODER_TELEMETRY", true, "Specifies whether telemetry is enabled or not. Coder collects anonymized usage data to help improve our product.")
@@ -719,11 +722,22 @@ func configureTLS(listener net.Listener, tlsMinVersion, tlsClientAuth, tlsCertFi
719722
return tls.NewListener(listener, tlsConfig), nil
720723
}
721724

722-
func configureGithubOAuth2(accessURL *url.URL, clientID, clientSecret string, allowSignups bool, allowOrgs []string) (*coderd.GithubOAuth2Config, error) {
725+
func configureGithubOAuth2(accessURL *url.URL, clientID, clientSecret string, allowSignups bool, allowOrgs []string, rawTeams []string) (*coderd.GithubOAuth2Config, error) {
723726
redirectURL, err := accessURL.Parse("/api/v2/users/oauth2/github/callback")
724727
if err != nil {
725728
return nil, xerrors.Errorf("parse github oauth callback url: %w", err)
726729
}
730+
allowTeams := make([]coderd.GithubOAuth2Team, 0, len(rawTeams))
731+
for _, rawTeam := range rawTeams {
732+
parts := strings.SplitN(rawTeam, "/", 2)
733+
if len(parts) != 2 {
734+
return nil, xerrors.Errorf("github team allowlist is formatted incorrectly. got %s; wanted <organization>/<team>", rawTeam)
735+
}
736+
allowTeams = append(allowTeams, coderd.GithubOAuth2Team{
737+
Organization: parts[0],
738+
Slug: parts[1],
739+
})
740+
}
727741
return &coderd.GithubOAuth2Config{
728742
OAuth2Config: &oauth2.Config{
729743
ClientID: clientID,
@@ -738,6 +752,7 @@ func configureGithubOAuth2(accessURL *url.URL, clientID, clientSecret string, al
738752
},
739753
AllowSignups: allowSignups,
740754
AllowOrganizations: allowOrgs,
755+
AllowTeams: allowTeams,
741756
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
742757
user, _, err := github.NewClient(client).Users.Get(ctx, "")
743758
return user, err
@@ -749,9 +764,18 @@ func configureGithubOAuth2(accessURL *url.URL, clientID, clientSecret string, al
749764
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
750765
memberships, _, err := github.NewClient(client).Organizations.ListOrgMemberships(ctx, &github.ListOrgMembershipsOptions{
751766
State: "active",
767+
ListOptions: github.ListOptions{
768+
PerPage: 100,
769+
},
752770
})
753771
return memberships, err
754772
},
773+
ListTeams: func(ctx context.Context, client *http.Client, org string) ([]*github.Team, error) {
774+
teams, _, err := github.NewClient(client).Teams.ListTeams(ctx, org, &github.ListOptions{
775+
PerPage: 100,
776+
})
777+
return teams, err
778+
},
755779
}, nil
756780
}
757781

cli/start.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func start() *cobra.Command {
4646
return err
4747
}
4848

49-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been started!\n", cliui.Styles.Keyword.Render(workspace.Name))
49+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been started at %s!\n", cliui.Styles.Keyword.Render(workspace.Name), cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp)))
5050
return nil
5151
},
5252
}

cli/stop.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ func stop() *cobra.Command {
4646
return err
4747
}
4848

49-
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been stopped!\n", cliui.Styles.Keyword.Render(workspace.Name))
49+
_, _ = fmt.Fprintf(cmd.OutOrStdout(), "\nThe %s workspace has been stopped at %s!\n", cliui.Styles.Keyword.Render(workspace.Name), cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp)))
5050
return nil
5151
},
5252
}

cli/templatecreate.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ func templateCreate() *cobra.Command {
114114
}
115115

116116
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "\n"+cliui.Styles.Wrap.Render(
117-
"The "+cliui.Styles.Keyword.Render(templateName)+" template has been created! "+
117+
"The "+cliui.Styles.Keyword.Render(templateName)+" template has been created at "+cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp))+"! "+
118118
"Developers can provision a workspace with this template using:")+"\n")
119119

120120
_, _ = fmt.Fprintln(cmd.OutOrStdout(), " "+cliui.Styles.Code.Render(fmt.Sprintf("coder create --template=%q [workspace name]", templateName)))
@@ -127,7 +127,7 @@ func templateCreate() *cobra.Command {
127127
cmd.Flags().StringVarP(&directory, "directory", "d", currentDirectory, "Specify the directory to create from")
128128
cmd.Flags().StringVarP(&provisioner, "test.provisioner", "", "terraform", "Customize the provisioner backend")
129129
cmd.Flags().StringVarP(&parameterFile, "parameter-file", "", "", "Specify a file path with parameter values.")
130-
cmd.Flags().DurationVarP(&maxTTL, "max-ttl", "", 168*time.Hour, "Specify a maximum TTL for worksapces created from this template.")
130+
cmd.Flags().DurationVarP(&maxTTL, "max-ttl", "", 168*time.Hour, "Specify a maximum TTL for workspaces created from this template.")
131131
cmd.Flags().DurationVarP(&minAutostartInterval, "min-autostart-interval", "", time.Hour, "Specify a minimum autostart interval for workspaces created from this template.")
132132
// This is for testing!
133133
err := cmd.Flags().MarkHidden("test.provisioner")

cli/templatedelete.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package cli
33
import (
44
"fmt"
55
"strings"
6+
"time"
67

78
"github.com/spf13/cobra"
89
"golang.org/x/xerrors"
@@ -88,7 +89,7 @@ func templateDelete() *cobra.Command {
8889
return xerrors.Errorf("delete template %q: %w", template.Name, err)
8990
}
9091

91-
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "Deleted template "+cliui.Styles.Code.Render(template.Name)+"!")
92+
_, _ = fmt.Fprintln(cmd.OutOrStdout(), "Deleted template "+cliui.Styles.Code.Render(template.Name)+" at "+cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp))+"!")
9293
}
9394

9495
return nil

cli/templateedit.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ func templateEdit() *cobra.Command {
4747
if err != nil {
4848
return xerrors.Errorf("update template metadata: %w", err)
4949
}
50-
_, _ = fmt.Printf("Updated template metadata!\n")
50+
_, _ = fmt.Printf("Updated template metadata at %s!\n", cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp)))
5151
return nil
5252
},
5353
}

cli/templateupdate.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ func templateUpdate() *cobra.Command {
9898
return err
9999
}
100100

101-
_, _ = fmt.Printf("Updated version!\n")
101+
_, _ = fmt.Printf("Updated version at %s!\n", cliui.Styles.DateTimeStamp.Render(time.Now().Format(time.Stamp)))
102102
return nil
103103
},
104104
}

coderd/coderd.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ func New(options *Options) *API {
124124
apiKeyMiddleware,
125125
httpmw.ExtractUserParam(api.Database),
126126
)
127-
r.Get("/*", api.workspaceAppsProxyPath)
127+
r.HandleFunc("/*", api.workspaceAppsProxyPath)
128128
}
129129
// %40 is the encoded character of the @ symbol. VS Code Web does
130130
// not handle character encoding properly, so it's safe to assume

coderd/database/db_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,9 @@ import (
1414

1515
func TestNestedInTx(t *testing.T) {
1616
t.Parallel()
17+
if testing.Short() {
18+
t.SkipNow()
19+
}
1720

1821
uid := uuid.New()
1922
sqlDB := testSQLDB(t)

coderd/userauth.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,23 @@ import (
1717
"github.com/coder/coder/codersdk"
1818
)
1919

20+
// GithubOAuth2Team represents a team scoped to an organization.
21+
type GithubOAuth2Team struct {
22+
Organization string
23+
Slug string
24+
}
25+
2026
// GithubOAuth2Provider exposes required functions for the Github authentication flow.
2127
type GithubOAuth2Config struct {
2228
httpmw.OAuth2Config
2329
AuthenticatedUser func(ctx context.Context, client *http.Client) (*github.User, error)
2430
ListEmails func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error)
2531
ListOrganizationMemberships func(ctx context.Context, client *http.Client) ([]*github.Membership, error)
32+
ListTeams func(ctx context.Context, client *http.Client, org string) ([]*github.Team, error)
2633

2734
AllowSignups bool
2835
AllowOrganizations []string
36+
AllowTeams []GithubOAuth2Team
2937
}
3038

3139
func (api *API) userAuthMethods(rw http.ResponseWriter, _ *http.Request) {
@@ -64,6 +72,41 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
6472
return
6573
}
6674

75+
// The default if no teams are specified is to allow all.
76+
if len(api.GithubOAuth2Config.AllowTeams) > 0 {
77+
teams, err := api.GithubOAuth2Config.ListTeams(r.Context(), oauthClient, *selectedMembership.Organization.Login)
78+
if err != nil {
79+
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{
80+
Message: "Failed to fetch teams from GitHub.",
81+
Detail: err.Error(),
82+
})
83+
return
84+
}
85+
86+
var allowedTeam *github.Team
87+
for _, team := range teams {
88+
for _, allowTeam := range api.GithubOAuth2Config.AllowTeams {
89+
if allowTeam.Organization != *selectedMembership.Organization.Login {
90+
// This needs to continue because multiple organizations
91+
// could exist in the allow/team listings.
92+
continue
93+
}
94+
if allowTeam.Slug != *team.Slug {
95+
continue
96+
}
97+
allowedTeam = team
98+
break
99+
}
100+
}
101+
102+
if allowedTeam == nil {
103+
httpapi.Write(rw, http.StatusUnauthorized, httpapi.Response{
104+
Message: fmt.Sprintf("You aren't a member of an authorized team in the %s Github organization!", *selectedMembership.Organization.Login),
105+
})
106+
return
107+
}
108+
}
109+
67110
emails, err := api.GithubOAuth2Config.ListEmails(r.Context(), oauthClient)
68111
if err != nil {
69112
httpapi.Write(rw, http.StatusInternalServerError, httpapi.Response{

0 commit comments

Comments
 (0)