2022-04-23 22:58:57 +00:00
|
|
|
package coderd_test
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"testing"
|
|
|
|
|
|
|
|
"github.com/google/go-github/v43/github"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"golang.org/x/oauth2"
|
|
|
|
|
|
|
|
"github.com/coder/coder/coderd"
|
|
|
|
"github.com/coder/coder/coderd/coderdtest"
|
|
|
|
"github.com/coder/coder/codersdk"
|
|
|
|
)
|
|
|
|
|
|
|
|
type oauth2Config struct{}
|
|
|
|
|
|
|
|
func (*oauth2Config) AuthCodeURL(state string, _ ...oauth2.AuthCodeOption) string {
|
|
|
|
return "/?state=" + url.QueryEscape(state)
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*oauth2Config) Exchange(context.Context, string, ...oauth2.AuthCodeOption) (*oauth2.Token, error) {
|
|
|
|
return &oauth2.Token{
|
|
|
|
AccessToken: "token",
|
|
|
|
}, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (*oauth2Config) TokenSource(context.Context, *oauth2.Token) oauth2.TokenSource {
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUserAuthMethods(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
t.Run("Password", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, nil)
|
|
|
|
methods, err := client.AuthMethods(context.Background())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, methods.Password)
|
|
|
|
require.False(t, methods.Github)
|
|
|
|
})
|
|
|
|
t.Run("Github", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{},
|
|
|
|
})
|
|
|
|
methods, err := client.AuthMethods(context.Background())
|
|
|
|
require.NoError(t, err)
|
|
|
|
require.True(t, methods.Password)
|
|
|
|
require.True(t, methods.Github)
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestUserOAuth2Github(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
t.Run("NotInAllowedOrganization", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("kyle"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
|
|
|
})
|
2022-07-09 02:37:18 +00:00
|
|
|
t.Run("NotInAllowedTeam", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
AllowOrganizations: []string{"coder"},
|
|
|
|
AllowTeams: []coderd.GithubOAuth2Team{{"another", "something"}, {"coder", "frontend"}},
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("coder"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
ListTeams: func(ctx context.Context, client *http.Client, org string) ([]*github.Team, error) {
|
|
|
|
return []*github.Team{{
|
|
|
|
Slug: github.String("nope"),
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
|
|
|
|
})
|
2022-04-23 22:58:57 +00:00
|
|
|
t.Run("UnverifiedEmail", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
AllowOrganizations: []string{"coder"},
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("coder"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
|
|
|
|
return &github.User{}, nil
|
|
|
|
},
|
|
|
|
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
|
|
|
|
return []*github.UserEmail{{
|
|
|
|
Email: github.String("testuser@coder.com"),
|
|
|
|
Verified: github.Bool(false),
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
_ = coderdtest.CreateFirstUser(t, client)
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
|
|
})
|
|
|
|
t.Run("BlockSignups", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
AllowOrganizations: []string{"coder"},
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("coder"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
|
|
|
|
return &github.User{}, nil
|
|
|
|
},
|
|
|
|
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
|
|
|
|
return []*github.UserEmail{}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusForbidden, resp.StatusCode)
|
|
|
|
})
|
|
|
|
t.Run("Signup", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
AllowOrganizations: []string{"coder"},
|
|
|
|
AllowSignups: true,
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("coder"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
|
|
|
|
return &github.User{
|
|
|
|
Login: github.String("kyle"),
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
|
2022-04-29 20:13:35 +00:00
|
|
|
return []*github.UserEmail{{
|
|
|
|
Email: github.String("kyle@coder.com"),
|
|
|
|
Verified: github.Bool(true),
|
|
|
|
Primary: github.Bool(true),
|
|
|
|
}}, nil
|
2022-04-23 22:58:57 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
|
|
|
})
|
|
|
|
t.Run("Login", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
AllowOrganizations: []string{"coder"},
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("coder"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
|
|
|
|
return &github.User{}, nil
|
|
|
|
},
|
|
|
|
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
|
|
|
|
return []*github.UserEmail{{
|
|
|
|
Email: github.String("testuser@coder.com"),
|
|
|
|
Verified: github.Bool(true),
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
_ = coderdtest.CreateFirstUser(t, client)
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
|
|
|
})
|
2022-07-09 02:37:18 +00:00
|
|
|
t.Run("SignupAllowedTeam", func(t *testing.T) {
|
|
|
|
t.Parallel()
|
|
|
|
client := coderdtest.New(t, &coderdtest.Options{
|
|
|
|
GithubOAuth2Config: &coderd.GithubOAuth2Config{
|
|
|
|
AllowSignups: true,
|
|
|
|
AllowOrganizations: []string{"coder"},
|
|
|
|
AllowTeams: []coderd.GithubOAuth2Team{{"coder", "frontend"}},
|
|
|
|
OAuth2Config: &oauth2Config{},
|
|
|
|
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
|
|
|
|
return []*github.Membership{{
|
|
|
|
Organization: &github.Organization{
|
|
|
|
Login: github.String("coder"),
|
|
|
|
},
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
ListTeams: func(ctx context.Context, client *http.Client, org string) ([]*github.Team, error) {
|
|
|
|
return []*github.Team{{
|
|
|
|
Slug: github.String("frontend"),
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
|
|
|
|
return &github.User{
|
|
|
|
Login: github.String("kyle"),
|
|
|
|
}, nil
|
|
|
|
},
|
|
|
|
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
|
|
|
|
return []*github.UserEmail{{
|
|
|
|
Email: github.String("kyle@coder.com"),
|
|
|
|
Verified: github.Bool(true),
|
|
|
|
Primary: github.Bool(true),
|
|
|
|
}}, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
})
|
|
|
|
resp := oauth2Callback(t, client)
|
|
|
|
require.Equal(t, http.StatusTemporaryRedirect, resp.StatusCode)
|
|
|
|
})
|
2022-04-23 22:58:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func oauth2Callback(t *testing.T, client *codersdk.Client) *http.Response {
|
|
|
|
client.HTTPClient.CheckRedirect = func(req *http.Request, via []*http.Request) error {
|
|
|
|
return http.ErrUseLastResponse
|
|
|
|
}
|
|
|
|
state := "somestate"
|
|
|
|
oauthURL, err := client.URL.Parse("/api/v2/users/oauth2/github/callback?code=asd&state=" + state)
|
|
|
|
require.NoError(t, err)
|
|
|
|
req, err := http.NewRequest("GET", oauthURL.String(), nil)
|
|
|
|
require.NoError(t, err)
|
|
|
|
req.AddCookie(&http.Cookie{
|
|
|
|
Name: "oauth_state",
|
|
|
|
Value: state,
|
|
|
|
})
|
|
|
|
res, err := client.HTTPClient.Do(req)
|
|
|
|
require.NoError(t, err)
|
|
|
|
t.Cleanup(func() {
|
|
|
|
_ = res.Body.Close()
|
|
|
|
})
|
|
|
|
return res
|
|
|
|
}
|