* internal/target: fix some target tests We had dupes and missing test cases, this fixes them. * internal/handlers: move listtoken parsing into helper This significantly simplifies the parsing logic needed in each handler.
183 lines
6.1 KiB
Go
183 lines
6.1 KiB
Go
// Copyright (c) HashiCorp, Inc.
|
|
// SPDX-License-Identifier: BUSL-1.1
|
|
|
|
package handlers
|
|
|
|
import (
|
|
"context"
|
|
|
|
"github.com/hashicorp/boundary/internal/errors"
|
|
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
|
|
"github.com/hashicorp/boundary/internal/listtoken"
|
|
"github.com/hashicorp/boundary/internal/types/resource"
|
|
"github.com/mr-tron/base58"
|
|
"google.golang.org/grpc/codes"
|
|
"google.golang.org/protobuf/proto"
|
|
"google.golang.org/protobuf/types/known/timestamppb"
|
|
)
|
|
|
|
// ParseListToken parses a list token from the input, returning
|
|
// an error if the parsing fails.
|
|
func ParseListToken(
|
|
ctx context.Context,
|
|
token string,
|
|
expectedResourceType resource.Type,
|
|
expectedGrantsHash []byte,
|
|
) (*listtoken.Token, error) {
|
|
const op = "handlers.ParseListToken"
|
|
marshaled, err := base58.Decode(token)
|
|
if err != nil {
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
}
|
|
var tok pbs.ListToken
|
|
if err := proto.Unmarshal(marshaled, &tok); err != nil {
|
|
return nil, errors.Wrap(ctx, err, op)
|
|
}
|
|
var listToken *listtoken.Token
|
|
switch st := tok.Token.(type) {
|
|
case *pbs.ListToken_PaginationToken:
|
|
listToken, err = listtoken.NewPagination(
|
|
ctx,
|
|
tok.CreateTime.AsTime(),
|
|
ListTokenResourceToResource(tok.ResourceType),
|
|
tok.GrantsHash,
|
|
st.PaginationToken.LastItemId,
|
|
st.PaginationToken.LastItemCreateTime.AsTime(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case *pbs.ListToken_StartRefreshToken:
|
|
listToken, err = listtoken.NewStartRefresh(
|
|
ctx,
|
|
tok.CreateTime.AsTime(),
|
|
ListTokenResourceToResource(tok.ResourceType),
|
|
tok.GrantsHash,
|
|
st.StartRefreshToken.PreviousDeletedIdsTime.AsTime(),
|
|
st.StartRefreshToken.PreviousPhaseUpperBound.AsTime(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case *pbs.ListToken_RefreshToken:
|
|
listToken, err = listtoken.NewRefresh(
|
|
ctx,
|
|
tok.CreateTime.AsTime(),
|
|
ListTokenResourceToResource(tok.ResourceType),
|
|
tok.GrantsHash,
|
|
st.RefreshToken.PreviousDeletedIdsTime.AsTime(),
|
|
st.RefreshToken.PhaseUpperBound.AsTime(),
|
|
st.RefreshToken.PhaseLowerBound.AsTime(),
|
|
st.RefreshToken.LastItemId,
|
|
st.RefreshToken.LastItemUpdateTime.AsTime(),
|
|
)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
default:
|
|
return nil, ApiErrorWithCodeAndMessage(codes.InvalidArgument, "unexpected list token subtype: %T", st)
|
|
}
|
|
if err := listToken.Validate(ctx, expectedResourceType, expectedGrantsHash); err != nil {
|
|
return nil, err
|
|
}
|
|
return listToken, nil
|
|
}
|
|
|
|
// MarshalListToken assembles and marshals a list token to its string representation.
|
|
func MarshalListToken(ctx context.Context, token *listtoken.Token, resourceType pbs.ResourceType) (string, error) {
|
|
const op = "handlers.MarshalListToken"
|
|
switch {
|
|
case token == nil:
|
|
return "", errors.New(ctx, errors.InvalidParameter, op, "token is required")
|
|
case resourceType == pbs.ResourceType_RESOURCE_TYPE_UNSPECIFIED:
|
|
return "", errors.New(ctx, errors.InvalidParameter, op, "missing resource type")
|
|
case token.ResourceType != ListTokenResourceToResource(resourceType):
|
|
return "", errors.New(ctx, errors.Internal, op, "list token resource type does not match expected resource type")
|
|
}
|
|
lt := &pbs.ListToken{
|
|
CreateTime: timestamppb.New(token.CreateTime),
|
|
ResourceType: resourceType,
|
|
GrantsHash: token.GrantsHash,
|
|
}
|
|
switch st := token.Subtype.(type) {
|
|
case *listtoken.PaginationToken:
|
|
lt.Token = &pbs.ListToken_PaginationToken{
|
|
PaginationToken: &pbs.PaginationToken{
|
|
LastItemId: st.LastItemId,
|
|
LastItemCreateTime: timestamppb.New(st.LastItemCreateTime),
|
|
},
|
|
}
|
|
case *listtoken.StartRefreshToken:
|
|
lt.Token = &pbs.ListToken_StartRefreshToken{
|
|
StartRefreshToken: &pbs.StartRefreshToken{
|
|
PreviousPhaseUpperBound: timestamppb.New(st.PreviousPhaseUpperBound),
|
|
PreviousDeletedIdsTime: timestamppb.New(st.PreviousDeletedIdsTime),
|
|
},
|
|
}
|
|
case *listtoken.RefreshToken:
|
|
lt.Token = &pbs.ListToken_RefreshToken{
|
|
RefreshToken: &pbs.RefreshToken{
|
|
PhaseUpperBound: timestamppb.New(st.PhaseUpperBound),
|
|
PhaseLowerBound: timestamppb.New(st.PhaseLowerBound),
|
|
PreviousDeletedIdsTime: timestamppb.New(st.PreviousDeletedIdsTime),
|
|
LastItemId: st.LastItemId,
|
|
LastItemUpdateTime: timestamppb.New(st.LastItemUpdateTime),
|
|
},
|
|
}
|
|
default:
|
|
return "", errors.New(ctx, errors.Internal, op, "unexpected list token subtype")
|
|
}
|
|
marshaled, err := proto.Marshal(lt)
|
|
if err != nil {
|
|
return "", errors.Wrap(ctx, err, op)
|
|
}
|
|
return base58.Encode(marshaled), nil
|
|
}
|
|
|
|
// ListTokenResourceToResource translates a protobuf list token resource type
|
|
// into a useable domain layer boundary resource type.
|
|
func ListTokenResourceToResource(rt pbs.ResourceType) resource.Type {
|
|
switch rt {
|
|
case pbs.ResourceType_RESOURCE_TYPE_ACCOUNT:
|
|
return resource.Account
|
|
case pbs.ResourceType_RESOURCE_TYPE_AUTH_METHOD:
|
|
return resource.AuthMethod
|
|
case pbs.ResourceType_RESOURCE_TYPE_AUTH_TOKEN:
|
|
return resource.AuthToken
|
|
case pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_LIBRARY:
|
|
return resource.CredentialLibrary
|
|
case pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_STORE:
|
|
return resource.CredentialStore
|
|
case pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL:
|
|
return resource.Credential
|
|
case pbs.ResourceType_RESOURCE_TYPE_GROUP:
|
|
return resource.Group
|
|
case pbs.ResourceType_RESOURCE_TYPE_HOST_CATALOG:
|
|
return resource.HostCatalog
|
|
case pbs.ResourceType_RESOURCE_TYPE_HOST_SET:
|
|
return resource.HostSet
|
|
case pbs.ResourceType_RESOURCE_TYPE_HOST:
|
|
return resource.Host
|
|
case pbs.ResourceType_RESOURCE_TYPE_MANAGED_GROUP:
|
|
return resource.ManagedGroup
|
|
case pbs.ResourceType_RESOURCE_TYPE_ROLE:
|
|
return resource.Role
|
|
case pbs.ResourceType_RESOURCE_TYPE_SCOPE:
|
|
return resource.Scope
|
|
case pbs.ResourceType_RESOURCE_TYPE_SESSION_RECORDING:
|
|
return resource.SessionRecording
|
|
case pbs.ResourceType_RESOURCE_TYPE_SESSION:
|
|
return resource.Session
|
|
case pbs.ResourceType_RESOURCE_TYPE_STORAGE_BUCKET:
|
|
return resource.StorageBucket
|
|
case pbs.ResourceType_RESOURCE_TYPE_TARGET:
|
|
return resource.Target
|
|
case pbs.ResourceType_RESOURCE_TYPE_USER:
|
|
return resource.User
|
|
case pbs.ResourceType_RESOURCE_TYPE_WORKER:
|
|
return resource.Worker
|
|
default:
|
|
return resource.Unknown
|
|
}
|
|
}
|