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

[public api] Implement GetOwnerToken rpc #10068

Merged
merged 6 commits into from
May 17, 2022
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
20 changes: 20 additions & 0 deletions components/gitpod-protocol/go/gitpod-service.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import (

// APIInterface wraps the
type APIInterface interface {
GetOwnerToken(ctx context.Context, workspaceID string) (res string, err error)
AdminBlockUser(ctx context.Context, req *AdminBlockUserRequest) (err error)
GetLoggedInUser(ctx context.Context) (res *User, err error)
UpdateLoggedInUser(ctx context.Context, user *User) (res *User, err error)
Expand Down Expand Up @@ -85,6 +86,8 @@ type APIInterface interface {
type FunctionName string

const (
// FunctionGetOwnerToken is the name of the getOwnerToken function
FunctionGetOwnerToken FunctionName = "getOwnerToken"
// FunctionAdminBlockUser is the name of the adminBlockUser function
FunctionAdminBlockUser FunctionName = "adminBlockUser"
// FunctionGetLoggedInUser is the name of the getLoggedInUser function
Expand Down Expand Up @@ -337,6 +340,23 @@ func (gp *APIoverJSONRPC) handler(ctx context.Context, conn *jsonrpc2.Conn, req
return
}

func (gp *APIoverJSONRPC) GetOwnerToken(ctx context.Context, workspaceID string) (res string, err error) {
if gp == nil {
err = errNotConnected
return
}
var _params []interface{}
_params = append(_params, workspaceID)

var _result string
err = gp.C.Call(ctx, "getOwnerToken", _params, &_result)
if err != nil {
return "", err
}
res = _result
return
}

// AdminBlockUser calls adminBlockUser on the server
func (gp *APIoverJSONRPC) AdminBlockUser(ctx context.Context, message *AdminBlockUserRequest) (err error) {
if gp == nil {
Expand Down
87 changes: 51 additions & 36 deletions components/gitpod-protocol/go/mock.go

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

29 changes: 28 additions & 1 deletion components/public-api-server/pkg/apiv1/workspace.go
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,34 @@ func (w *WorkspaceService) GetWorkspace(ctx context.Context, r *v1.GetWorkspaceR
}

func (w *WorkspaceService) GetOwnerToken(ctx context.Context, r *v1.GetOwnerTokenRequest) (*v1.GetOwnerTokenResponse, error) {
return &v1.GetOwnerTokenResponse{Token: "some-owner-token"}, nil
logger := ctxlogrus.Extract(ctx)
token, err := bearerTokenFromContext(ctx)
if err != nil {
return nil, err
}

server, err := w.connectionPool.Get(ctx, token)
if err != nil {
logger.WithError(err).Error("Failed to get connection to server.")
return nil, status.Error(codes.Internal, "failed to establish connection to downstream services")
}

ownerToken, err := server.GetOwnerToken(ctx, r.GetWorkspaceId())

if err != nil {
logger.WithError(err).Error("Failed to get owner token.")
converted := proxy.ConvertError(err)
switch status.Code(converted) {
case codes.PermissionDenied:
return nil, status.Error(codes.PermissionDenied, "insufficient permission to retrieve ownertoken")
case codes.NotFound:
return nil, status.Error(codes.NotFound, "workspace does not exist")
default:
return nil, status.Error(codes.Internal, "unable to retrieve owner token")
}
}

return &v1.GetOwnerTokenResponse{Token: ownerToken}, nil
}

func bearerTokenFromContext(ctx context.Context) (string, error) {
Expand Down
69 changes: 63 additions & 6 deletions components/public-api-server/pkg/apiv1/workspace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,72 @@ func TestWorkspaceService_GetWorkspace(t *testing.T) {
}

func TestWorkspaceService_GetOwnerToken(t *testing.T) {
const (
bearerToken = "bearer-token-for-tests"
foundWorkspaceID = "easycz-seer-xl8o1zacpyw"
ownerToken = "some-owner-token"
)

srv := baseserver.NewForTests(t,
baseserver.WithGRPC(baseserver.MustUseRandomLocalAddress(t)),
)
var connPool *FakeServerConnPool

connPool := &FakeServerConnPool{
api: &FakeGitpodAPI{
ownertokens: map[string]string{foundWorkspaceID: ownerToken},
},
}
v1.RegisterWorkspacesServiceServer(srv.GRPC(), NewWorkspaceService(connPool))
baseserver.StartServerForTests(t, srv)

conn, err := grpc.Dial(srv.GRPCAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()))
require.NoError(t, err)

client := v1.NewWorkspacesServiceClient(conn)
ctx := context.Background()
ctx := metadata.AppendToOutgoingContext(context.Background(), "authorization", bearerToken)

actualOwnerId, err := client.GetOwnerToken(ctx, &v1.GetOwnerTokenRequest{WorkspaceId: "some-workspace-id"})
require.NoError(t, err)
type Expectation struct {
Code codes.Code
Response *v1.GetOwnerTokenResponse
}

require.Equal(t, "some-owner-token", actualOwnerId.Token)
scenarios := []struct {
name string
WorkspaceID string
Expect Expectation
}{
{
name: "returns an owner token when workspace is found by ID",
WorkspaceID: foundWorkspaceID,
Expect: Expectation{
Code: codes.OK,
Response: &v1.GetOwnerTokenResponse{
Token: ownerToken,
},
},
},
{
name: "not found when workspace is not found by ID",
WorkspaceID: "some-not-found-workspace-id",
Expect: Expectation{
Code: codes.NotFound,
},
},
}

for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
resp, err := client.GetOwnerToken(ctx, &v1.GetOwnerTokenRequest{
WorkspaceId: scenario.WorkspaceID,
})
if diff := cmp.Diff(scenario.Expect, Expectation{
Code: status.Code(err),
Response: resp,
}, protocmp.Transform()); diff != "" {
t.Errorf("unexpected difference:\n%v", diff)
}
})
}
}

type FakeServerConnPool struct {
Expand All @@ -145,7 +193,8 @@ func (f *FakeServerConnPool) Get(ctx context.Context, token string) (gitpod.APII
}

type FakeGitpodAPI struct {
workspaces map[string]*gitpod.WorkspaceInfo
workspaces map[string]*gitpod.WorkspaceInfo
ownertokens map[string]string
}

func (f *FakeGitpodAPI) GetWorkspace(ctx context.Context, id string) (res *gitpod.WorkspaceInfo, err error) {
Expand All @@ -157,6 +206,14 @@ func (f *FakeGitpodAPI) GetWorkspace(ctx context.Context, id string) (res *gitpo
return w, nil
}

func (f *FakeGitpodAPI) GetOwnerToken(ctx context.Context, workspaceID string) (res string, err error) {
w, ok := f.ownertokens[workspaceID]
if !ok {
return "", errors.New("code 404")
}
return w, nil
}

func (f *FakeGitpodAPI) AdminBlockUser(ctx context.Context, req *gitpod.AdminBlockUserRequest) (err error) {
panic("implement me")
}
Expand Down
5 changes: 5 additions & 0 deletions components/public-api-server/pkg/proxy/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,11 @@ func ConvertError(err error) error {
return status.Error(codes.PermissionDenied, s)
}

// components/gitpod-protocol/src/messaging/error.ts
if strings.Contains(s, "code 403") {
return status.Error(codes.PermissionDenied, s)
}

// components/gitpod-protocol/src/messaging/error.ts
if strings.Contains(s, "code 404") {
return status.Error(codes.NotFound, s)
Expand Down