Huiwen 5ff4535ac5
Add scope flag to idp commands (#19727)
* Add `scope` flag to idp commands

* fix flag inherit
2024-05-15 16:27:53 +08:00

117 lines
3.3 KiB
Go

// Copyright (c) 2022 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 cmd
import (
"context"
"encoding/json"
"fmt"
"time"
connect "github.com/bufbuild/connect-go"
"github.com/gitpod-io/gitpod/common-go/util"
"github.com/gitpod-io/gitpod/components/public-api/go/client"
v1 "github.com/gitpod-io/gitpod/components/public-api/go/experimental/v1"
"github.com/gitpod-io/gitpod/gitpod-cli/pkg/gitpod"
supervisor "github.com/gitpod-io/gitpod/supervisor/api"
"github.com/golang-jwt/jwt/v5"
"github.com/spf13/cobra"
"golang.org/x/xerrors"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
)
var idpTokenOpts struct {
Audience []string
Decode bool
Scope string
}
var idpTokenCmd = &cobra.Command{
Use: "token",
Short: "Requests an ID token for this workspace",
RunE: func(cmd *cobra.Command, args []string) (err error) {
cmd.SilenceUsage = true
ctx, cancel := context.WithTimeout(cmd.Context(), 5*time.Second)
defer cancel()
tkn, err := idpToken(ctx, idpTokenOpts.Audience, idpTokenOpts.Scope)
token, _, err := jwt.NewParser().ParseUnverified(tkn, jwt.MapClaims{})
if err != nil {
return err
}
// If the user wants to decode the token, then do so.
if idpTokenOpts.Decode {
output := map[string]interface{}{
"Header": token.Header,
"Payload": token.Claims,
}
// The header and payload are then marshalled into a map and printed to the screen.
outputPretty, err := json.MarshalIndent(output, "", " ")
if err != nil {
return xerrors.Errorf("Failed to marshal output: %w", err)
}
fmt.Printf("%s\n", outputPretty)
return nil
}
fmt.Println(tkn)
return nil
},
}
func idpToken(ctx context.Context, audience []string, scope string) (idToken string, err error) {
wsInfo, err := gitpod.GetWSInfo(ctx)
if err != nil {
return "", err
}
supervisorConn, err := grpc.Dial(util.GetSupervisorAddress(), grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return "", xerrors.Errorf("failed connecting to supervisor: %w", err)
}
defer supervisorConn.Close()
clientToken, err := supervisor.NewTokenServiceClient(supervisorConn).GetToken(ctx, &supervisor.GetTokenRequest{
Host: wsInfo.GitpodApi.Host,
Kind: "gitpod",
Scope: []string{
"function:getWorkspace",
},
})
if err != nil {
return "", xerrors.Errorf("failed getting token from supervisor: %w", err)
}
c, err := client.New(client.WithCredentials(clientToken.Token), client.WithURL("https://api."+wsInfo.GitpodApi.Host))
if err != nil {
return "", err
}
tkn, err := c.IdentityProvider.GetIDToken(ctx, &connect.Request[v1.GetIDTokenRequest]{
Msg: &v1.GetIDTokenRequest{
Audience: audience,
WorkspaceId: wsInfo.WorkspaceId,
Scope: scope,
},
})
if err != nil {
return "", err
}
return tkn.Msg.Token, nil
}
func init() {
idpCmd.AddCommand(idpTokenCmd)
idpTokenCmd.Flags().StringArrayVar(&idpTokenOpts.Audience, "audience", nil, "audience of the ID token")
_ = idpTokenCmd.MarkFlagRequired("audience")
idpTokenCmd.Flags().BoolVar(&idpTokenOpts.Decode, "decode", false, "decode token to JSON")
idpTokenCmd.Flags().StringVar(&idpTokenOpts.Scope, "scope", "", "scopes string of the ID token")
}