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

chore: ensure github uids are unique#11826

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
Emyrk merged 6 commits intomainfromstevenmasley/github_oauth_email
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes fromall commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 21 additions & 1 deletioncoderd/coderdtest/oidctest/idp.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -16,6 +16,7 @@ import (
"net/http"
"net/http/cookiejar"
"net/http/httptest"
"net/http/httputil"
"net/url"
"strconv"
"strings"
Expand DownExpand Up@@ -67,6 +68,9 @@ type FakeIDP struct {
handler http.Handler
cfg *oauth2.Config

// callbackPath allows changing where the callback path to coderd is expected.
// This only affects using the Login helper functions.
callbackPath string
// clientID to be used by coderd
clientID string
clientSecret string
Expand DownExpand Up@@ -161,6 +165,12 @@ func WithDefaultExpire(d time.Duration) func(*FakeIDP) {
}
}

func WithCallbackPath(path string) func(*FakeIDP) {
return func(f *FakeIDP) {
f.callbackPath = path
}
}

func WithStaticCredentials(id, secret string) func(*FakeIDP) {
return func(f *FakeIDP) {
if id != "" {
Expand DownExpand Up@@ -369,6 +379,12 @@ func (f *FakeIDP) Login(t testing.TB, client *codersdk.Client, idTokenClaims jwt
t.Helper()

client, resp := f.AttemptLogin(t, client, idTokenClaims, opts...)
if resp.StatusCode != http.StatusOK {
data, err := httputil.DumpResponse(resp, true)
if err == nil {
t.Logf("Attempt Login response payload\n%s", string(data))
}
}
require.Equal(t, http.StatusOK, resp.StatusCode, "client failed to login")
return client, resp
}
Expand DownExpand Up@@ -398,7 +414,11 @@ func (f *FakeIDP) AttemptLogin(t testing.TB, client *codersdk.Client, idTokenCla
func (f *FakeIDP) LoginWithClient(t testing.TB, client *codersdk.Client, idTokenClaims jwt.MapClaims, opts ...func(r *http.Request)) (*codersdk.Client, *http.Response) {
t.Helper()

coderOauthURL, err := client.URL.Parse("/api/v2/users/oidc/callback")
path := "/api/v2/users/oidc/callback"
if f.callbackPath != "" {
path = f.callbackPath
}
coderOauthURL, err := client.URL.Parse(path)
require.NoError(t, err)
f.SetRedirect(t, coderOauthURL.String())

Expand Down
19 changes: 19 additions & 0 deletionscoderd/userauth.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -604,6 +604,25 @@ func (api *API) userOAuth2Github(rw http.ResponseWriter, r *http.Request) {
return
}

// If we have a nil GitHub ID, that is a big problem. That would mean we link
// this user and all other users with this bug to the same uuid.
// We should instead throw an error. This should never occur in production.
//
// Verified that the lowest ID on GitHub is "1", so 0 should never occur.
if ghUser.GetID() == 0 {
httpapi.Write(ctx, rw, http.StatusInternalServerError, codersdk.Response{
Message: "The GitHub user ID is missing, this should never happen. Please report this error.",
// If this happens, the User could either be:
// - Empty, in which case all these fields would also be empty.
// - Not a user, in which case the "Type" would be something other than "User"
Detail: fmt.Sprintf("Other user fields: name=%q, email=%q, type=%q",
ghUser.GetName(),
ghUser.GetEmail(),
ghUser.GetType(),
Comment on lines +618 to +621
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Could this potentially leak another user's email? Would it be safer to log this instead?

Copy link
MemberAuthor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

No it cannot. This comes from the authenticated request to github using your oauth token.

So this payload comes from:

  • User does github oauth flow with coder
  • Oauth flow succeeds
  • Coder uses their oauth token to determine to make a new user or link to an existing
    • In this flow, the AuthentictedUser request fails from anid=0.

To leak someone else email, you would need to authenticate as that user via github.

),
})
return
}
user, link, err := findLinkedUser(ctx, api.Database, githubLinkedID(ghUser), verifiedEmail.GetEmail())
if err != nil {
logger.Error(ctx, "oauth2: unable to find linked user", slog.F("gh_user", ghUser.Name), slog.Error(err))
Expand Down
97 changes: 95 additions & 2 deletionscoderd/userauth_test.go
View file
Open in desktop
Original file line numberDiff line numberDiff line change
Expand Up@@ -14,6 +14,7 @@ import (
"github.com/golang-jwt/jwt/v4"
"github.com/google/go-github/v43/github"
"github.com/google/uuid"
"github.com/prometheus/client_golang/prometheus"
"github.com/stretchr/testify/require"
"golang.org/x/xerrors"

Expand All@@ -25,6 +26,7 @@ import (
"github.com/coder/coder/v2/coderd/database"
"github.com/coder/coder/v2/coderd/database/dbgen"
"github.com/coder/coder/v2/coderd/database/dbtestutil"
"github.com/coder/coder/v2/coderd/promoauth"
"github.com/coder/coder/v2/codersdk"
"github.com/coder/coder/v2/testutil"
)
Expand DownExpand Up@@ -205,6 +207,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
ID: github.Int64(100),
Login: github.String("kyle"),
}, nil
},
Expand DownExpand Up@@ -264,7 +267,9 @@ func TestUserOAuth2Github(t *testing.T) {
}}, nil
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{}, nil
return &github.User{
ID: github.Int64(100),
}, nil
},
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
return []*github.UserEmail{{
Expand DownExpand Up@@ -295,7 +300,9 @@ func TestUserOAuth2Github(t *testing.T) {
}}, nil
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{}, nil
return &github.User{
ID: github.Int64(100),
}, nil
},
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
return []*github.UserEmail{{
Expand DownExpand Up@@ -390,6 +397,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
ID: github.Int64(100),
Login: github.String("kyle"),
}, nil
},
Expand DownExpand Up@@ -442,6 +450,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
ID: github.Int64(100),
Login: github.String("mathias"),
}, nil
},
Expand DownExpand Up@@ -494,6 +503,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
ID: github.Int64(100),
Login: github.String("mathias"),
}, nil
},
Expand DownExpand Up@@ -532,6 +542,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
ID: github.Int64(100),
Login: github.String("mathias"),
}, nil
},
Expand DownExpand Up@@ -574,6 +585,7 @@ func TestUserOAuth2Github(t *testing.T) {
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
ID: github.Int64(100),
Login: github.String("kyle"),
}, nil
},
Expand All@@ -591,6 +603,87 @@ func TestUserOAuth2Github(t *testing.T) {

require.Equal(t, http.StatusUnauthorized, resp.StatusCode)
})
t.Run("ChangedEmail", func(t *testing.T) {
t.Parallel()

fake := oidctest.NewFakeIDP(t,
oidctest.WithServing(),
oidctest.WithCallbackPath("/api/v2/users/oauth2/github/callback"),
)
const ghID = int64(7777)
auditor := audit.NewMock()
coderEmail := &github.UserEmail{
Email: github.String("alice@coder.com"),
Verified: github.Bool(true),
Primary: github.Bool(true),
}
gmailEmail := &github.UserEmail{
Email: github.String("alice@gmail.com"),
Verified: github.Bool(true),
Primary: github.Bool(false),
}
emails := []*github.UserEmail{
gmailEmail,
coderEmail,
}

client := coderdtest.New(t, &coderdtest.Options{
Auditor: auditor,
GithubOAuth2Config: &coderd.GithubOAuth2Config{
AllowSignups: true,
AllowEveryone: true,
OAuth2Config: promoauth.NewFactory(prometheus.NewRegistry()).NewGithub("test-github", fake.OIDCConfig(t, []string{})),
ListOrganizationMemberships: func(ctx context.Context, client *http.Client) ([]*github.Membership, error) {
return []*github.Membership{}, nil
},
TeamMembership: func(ctx context.Context, client *http.Client, org, team, username string) (*github.Membership, error) {
return nil, xerrors.New("no teams")
},
AuthenticatedUser: func(ctx context.Context, client *http.Client) (*github.User, error) {
return &github.User{
Login: github.String("alice"),
ID: github.Int64(ghID),
}, nil
},
ListEmails: func(ctx context.Context, client *http.Client) ([]*github.UserEmail, error) {
return emails, nil
},
},
})

ctx := testutil.Context(t, testutil.WaitMedium)
// This should register the user
client, _ = fake.Login(t, client, jwt.MapClaims{})
user, err := client.User(ctx, "me")
require.NoError(t, err)
userID := user.ID
require.Equal(t, user.Email, *coderEmail.Email)

// Now the user is registered, let's change their primary email.
coderEmail.Primary = github.Bool(false)
gmailEmail.Primary = github.Bool(true)

client, _ = fake.Login(t, client, jwt.MapClaims{})
user, err = client.User(ctx, "me")
require.NoError(t, err)
require.Equal(t, user.ID, userID)
require.Equal(t, user.Email, *gmailEmail.Email)

// Entirely change emails.
newEmail := "alice@newdomain.com"
emails = []*github.UserEmail{
{
Email: github.String("alice@newdomain.com"),
Primary: github.Bool(true),
Verified: github.Bool(true),
},
}
client, _ = fake.Login(t, client, jwt.MapClaims{})
user, err = client.User(ctx, "me")
require.NoError(t, err)
require.Equal(t, user.ID, userID)
require.Equal(t, user.Email, newEmail)
})
Comment on lines +606 to +686
Copy link
MemberAuthor

@EmyrkEmyrkJan 26, 2024
edited
Loading

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Added a change email unit test using the fake idp.

}

// nolint:bodyclose
Expand Down

[8]ページ先頭

©2009-2025 Movatter.jp