mirror of
https://github.com/gitpod-io/gitpod.git
synced 2025-12-08 17:36:30 +00:00
106 lines
3.1 KiB
Go
106 lines
3.1 KiB
Go
// Copyright (c) 2023 Gitpod GmbH. All rights reserved.
|
|
// Licensed under the GNU Affero General Public License (AGPL).
|
|
// See License.AGPL.txt in the project root for license information.
|
|
|
|
package apiv1
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
connect "github.com/bufbuild/connect-go"
|
|
"github.com/gitpod-io/gitpod/common-go/log"
|
|
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
|
|
"github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1/v1connect"
|
|
"github.com/gitpod-io/gitpod/public-api-server/pkg/proxy"
|
|
"github.com/zitadel/oidc/pkg/oidc"
|
|
)
|
|
|
|
type IDTokenSource interface {
|
|
IDToken(ctx context.Context, org string, audience []string, userInfo oidc.UserInfo) (string, error)
|
|
}
|
|
|
|
func NewIdentityProviderService(serverConnPool proxy.ServerConnectionPool, source IDTokenSource) *IdentityProviderService {
|
|
return &IdentityProviderService{
|
|
connectionPool: serverConnPool,
|
|
idTokenSource: source,
|
|
}
|
|
}
|
|
|
|
type IdentityProviderService struct {
|
|
connectionPool proxy.ServerConnectionPool
|
|
idTokenSource IDTokenSource
|
|
|
|
v1connect.UnimplementedWorkspacesServiceHandler
|
|
}
|
|
|
|
var _ v1connect.IdentityProviderServiceHandler = ((*IdentityProviderService)(nil))
|
|
|
|
// GetIDToken implements v1connect.IDPServiceHandler
|
|
func (srv *IdentityProviderService) GetIDToken(ctx context.Context, req *connect.Request[v1.GetIDTokenRequest]) (*connect.Response[v1.GetIDTokenResponse], error) {
|
|
workspaceID, err := validateWorkspaceID(ctx, req.Msg.GetWorkspaceId())
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if len(req.Msg.Audience) < 1 {
|
|
return nil, connect.NewError(connect.CodeInvalidArgument, fmt.Errorf("Must have at least one audience entry"))
|
|
}
|
|
|
|
conn, err := getConnection(ctx, srv.connectionPool)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// We use GetIDToken as standin for the IDP operation authorisation until we have a better way of handling this
|
|
err = conn.GetIDToken(ctx)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
workspace, err := conn.GetWorkspace(ctx, workspaceID)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get workspace.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
user, err := conn.GetLoggedInUser(ctx)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to get calling user.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
|
|
var email string
|
|
for _, id := range user.Identities {
|
|
if id == nil || id.Deleted || id.PrimaryEmail == "" {
|
|
continue
|
|
}
|
|
email = id.PrimaryEmail
|
|
break
|
|
}
|
|
|
|
if workspace.Workspace == nil {
|
|
log.Extract(ctx).WithError(err).Error("Server did not return a workspace.")
|
|
return nil, connect.NewError(connect.CodeNotFound, fmt.Errorf("workspace not found"))
|
|
}
|
|
|
|
subject := workspace.Workspace.ContextURL
|
|
userInfo := oidc.NewUserInfo()
|
|
userInfo.SetName(user.Name)
|
|
userInfo.SetSubject(subject)
|
|
if email != "" {
|
|
userInfo.SetEmail(email, user.OrganizationId != "")
|
|
}
|
|
|
|
token, err := srv.idTokenSource.IDToken(ctx, "gitpod", req.Msg.Audience, userInfo)
|
|
if err != nil {
|
|
log.Extract(ctx).WithError(err).Error("Failed to produce ID token.")
|
|
return nil, proxy.ConvertError(err)
|
|
}
|
|
return &connect.Response[v1.GetIDTokenResponse]{
|
|
Msg: &v1.GetIDTokenResponse{
|
|
Token: token,
|
|
},
|
|
}, nil
|
|
}
|