Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit18523d5

Browse files
committed
Add gitauth to cliui
1 parent18488f8 commit18523d5

File tree

11 files changed

+327
-39
lines changed

11 files changed

+327
-39
lines changed

‎cli/cliui/gitauth.go

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
package cliui
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"io"
7+
"time"
8+
9+
"github.com/briandowns/spinner"
10+
11+
"github.com/coder/coder/codersdk"
12+
)
13+
14+
typeGitAuthOptionsstruct {
15+
Fetchfunc(context.Context) ([]codersdk.TemplateVersionGitAuth,error)
16+
FetchInterval time.Duration
17+
}
18+
19+
funcGitAuth(ctx context.Context,writer io.Writer,optsGitAuthOptions)error {
20+
ifopts.FetchInterval==0 {
21+
opts.FetchInterval=500*time.Millisecond
22+
}
23+
gitAuth,err:=opts.Fetch(ctx)
24+
iferr!=nil {
25+
returnerr
26+
}
27+
28+
spin:=spinner.New(spinner.CharSets[78],100*time.Millisecond,spinner.WithColor("fgHiGreen"))
29+
spin.Writer=writer
30+
spin.ForceOutput=true
31+
spin.Suffix=" Waiting for Git authentication..."
32+
deferspin.Stop()
33+
34+
ticker:=time.NewTicker(opts.FetchInterval)
35+
deferticker.Stop()
36+
for_,auth:=rangegitAuth {
37+
ifauth.Authenticated {
38+
returnnil
39+
}
40+
41+
_,_=fmt.Fprintf(writer,"You must authenticate with %s to create a workspace with this template. Visit:\n\n\t%s\n\n",auth.Type.Pretty(),auth.AuthenticateURL)
42+
43+
ticker.Reset(opts.FetchInterval)
44+
spin.Start()
45+
for {
46+
select {
47+
case<-ctx.Done():
48+
returnctx.Err()
49+
case<-ticker.C:
50+
}
51+
gitAuth,err:=opts.Fetch(ctx)
52+
iferr!=nil {
53+
returnerr
54+
}
55+
varauthedbool
56+
for_,a:=rangegitAuth {
57+
if!a.Authenticated||a.ID!=auth.ID {
58+
continue
59+
}
60+
authed=true
61+
break
62+
}
63+
// The user authenticated with the provider!
64+
ifauthed {
65+
break
66+
}
67+
}
68+
spin.Stop()
69+
_,_=fmt.Fprintf(writer,"Successfully authenticated with %s!\n\n",auth.Type.Pretty())
70+
}
71+
returnnil
72+
}

‎cli/cliui/gitauth_test.go

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package cliui_test
2+
3+
import (
4+
"context"
5+
"net/url"
6+
"sync/atomic"
7+
"testing"
8+
"time"
9+
10+
"github.com/spf13/cobra"
11+
"github.com/stretchr/testify/assert"
12+
13+
"github.com/coder/coder/cli/cliui"
14+
"github.com/coder/coder/codersdk"
15+
"github.com/coder/coder/pty/ptytest"
16+
"github.com/coder/coder/testutil"
17+
)
18+
19+
funcTestGitAuth(t*testing.T) {
20+
t.Parallel()
21+
22+
ctx,cancel:=context.WithTimeout(context.Background(),testutil.WaitShort)
23+
defercancel()
24+
25+
ptty:=ptytest.New(t)
26+
cmd:=&cobra.Command{
27+
RunE:func(cmd*cobra.Command,args []string)error {
28+
varfetched atomic.Bool
29+
returncliui.GitAuth(cmd.Context(),cmd.OutOrStdout(), cliui.GitAuthOptions{
30+
Fetch:func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth,error) {
31+
deferfetched.Store(true)
32+
return []codersdk.TemplateVersionGitAuth{{
33+
ID:"github",
34+
Type:codersdk.GitProviderGitHub,
35+
Authenticated:fetched.Load(),
36+
AuthenticateURL:"https://example.com/gitauth/github?redirect="+url.QueryEscape("/gitauth?notify"),
37+
}},nil
38+
},
39+
FetchInterval:time.Millisecond,
40+
})
41+
},
42+
}
43+
cmd.SetOutput(ptty.Output())
44+
cmd.SetIn(ptty.Input())
45+
done:=make(chanstruct{})
46+
gofunc() {
47+
deferclose(done)
48+
err:=cmd.Execute()
49+
assert.NoError(t,err)
50+
}()
51+
ptty.ExpectMatchContext(ctx,"You must authenticate with")
52+
ptty.ExpectMatchContext(ctx,"https://example.com/gitauth/github")
53+
ptty.ExpectMatchContext(ctx,"Successfully authenticated with GitHub")
54+
<-done
55+
}

‎cli/create.go

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package cli
22

33
import (
4+
"context"
45
"fmt"
56
"io"
67
"time"
@@ -324,6 +325,15 @@ PromptRichParamLoop:
324325
_,_=fmt.Fprintln(cmd.OutOrStdout())
325326
}
326327

