handlers: add refresh token helpers

Helpers for parsing, marshalling and converting types
to domain types.
This commit is contained in:
Johan Brandhorst-Satzkorn committed 2024-01-05 07:43:19 -08:00
1 parent 84e5b9401d
commit a5b24e79b9
2 files changed
+318

No files matched your search

@@ -0,0 +1,89 @@
// 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/types/resource"
"github.com/mr-tron/base58"
"google.golang.org/protobuf/proto"
)
// ParseRefreshToken parses a refresh token from the input, returning
// an error if the parsing fails.
func ParseRefreshToken(ctx context.Context, token string) (*pbs.ListRefreshToken, error) {
const op = "handlers.ParseRefreshToken"
marshaled, err := base58.Decode(token)
if err != nil {
return nil, errors.Wrap(ctx, err, op)
}
var tok pbs.ListRefreshToken
if err := proto.Unmarshal(marshaled, &tok); err != nil {
return nil, errors.Wrap(ctx, err, op)
}
return &tok, nil
}
// MarshalRefreshToken marshals a refresh token to its string representation.
func MarshalRefreshToken(ctx context.Context, token *pbs.ListRefreshToken) (string, error) {
const op = "handlers.MarshalRefreshToken"
if token == nil {
return "", errors.New(ctx, errors.InvalidParameter, op, "token is required")
}
marshaled, err := proto.Marshal(token)
if err != nil {
return "", errors.Wrap(ctx, err, op)
}
return base58.Encode(marshaled), nil
}
// RefreshTokenResourceToResource translates a protobuf refresh token resource type
// into a useable domain layer boundary resource type.
func RefreshTokenResourceToResource(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
}
}
@@ -0,0 +1,229 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package handlers_test
import (
"context"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/hashicorp/boundary/internal/daemon/controller/handlers"
pbs "github.com/hashicorp/boundary/internal/gen/controller/api/services"
"github.com/hashicorp/boundary/internal/types/resource"
"github.com/mr-tron/base58"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"google.golang.org/protobuf/testing/protocmp"
"google.golang.org/protobuf/types/known/timestamppb"
)
func Test_ParseRefreshToken(t *testing.T) {
testToken := &pbs.ListRefreshToken{
CreatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)),
ResourceType: pbs.ResourceType_RESOURCE_TYPE_SESSION,
GrantsHash: []byte("some hash"),
LastItemId: "s_1234567890",
LastItemUpdatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)),
}
tokBytes, err := proto.Marshal(testToken)
require.NoError(t, err)
tokString := base58.Encode(tokBytes)
tests := []struct {
name string
token string
want *pbs.ListRefreshToken
wantErr bool
}{
{
name: "valid",
token: tokString,
want: testToken,
},
{
name: "empty token",
token: "",
wantErr: true,
},
{
name: "invalid base58",
token: "not a token",
wantErr: true,
},
{
name: "invalid proto",
token: base58.Encode([]byte("not a token")),
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := handlers.ParseRefreshToken(context.Background(), tt.token)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform()))
})
}
}
func Test_MarshalRefreshToken(t *testing.T) {
testToken := &pbs.ListRefreshToken{
CreatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)),
ResourceType: pbs.ResourceType_RESOURCE_TYPE_SESSION,
GrantsHash: []byte("some hash"),
LastItemId: "s_1234567890",
LastItemUpdatedTime: timestamppb.New(time.Date(2000, time.January, 1, 0, 0, 0, 0, time.UTC)),
}
tokBytes, err := proto.Marshal(testToken)
require.NoError(t, err)
tokString := base58.Encode(tokBytes)
tests := []struct {
name string
token *pbs.ListRefreshToken
want string
wantErr bool
}{
{
name: "valid",
token: testToken,
want: tokString,
},
{
name: "nil token",
token: nil,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := handlers.MarshalRefreshToken(context.Background(), tt.token)
if tt.wantErr {
require.Error(t, err)
return
}
require.NoError(t, err)
require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform()))
})
}
}
func TestRefreshTokenResourceToResource(t *testing.T) {
tests := []struct {
name string
rt pbs.ResourceType
want resource.Type
}{
{
name: "default unknown",
rt: 0,
want: resource.Unknown,
},
{
name: "account",
rt: pbs.ResourceType_RESOURCE_TYPE_ACCOUNT,
want: resource.Account,
},
{
name: "auth_method",
rt: pbs.ResourceType_RESOURCE_TYPE_AUTH_METHOD,
want: resource.AuthMethod,
},
{
name: "auth_token",
rt: pbs.ResourceType_RESOURCE_TYPE_AUTH_TOKEN,
want: resource.AuthToken,
},
{
name: "credential_library",
rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_LIBRARY,
want: resource.CredentialLibrary,
},
{
name: "credential_store",
rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL_STORE,
want: resource.CredentialStore,
},
{
name: "credential",
rt: pbs.ResourceType_RESOURCE_TYPE_CREDENTIAL,
want: resource.Credential,
},
{
name: "group",
rt: pbs.ResourceType_RESOURCE_TYPE_GROUP,
want: resource.Group,
},
{
name: "host_catalog",
rt: pbs.ResourceType_RESOURCE_TYPE_HOST_CATALOG,
want: resource.HostCatalog,
},
{
name: "host_set",
rt: pbs.ResourceType_RESOURCE_TYPE_HOST_SET,
want: resource.HostSet,
},
{
name: "host",
rt: pbs.ResourceType_RESOURCE_TYPE_HOST,
want: resource.Host,
},
{
name: "managed_group",
rt: pbs.ResourceType_RESOURCE_TYPE_MANAGED_GROUP,
want: resource.ManagedGroup,
},
{
name: "role",
rt: pbs.ResourceType_RESOURCE_TYPE_ROLE,
want: resource.Role,
},
{
name: "scope",
rt: pbs.ResourceType_RESOURCE_TYPE_SCOPE,
want: resource.Scope,
},
{
name: "session_recording",
rt: pbs.ResourceType_RESOURCE_TYPE_SESSION_RECORDING,
want: resource.SessionRecording,
},
{
name: "session",
rt: pbs.ResourceType_RESOURCE_TYPE_SESSION,
want: resource.Session,
},
{
name: "storage_bucket",
rt: pbs.ResourceType_RESOURCE_TYPE_STORAGE_BUCKET,
want: resource.StorageBucket,
},
{
name: "target",
rt: pbs.ResourceType_RESOURCE_TYPE_TARGET,
want: resource.Target,
},
{
name: "user",
rt: pbs.ResourceType_RESOURCE_TYPE_USER,
want: resource.User,
},
{
name: "worker",
rt: pbs.ResourceType_RESOURCE_TYPE_WORKER,
want: resource.Worker,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := handlers.RefreshTokenResourceToResource(tt.rt)
require.Empty(t, cmp.Diff(got, tt.want, protocmp.Transform()))
})
}
}