Skip to content
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[local-app] gracefully handle invalid token #5369

Merged
merged 1 commit into from
Aug 31, 2021
Merged
Show file tree
Hide file tree
Changes from all 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
8 changes: 8 additions & 0 deletions components/gitpod-db/src/typeorm/user-db-impl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,14 @@ export class TypeORMUserDBImpl implements UserDB {
return { user: token.user, token };
}

public async findGitpodTokensOfUser(userId: string, tokenHash: string): Promise<GitpodToken | undefined> {
const repo = await this.getGitpodTokenRepo()
const qBuilder = repo.createQueryBuilder('gitpodToken')
.leftJoinAndSelect("gitpodToken.user", "user");
qBuilder.where('user.id = :userId AND gitpodToken.tokenHash = :tokenHash', { userId, tokenHash });
return qBuilder.getOne();
}

public async findAllGitpodTokensOfUser(userId: string): Promise<GitpodToken[]> {
const repo = await this.getGitpodTokenRepo()
const qBuilder = repo.createQueryBuilder('gitpodToken')
Expand Down
1 change: 1 addition & 0 deletions components/gitpod-db/src/user-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ export interface UserDB extends OAuthUserRepository, OAuthTokenRepository {
findUserByName(name: string): Promise<User | undefined>;

findUserByGitpodToken(tokenHash: string, tokenType?: GitpodTokenType): Promise<{ user: User, token: GitpodToken } | undefined>;
findGitpodTokensOfUser(userId: string, tokenHash: string): Promise<GitpodToken | undefined>;
findAllGitpodTokensOfUser(userId: string): Promise<GitpodToken[]>;
storeGitpodToken(token: GitpodToken & { user: DBUser }): Promise<void>;
deleteGitpodToken(tokenHash: string): Promise<void>;
Expand Down
23 changes: 23 additions & 0 deletions components/gitpod-protocol/go/gitpod-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ type APIInterface interface {
DeleteOwnAuthProvider(ctx context.Context, params *DeleteOwnAuthProviderParams) (err error)
GetBranding(ctx context.Context) (res *Branding, err error)
GetConfiguration(ctx context.Context) (res *Configuration, err error)
GetGitpodTokenScopes(ctx context.Context, tokenHash string) (res []string, err error)
GetToken(ctx context.Context, query *GetTokenSearchOptions) (res *Token, err error)
GetPortAuthenticationToken(ctx context.Context, workspaceID string) (res *Token, err error)
DeleteAccount(ctx context.Context) (err error)
Expand Down Expand Up @@ -106,6 +107,8 @@ const (
FunctionGetBranding FunctionName = "getBranding"
// FunctionGetConfiguration is the name of the getConfiguration function
FunctionGetConfiguration FunctionName = "getConfiguration"
// FunctionGetGitpodTokenScopes is the name of the GetGitpodTokenScopes function
FunctionGetGitpodTokenScopes FunctionName = "getGitpodTokenScopes"
// FunctionGetToken is the name of the getToken function
FunctionGetToken FunctionName = "getToken"
// FunctionGetPortAuthenticationToken is the name of the getPortAuthenticationToken function
Expand Down Expand Up @@ -499,6 +502,26 @@ func (gp *APIoverJSONRPC) GetConfiguration(ctx context.Context) (res *Configurat
return
}

// GetGitpodTokenScopes calls getGitpodTokenScopes on the server
func (gp *APIoverJSONRPC) GetGitpodTokenScopes(ctx context.Context, tokenHash string) (res []string, err error) {
if gp == nil {
err = errNotConnected
return
}
var _params []interface{}

_params = append(_params, tokenHash)

var result []string
err = gp.C.Call(ctx, "getGitpodTokenScopes", _params, &result)
if err != nil {
return
}
res = result

return
}

// GetToken calls getToken on the server
func (gp *APIoverJSONRPC) GetToken(ctx context.Context, query *GetTokenSearchOptions) (res *Token, err error) {
if gp == nil {
Expand Down
15 changes: 15 additions & 0 deletions components/gitpod-protocol/go/mock.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

38 changes: 34 additions & 4 deletions components/gitpod-protocol/go/reconnecting-ws.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"sync"
"time"
Expand All @@ -16,6 +17,19 @@ import (
"github.com/sirupsen/logrus"
)

// ErrClosed is returned when the reconnecting web socket is closed.
var ErrClosed = errors.New("reconnecting-ws: closed")

// ErrBadHandshake is returned when the server response to opening handshake is
// invalid.
type ErrBadHandshake struct {
Resp *http.Response
}

func (e *ErrBadHandshake) Error() string {
return fmt.Sprintf("reconnecting-ws: bad handshake: code %v", e.Resp.StatusCode)
}

// The ReconnectingWebsocket represents a Reconnecting WebSocket connection.
type ReconnectingWebsocket struct {
url string
Expand All @@ -27,6 +41,7 @@ type ReconnectingWebsocket struct {
reconnectionDelayGrowFactor float64

once sync.Once
closeErr error
closedCh chan struct{}
connCh chan chan *WebsocketConnection
errCh chan error
Expand Down Expand Up @@ -54,7 +69,12 @@ func NewReconnectingWebsocket(url string, reqHeader http.Header, log *logrus.Ent

// Close closes the underlying webscoket connection.
func (rc *ReconnectingWebsocket) Close() error {
return rc.closeWithError(ErrClosed)
}

func (rc *ReconnectingWebsocket) closeWithError(closeErr error) error {
rc.once.Do(func() {
rc.closeErr = closeErr
close(rc.closedCh)
})
return nil
Expand All @@ -69,7 +89,7 @@ func (rc *ReconnectingWebsocket) EnsureConnection(handler func(conn *WebsocketCo
connCh := make(chan *WebsocketConnection, 1)
select {
case <-rc.closedCh:
return errors.New("closed")
return rc.closeErr
case rc.connCh <- connCh:
}
conn := <-connCh
Expand All @@ -79,7 +99,7 @@ func (rc *ReconnectingWebsocket) EnsureConnection(handler func(conn *WebsocketCo
}
select {
case <-rc.closedCh:
return errors.New("closed")
return rc.closeErr
case rc.errCh <- err:
}
}
Expand Down Expand Up @@ -166,7 +186,7 @@ func (rc *ReconnectingWebsocket) connect(ctx context.Context) *WebsocketConnecti
}

dialer := websocket.Dialer{HandshakeTimeout: rc.handshakeTimeout}
conn, _, err := dialer.DialContext(ctx, rc.url, rc.reqHeader)
conn, resp, err := dialer.DialContext(ctx, rc.url, rc.reqHeader)
if err == nil {
rc.log.WithField("url", rc.url).Debug("connection was successfully established")
ws, err := NewWebsocketConnection(context.Background(), conn, func(staleErr error) {
Expand All @@ -177,7 +197,17 @@ func (rc *ReconnectingWebsocket) connect(ctx context.Context) *WebsocketConnecti
}
}

rc.log.WithError(err).WithField("url", rc.url).Errorf("failed to connect, trying again in %d seconds...", uint32(delay.Seconds()))
if err == websocket.ErrBadHandshake {
_ = rc.closeWithError(&ErrBadHandshake{resp})
return nil
}

var statusCode int
if resp != nil {
statusCode = resp.StatusCode
}

rc.log.WithError(err).WithField("statusCode", statusCode).WithField("url", rc.url).Errorf("failed to connect, trying again in %d seconds...", uint32(delay.Seconds()))
select {
case <-rc.closedCh:
return nil
Expand Down
1 change: 1 addition & 0 deletions components/gitpod-protocol/src/gitpod-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export interface GitpodServer extends JsonRpcServer<GitpodClient>, AdminServer,
getBranding(): Promise<Branding>;
getConfiguration(): Promise<Configuration>;
getToken(query: GitpodServer.GetTokenSearchOptions): Promise<Token | undefined>;
getGitpodTokenScopes(tokenHash: string): Promise<string[]>;
/**
* @deprecated
*/
Expand Down
3 changes: 2 additions & 1 deletion components/local-app/go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ require (
github.com/gitpod-io/gitpod/gitpod-protocol v0.0.0-00010101000000-000000000000
github.com/gitpod-io/gitpod/local-app/api v0.0.0-00010101000000-000000000000
github.com/gitpod-io/gitpod/supervisor/api v0.0.0-00010101000000-000000000000
github.com/golang/mock v1.6.0 // indirect
github.com/google/go-cmp v0.5.6 // indirect
github.com/google/uuid v1.1.2
github.com/improbable-eng/grpc-web v0.14.0
github.com/kevinburke/ssh_config v1.1.0
Expand All @@ -30,7 +32,6 @@ require (
github.com/danieljoos/wincred v1.1.0 // indirect
github.com/fsnotify/fsnotify v1.4.9 // indirect
github.com/godbus/dbus/v5 v5.0.3 // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.2 // indirect
github.com/gorilla/websocket v1.4.2 // indirect
github.com/grpc-ecosystem/grpc-gateway/v2 v2.5.0 // indirect
Expand Down
1 change: 1 addition & 0 deletions components/local-app/go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@ golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4 h1:4nGaVu0QrbjT/AK2PRLuQfQuh6DJve+pELhqTdAj3x0=
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
Expand Down
Loading