328+
err=cliui.GitAuth(ctx,cmd.OutOrStdout(), cliui.GitAuthOptions{
329+
Fetch:func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth,error) {
330+
returnclient.TemplateVersionGitAuth(ctx,templateVersion.ID)
331+
},
332+
})
333+
iferr!=nil {
334+
returnnil,xerrors.Errorf("template version git auth: %w",err)
335+
}
336+
327337
// Run a dry-run with the given parameters to check correctness
328338
dryRun,err:=client.CreateTemplateVersionDryRun(cmd.Context(),templateVersion.ID, codersdk.CreateTemplateVersionDryRunRequest{
329339
WorkspaceName:args.NewWorkspaceName,

‎cli/create_test.go

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@ package cli_test
33
import (
44
"context"
55
"fmt"
6+
"net/http"
7+
"net/url"
68
"os"
9+
"regexp"
710
"testing"
811
"time"
912

1013
"github.com/stretchr/testify/assert"
1114
"github.com/stretchr/testify/require"
15+
"golang.org/x/oauth2"
1216

1317
"github.com/coder/coder/cli/clitest"
1418
"github.com/coder/coder/coderd/coderdtest"
19+
"github.com/coder/coder/coderd/database"
20+
"github.com/coder/coder/coderd/gitauth"
1521
"github.com/coder/coder/codersdk"
1622
"github.com/coder/coder/provisioner/echo"
1723
"github.com/coder/coder/provisionersdk/proto"
@@ -603,6 +609,61 @@ func TestCreateValidateRichParameters(t *testing.T) {
603609
})
604610
}
605611

612+
funcTestCreateWithGitAuth(t*testing.T) {
613+
t.Parallel()
614+
echoResponses:=&echo.Responses{
615+
Parse:echo.ParseComplete,
616+
ProvisionPlan: []*proto.Provision_Response{
617+
{
618+
Type:&proto.Provision_Response_Complete{
619+
Complete:&proto.Provision_Complete{
620+
GitAuthProviders: []string{"github"},
621+
},
622+
},
623+
},
624+
},
625+
ProvisionApply: []*proto.Provision_Response{{
626+
Type:&proto.Provision_Response_Complete{
627+
Complete:&proto.Provision_Complete{},
628+
},
629+
}},
630+
}
631+
632+
client:=coderdtest.New(t,&coderdtest.Options{
633+
GitAuthConfigs: []*gitauth.Config{{
634+
OAuth2Config:&oauth2Config{},
635+
ID:"github",
636+
Regex:regexp.MustCompile(`github\.com`),
637+
Type:codersdk.GitProviderGitHub,
638+
}},
639+
IncludeProvisionerDaemon:true,
640+
})
641+
user:=coderdtest.CreateFirstUser(t,client)
642+
version:=coderdtest.CreateTemplateVersion(t,client,user.OrganizationID,echoResponses)
643+
coderdtest.AwaitTemplateVersionJob(t,client,version.ID)
644+
template:=coderdtest.CreateTemplate(t,client,user.OrganizationID,version.ID)
645+
646+
cmd,root:=clitest.New(t,"create","my-workspace","--template",template.Name)
647+
clitest.SetupConfig(t,client,root)
648+
doneChan:=make(chanstruct{})
649+
pty:=ptytest.New(t)
650+
cmd.SetIn(pty.Input())
651+
cmd.SetOut(pty.Output())
652+
gofunc() {
653+
deferclose(doneChan)
654+
err:=cmd.Execute()
655+
assert.NoError(t,err)
656+
}()
657+
658+
pty.ExpectMatch("You must authenticate with GitHub to create a workspace")
659+
resp:=coderdtest.RequestGitAuthCallback(t,"github",client)
660+
_=resp.Body.Close()
661+
require.Equal(t,http.StatusTemporaryRedirect,resp.StatusCode)
662+
pty.ExpectMatch("Confirm create?")
663+
pty.WriteLine("yes")
664+
<-doneChan
665+
}
666+
606667
funccreateTestParseResponseWithDefault(defaultValuestring) []*proto.Parse_Response {
607668
return []*proto.Parse_Response{{
608669
Type:&proto.Parse_Response_Complete{
@@ -638,3 +699,40 @@ func createTestParseResponseWithDefault(defaultValue string) []*proto.Parse_Resp
638699
},
639700
}}
640701
}
702+
703+
typeoauth2Configstruct {
704+
token*oauth2.Token
705+
}
706+
707+
func (*oauth2Config)AuthCodeURL(statestring,_...oauth2.AuthCodeOption)string {
708+
return"/?state="+url.QueryEscape(state)
709+
}
710+
711+
func (o*oauth2Config)Exchange(context.Context,string,...oauth2.AuthCodeOption) (*oauth2.Token,error) {
712+
return&oauth2.Token{
713+
AccessToken:"token",
714+
RefreshToken:"refresh",
715+
Expiry:database.Now().Add(time.Hour),
716+
},nil
717+
}
718+
719+
func (o*oauth2Config)TokenSource(context.Context,*oauth2.Token) oauth2.TokenSource {
720+
return&oauth2TokenSource{
721+
token:o.token,
722+
}
723+
}
724+
725+
typeoauth2TokenSourcestruct {
726+
token*oauth2.Token
727+
}
728+
729+
func (o*oauth2TokenSource)Token() (*oauth2.Token,error) {
730+
ifo.token!=nil {
731+
returno.token,nil
732+
}
733+
return&oauth2.Token{
734+
AccessToken:"token",
735+
RefreshToken:"refresh",
736+
Expiry:database.Now().Add(time.Hour),
737+
},nil
738+
}

‎cmd/cliui/main.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import (
55
"errors"
66
"fmt"
77
"io"
8+
"net/url"
89
"os"
910
"strings"
11+
"sync/atomic"
1012
"time"
1113

1214
"github.com/spf13/cobra"
@@ -235,6 +237,37 @@ func main() {
235237
},
236238
})
237239

