| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- package authmanagement
- import (
- "context"
- "fmt"
- "time"
- "github.com/google/uuid"
- "github.com/dgrijalva/jwt-go"
- "github.com/porter-dev/porter/internal/auth/token"
- "github.com/porter-dev/porter/internal/models"
- "github.com/porter-dev/porter/internal/telemetry"
- "connectrpc.com/connect"
- porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
- )
- // APIToken returns an encoded token for programmatic access to the Porter UI. Currently, this token is hardcoded
- // to use the "porter-agent-token" name. Once this endpoint is used for multiple tokens, the GRPC request should
- // include the token name or type as an argument.
- func (a AuthManagementService) APIToken(ctx context.Context, req *connect.Request[porterv1.APITokenRequest]) (*connect.Response[porterv1.APITokenResponse], error) {
- ctx, span := telemetry.NewSpan(ctx, "auth-endpoint-api-token")
- defer span.End()
- resp := connect.NewResponse(&porterv1.APITokenResponse{})
- if req == nil {
- err := telemetry.Error(ctx, span, nil, "missing request")
- return resp, connect.NewError(connect.CodeInvalidArgument, err)
- }
- if req.Msg == nil {
- err := telemetry.Error(ctx, span, nil, "missing request message")
- return resp, connect.NewError(connect.CodeInvalidArgument, err)
- }
- if req.Msg.ProjectId == 0 {
- err := telemetry.Error(ctx, span, nil, "missing project id")
- return resp, connect.NewError(connect.CodeInvalidArgument, err)
- }
- telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "project-id", Value: req.Msg.ProjectId})
- existingTokens, err := a.Config.APITokenManager.ListAPITokensByProjectID(uint(req.Msg.ProjectId))
- if err != nil {
- return resp, telemetry.Error(ctx, span, err, "error listing api tokens")
- }
- var apiToken *models.APIToken
- for _, tok := range existingTokens {
- if tok.Name == "porter-agent-token" {
- telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "token-exists", Value: true})
- apiToken = tok
- }
- }
- if apiToken == nil {
- telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "token-exists", Value: false})
- tokenID, err := uuid.NewUUID()
- if err != nil {
- return resp, telemetry.Error(ctx, span, err, "error generating tokenID")
- }
- expiresAt := time.Now().Add(time.Hour * 24 * 365)
- apiToken = &models.APIToken{
- UniqueID: tokenID.String(),
- ProjectID: uint(req.Msg.ProjectId),
- Expiry: &expiresAt,
- Revoked: false,
- PolicyUID: "developer",
- PolicyName: "developer",
- Name: "porter-agent-token",
- }
- apiToken, err = a.Config.APITokenManager.CreateAPIToken(apiToken)
- if err != nil {
- return resp, telemetry.Error(ctx, span, err, "error creating api token")
- }
- }
- telemetry.WithAttributes(span,
- telemetry.AttributeKV{Key: "token-id", Value: apiToken.UniqueID},
- telemetry.AttributeKV{Key: "expiry", Value: apiToken.Expiry.UTC().String()},
- )
- now := time.Now().UTC()
- token := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims{
- "sub_kind": "porter-agent",
- "sub": string(token.API),
- "iat": fmt.Sprintf("%d", now.Unix()),
- "project_id": apiToken.ProjectID,
- "token_id": apiToken.UniqueID,
- })
- encodedToken, err := token.SignedString([]byte(a.Config.TokenGeneratorSecret))
- if err != nil {
- return resp, telemetry.Error(ctx, span, err, "error signing token")
- }
- resp.Msg.Token = encodedToken
- return resp, nil
- }
|