240+
root.AddCommand(&cobra.Command{
241+
Use:"git-auth",
242+
RunE:func(cmd*cobra.Command,args []string)error {
243+
varcount atomic.Int32
244+
vargithubAuthed atomic.Bool
245+
vargitlabAuthed atomic.Bool
246+
gofunc() {
247+
time.Sleep(time.Second)
248+
githubAuthed.Store(true)
249+
time.Sleep(time.Second*2)
250+
gitlabAuthed.Store(true)
251+
}()
252+
returncliui.GitAuth(cmd.Context(),cmd.OutOrStdout(), cliui.GitAuthOptions{
253+
Fetch:func(ctx context.Context) ([]codersdk.TemplateVersionGitAuth,error) {
254+
count.Add(1)
255+
return []codersdk.TemplateVersionGitAuth{{
256+
ID:"github",
257+
Type:codersdk.GitProviderGitHub,
258+
Authenticated:githubAuthed.Load(),
259+
AuthenticateURL:"https://example.com/gitauth/github?redirect="+url.QueryEscape("/gitauth?notify"),
260+
}, {
261+
ID:"gitlab",
262+
Type:codersdk.GitProviderGitLab,
263+
Authenticated:gitlabAuthed.Load(),
264+
AuthenticateURL:"https://example.com/gitauth/gitlab?redirect="+url.QueryEscape("/gitauth?notify"),
265+
}},nil
266+
},
267+
})
268+
},
269+
})
270+
238271
err:=root.Execute()
239272
iferr!=nil {
240273
_,_=fmt.Println(err.Error())

‎coderd/coderdtest/coderdtest.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -717,6 +717,33 @@ func MustWorkspace(t *testing.T, client *codersdk.Client, workspaceID uuid.UUID)
717717
returnws
718718
}
719719

720+
// RequestGitAuthCallback makes a request with the proper OAuth2 state cookie
721+
// to the git auth callback endpoint.
722+
funcRequestGitAuthCallback(t*testing.T,providerIDstring,client*codersdk.Client)*http.Response {
723+
client.HTTPClient.CheckRedirect=func(req*http.Request,via []*http.Request)error {
724+
returnhttp.ErrUseLastResponse
725+
}
726+
state:="somestate"
727+
oauthURL,err:=client.URL.Parse(fmt.Sprintf("/gitauth/%s/callback?code=asd&state=%s",providerID,state))
728+
require.NoError(t,err)
729+
req,err:=http.NewRequestWithContext(context.Background(),"GET",oauthURL.String(),nil)
730+
require.NoError(t,err)
731+
req.AddCookie(&http.Cookie{
732+
Name:codersdk.OAuth2StateCookie,
733+
Value:state,
734+
})
735+
req.AddCookie(&http.Cookie{
736+
Name:codersdk.SessionTokenCookie,
737+
Value:client.SessionToken(),
738+
})
739+
res,err:=client.HTTPClient.Do(req)
740+
require.NoError(t,err)
741+
t.Cleanup(func() {
742+
_=res.Body.Close()
743+
})
744+
returnres
745+
}
746+
720747
// NewGoogleInstanceIdentity returns a metadata client and ID token validator for faking
721748
// instance authentication for Google Cloud.
722749
// nolint:revive

‎coderd/templateversions.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,14 @@ func (api *API) templateVersionGitAuth(rw http.ResponseWriter, r *http.Request)
292292
})
293293
return
294294
}
295+
query:=redirectURL.Query()
296+
// The frontend uses a BroadcastChannel to notify listening pages for
297+
// Git auth updates if the "notify" query parameter is set.
298+
//
299+
// It's important we do this in the backend, because the same endpoint
300+
// is used for CLI authentication.
301+
query.Add("redirect","/gitauth?notify")
302+
redirectURL.RawQuery=query.Encode()
295303

296304
provider:= codersdk.TemplateVersionGitAuth{
297305
ID:config.ID,

‎coderd/templateversions_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -485,7 +485,7 @@ func TestTemplateVersionsGitAuth(t *testing.T) {
485485
require.False(t,providers[0].Authenticated)
486486

487487
// Perform the Git auth callback to authenticate the user...
488-
resp:=gitAuthCallback(t,"github",client)
488+
resp:=coderdtest.RequestGitAuthCallback(t,"github",client)
489489
_=resp.Body.Close()
490490
require.Equal(t,http.StatusTemporaryRedirect,resp.StatusCode)
491491

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp