feat(apptoken): Implement ListRefreshPage (#6387)

Co-authored-by: Michael Milton <mikemountain@users.noreply.github.com>
pull/6420/merge
April-May 3 months ago committed by GitHub
parent 5f3d4a449d
commit b4bd07fe3c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

@ -890,9 +890,39 @@ func TestRepository_queryAppTokens(t *testing.T) {
orgUser := iam.TestUser(t, iamRepo, org1.PublicId)
// Create app tokens
gToken := TestAppToken(t, repo, globals.GlobalPrefix, globalUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
orgToken := TestAppToken(t, repo, org1.PublicId, orgUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
projToken := TestAppToken(t, repo, proj1.PublicId, orgUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
gToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: globals.GlobalPrefix,
CreatedByUserId: globalUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
orgToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: org1.PublicId,
CreatedByUserId: orgUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
projToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: proj1.PublicId,
CreatedByUserId: orgUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
testCases := []struct {
name string
@ -990,9 +1020,39 @@ func TestRepository_listAppTokens(t *testing.T) {
orgUser := iam.TestUser(t, iamRepo, org1.PublicId)
// Create app tokens
gToken := TestAppToken(t, repo, globals.GlobalPrefix, globalUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
orgToken := TestAppToken(t, repo, org1.PublicId, orgUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
projToken := TestAppToken(t, repo, proj1.PublicId, orgUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
gToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: globals.GlobalPrefix,
CreatedByUserId: globalUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
orgToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: org1.PublicId,
CreatedByUserId: orgUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
projToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: proj1.PublicId,
CreatedByUserId: orgUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
testCases := []struct {
name string
@ -1086,7 +1146,17 @@ func TestRepository_listAppTokens(t *testing.T) {
// Create enough tokens to exceed the limit
for range make([]int, 5) {
TestAppToken(t, repo, orgExceedLimit.PublicId, orgExceedLimitUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, "individual")
TestCreateAppToken(t, repo, &AppToken{
ScopeId: orgExceedLimit.PublicId,
CreatedByUserId: orgExceedLimitUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
}
tokens, _, err := repo.listAppTokens(ctx, []string{orgExceedLimit.PublicId}, []Option{WithLimit(10)}...)
@ -1112,13 +1182,50 @@ func TestRepository_listAppTokensRefresh(t *testing.T) {
orgUser := iam.TestUser(t, iamRepo, org1.PublicId)
expireInSixSeconds := timestamp.New(timestamp.Now().AsTime().Add(6 * time.Second))
staleInFourSeconds := uint32(4)
// Create app tokens
allTokens := []*AppToken{}
for range 3 {
gToken := TestAppToken(t, repo, globals.GlobalPrefix, globalUser, 4, expireInSixSeconds, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
orgToken := TestAppToken(t, repo, org1.PublicId, orgUser, 4, expireInSixSeconds, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
projToken := TestAppToken(t, repo, proj1.PublicId, orgUser, 4, expireInSixSeconds, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
gToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: globals.GlobalPrefix,
CreatedByUserId: globalUser.PublicId,
ExpirationTime: expireInSixSeconds,
TimeToStaleSeconds: staleInFourSeconds,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
orgToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: org1.PublicId,
CreatedByUserId: orgUser.PublicId,
ExpirationTime: expireInSixSeconds,
TimeToStaleSeconds: staleInFourSeconds,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
projToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: proj1.PublicId,
CreatedByUserId: orgUser.PublicId,
ExpirationTime: expireInSixSeconds,
TimeToStaleSeconds: staleInFourSeconds,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
allTokens = append(allTokens, gToken, orgToken, projToken)
}
@ -1261,9 +1368,39 @@ func TestRepository_estimatedCount(t *testing.T) {
org, proj := iam.TestScopes(t, iamRepo, iam.WithName("org1"), iam.WithDescription("Test Org 1"))
orgUser := iam.TestUser(t, iamRepo, org.PublicId)
gToken := TestAppToken(t, repo, globals.GlobalPrefix, globalUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
oToken := TestAppToken(t, repo, org.PublicId, orgUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
pToken := TestAppToken(t, repo, proj.PublicId, orgUser, 0, nil, []string{"ids=*;type=scope;actions=list,read"}, true, globals.GrantScopeIndividual)
gToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: globals.GlobalPrefix,
CreatedByUserId: globalUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
oToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: org.PublicId,
CreatedByUserId: orgUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
pToken := TestCreateAppToken(t, repo, &AppToken{
ScopeId: proj.PublicId,
CreatedByUserId: orgUser.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: []string{"ids=*;type=scope;actions=list,read"},
GrantedScopes: []string{globals.GrantScopeThis},
},
},
})
// Run analyze to update estimate
_, err = sqlDb.ExecContext(ctx, "analyze")

@ -47,7 +47,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=group;actions=list",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.Group, resource.Scope},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -55,9 +55,9 @@ func TestGrantsForToken(t *testing.T) {
wantErr: false,
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: "global",
AppTokenScopeId: globals.GlobalPrefix,
AppTokenParentScopeId: "",
GrantScopeId: "descendants",
GrantScopeId: globals.GrantScopeDescendants,
Grant: "ids=*;type=group;actions=list,ids=*;type=scope;actions=list,read",
},
},
@ -69,7 +69,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=account;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.Account},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -77,9 +77,9 @@ func TestGrantsForToken(t *testing.T) {
wantErr: false,
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: "global",
AppTokenScopeId: globals.GlobalPrefix,
AppTokenParentScopeId: "",
GrantScopeId: "descendants",
GrantScopeId: globals.GrantScopeDescendants,
Grant: "ids=*;type=account;actions=list,read",
},
},
@ -91,7 +91,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=credential-library;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.CredentialLibrary},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -99,9 +99,9 @@ func TestGrantsForToken(t *testing.T) {
wantErr: false,
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: "global",
AppTokenScopeId: globals.GlobalPrefix,
AppTokenParentScopeId: "",
GrantScopeId: "descendants",
GrantScopeId: globals.GrantScopeDescendants,
Grant: "ids=*;type=credential-library;actions=list,read",
},
},
@ -114,7 +114,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=scope;actions=list,read",
},
grantThisScope: true,
grantScope: "children",
grantScope: globals.GrantScopeChildren,
rTypes: []resource.Type{resource.Account, resource.Scope},
tokenScopeId: org1.PublicId,
reqScopeId: org1.PublicId,
@ -123,8 +123,8 @@ func TestGrantsForToken(t *testing.T) {
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: org1.PublicId,
AppTokenParentScopeId: "global",
GrantScopeId: "children",
AppTokenParentScopeId: globals.GlobalPrefix,
GrantScopeId: globals.GrantScopeChildren,
Grant: "ids=*;type=account;actions=list,ids=*;type=scope;actions=list,read",
},
},
@ -136,7 +136,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=auth-method;actions=list,read",
},
grantThisScope: true,
grantScope: "children",
grantScope: globals.GrantScopeChildren,
rTypes: []resource.Type{resource.AuthMethod},
tokenScopeId: org1.PublicId,
reqScopeId: org1.PublicId,
@ -145,8 +145,8 @@ func TestGrantsForToken(t *testing.T) {
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: org1.PublicId,
AppTokenParentScopeId: "global",
GrantScopeId: "children",
AppTokenParentScopeId: globals.GlobalPrefix,
GrantScopeId: globals.GrantScopeChildren,
Grant: "ids=*;type=auth-method;actions=list,read",
},
},
@ -158,7 +158,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=target;actions=list,read",
},
grantThisScope: true,
grantScope: "children",
grantScope: globals.GrantScopeChildren,
rTypes: []resource.Type{resource.Target},
tokenScopeId: org1.PublicId,
reqScopeId: org1.PublicId,
@ -167,8 +167,8 @@ func TestGrantsForToken(t *testing.T) {
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: org1.PublicId,
AppTokenParentScopeId: "global",
GrantScopeId: "children",
AppTokenParentScopeId: globals.GlobalPrefix,
GrantScopeId: globals.GrantScopeChildren,
Grant: "ids=*;type=target;actions=list,read",
},
},
@ -192,7 +192,7 @@ func TestGrantsForToken(t *testing.T) {
{
AppTokenScopeId: proj1.PublicId,
AppTokenParentScopeId: org1.PublicId,
GrantScopeId: "individual",
GrantScopeId: globals.GrantScopeIndividual,
Grant: "ids=*;type=host-set;actions=read,ids=*;type=host;actions=list,ids=*;type=target;actions=list,read",
},
},
@ -214,7 +214,7 @@ func TestGrantsForToken(t *testing.T) {
{
AppTokenScopeId: proj1.PublicId,
AppTokenParentScopeId: org1.PublicId,
GrantScopeId: "individual",
GrantScopeId: globals.GrantScopeIndividual,
Grant: "ids=*;type=target;actions=list,read",
},
},
@ -227,7 +227,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=scope;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.Group, resource.Scope},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -235,9 +235,9 @@ func TestGrantsForToken(t *testing.T) {
wantErr: false,
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: "global",
AppTokenScopeId: globals.GlobalPrefix,
AppTokenParentScopeId: "",
GrantScopeId: "descendants",
GrantScopeId: globals.GrantScopeDescendants,
Grant: "ids=*;type=group;actions=list,ids=*;type=scope;actions=list,read",
},
},
@ -249,7 +249,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=auth-method;actions=list,read",
},
grantThisScope: false,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.AuthMethod},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: org1.PublicId,
@ -257,9 +257,9 @@ func TestGrantsForToken(t *testing.T) {
wantErr: false,
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: "global",
AppTokenScopeId: globals.GlobalPrefix,
AppTokenParentScopeId: "",
GrantScopeId: "descendants",
GrantScopeId: globals.GrantScopeDescendants,
Grant: "ids=*;type=auth-method;actions=list,read",
},
},
@ -271,7 +271,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=target;actions=list,read",
},
grantThisScope: false,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.Target},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: proj1.PublicId,
@ -279,9 +279,9 @@ func TestGrantsForToken(t *testing.T) {
wantErr: false,
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: "global",
AppTokenScopeId: globals.GlobalPrefix,
AppTokenParentScopeId: "",
GrantScopeId: "descendants",
GrantScopeId: globals.GrantScopeDescendants,
Grant: "ids=*;type=target;actions=list,read",
},
},
@ -294,7 +294,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=auth-method;actions=list",
},
grantThisScope: true,
grantScope: "children",
grantScope: globals.GrantScopeChildren,
rTypes: []resource.Type{resource.Account, resource.AuthMethod},
tokenScopeId: org1.PublicId,
reqScopeId: org1.PublicId,
@ -303,8 +303,8 @@ func TestGrantsForToken(t *testing.T) {
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: org1.PublicId,
AppTokenParentScopeId: "global",
GrantScopeId: "children",
AppTokenParentScopeId: globals.GlobalPrefix,
GrantScopeId: globals.GrantScopeChildren,
Grant: "ids=*;type=account;actions=list,read,ids=*;type=auth-method;actions=list",
},
},
@ -317,7 +317,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=target;actions=list,read",
},
grantThisScope: false,
grantScope: "children",
grantScope: globals.GrantScopeChildren,
rTypes: []resource.Type{resource.Host, resource.Target},
tokenScopeId: org1.PublicId,
reqScopeId: proj1.PublicId,
@ -326,8 +326,8 @@ func TestGrantsForToken(t *testing.T) {
expectedGrants: tempGrantTuples{
{
AppTokenScopeId: org1.PublicId,
AppTokenParentScopeId: "global",
GrantScopeId: "children",
AppTokenParentScopeId: globals.GlobalPrefix,
GrantScopeId: globals.GrantScopeChildren,
Grant: "ids=*;type=host;actions=list,ids=*;type=target;actions=list,read",
},
},
@ -339,7 +339,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=scope;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: nil,
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -354,7 +354,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=scope;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.Unknown},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -369,7 +369,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=scope;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.All},
tokenScopeId: globals.GlobalPrefix,
reqScopeId: globals.GlobalPrefix,
@ -384,7 +384,7 @@ func TestGrantsForToken(t *testing.T) {
"ids=*;type=scope;actions=list,read",
},
grantThisScope: true,
grantScope: "descendants",
grantScope: globals.GrantScopeDescendants,
rTypes: []resource.Type{resource.All},
tokenScopeId: globals.GlobalPrefix,
recursive: true,
@ -402,8 +402,23 @@ func TestGrantsForToken(t *testing.T) {
opts = append(opts, WithRecursive(tc.recursive))
}
grantedScopes := []string{tc.grantScope}
if tc.grantThisScope {
grantedScopes = append(grantedScopes, globals.GrantScopeThis)
}
// Create a token with the specified grants
token := TestAppToken(t, repo, tc.tokenScopeId, tc.u, 0, nil, tc.grants, tc.grantThisScope, tc.grantScope)
token := TestCreateAppToken(t, repo, &AppToken{
ScopeId: tc.tokenScopeId,
CreatedByUserId: tc.u.PublicId,
Permissions: []AppTokenPermission{
{
Label: "test",
Grants: tc.grants,
GrantedScopes: grantedScopes,
},
},
})
// Fetch the grants for the token
gt, err := repo.GrantsForToken(ctx, token.PublicId, tc.rTypes, tc.reqScopeId, opts...)

@ -41,7 +41,7 @@ func ListPage(
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing token")
case repo == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing repo")
case withScopeIds == nil:
case len(withScopeIds) == 0:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope ids")
case tok.ResourceType != resource.AppToken:
return nil, errors.New(ctx, errors.InvalidParameter, op, "token did not have an app token resource type")

@ -45,7 +45,7 @@ func ListRefresh(
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing token")
case repo == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing repo")
case withScopeIds == nil:
case len(withScopeIds) == 0:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope ids")
case tok.ResourceType != resource.AppToken:
return nil, errors.New(ctx, errors.InvalidParameter, op, "token did not have an app token resource type")

@ -0,0 +1,83 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package apptoken
import (
"context"
"time"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/listtoken"
"github.com/hashicorp/boundary/internal/pagination"
"github.com/hashicorp/boundary/internal/types/resource"
)
// ListRefreshPage lists up to page size app tokens, filtering out entries that
// do not pass the filter item function. It will automatically request
// more app tokens from the database, at page size chunks, to fill the page.
// It will start its paging based on the information in the token.
// It returns a new list token used to continue pagination or refresh items.
// App tokens are ordered by update time descending (most recently updated first).
// App tokens may contain items that were already returned during the initial
// pagination phase. It also returns a list of any app tokens deleted since the
// last response.
func ListRefreshPage(
ctx context.Context,
grantsHash []byte,
pageSize int,
filterItemFn pagination.ListFilterFunc[*AppToken],
tok *listtoken.Token,
repo *Repository,
withScopeIds []string,
) (*pagination.ListResponse[*AppToken], error) {
const op = "apptoken.ListRefreshPage"
switch {
case len(grantsHash) == 0:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing grants hash")
case pageSize < 1:
return nil, errors.New(ctx, errors.InvalidParameter, op, "page size must be at least 1")
case filterItemFn == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing filter item callback")
case tok == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing token")
case repo == nil:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing repo")
case len(withScopeIds) == 0:
return nil, errors.New(ctx, errors.InvalidParameter, op, "missing scope ids")
case tok.ResourceType != resource.AppToken:
return nil, errors.New(ctx, errors.InvalidParameter, op, "token did not have an app token resource type")
}
rt, ok := tok.Subtype.(*listtoken.RefreshToken)
if !ok {
return nil, errors.New(ctx, errors.InvalidParameter, op, "token did not have a refresh token component")
}
listItemsFn := func(ctx context.Context, lastPageItem *AppToken, limit int) ([]*AppToken, time.Time, error) {
opts := []Option{
WithLimit(limit),
}
if lastPageItem != nil {
opts = append(opts, WithStartPageAfterItem(lastPageItem))
} else {
lastItem, err := tok.LastItem(ctx)
if err != nil {
return nil, time.Time{}, err
}
opts = append(opts, WithStartPageAfterItem(lastItem))
}
// Add the database read timeout to account for any creations missed due to concurrent
// transactions in the original list pagination phase.
return repo.listAppTokensRefresh(ctx, rt.PhaseLowerBound.Add(-globals.RefreshReadLookbackDuration), withScopeIds, opts...)
}
listDeletedIdsFn := func(ctx context.Context, since time.Time) ([]string, time.Time, error) {
// Add the database read timeout to account for any deletes missed due to concurrent
// transactions in the original list pagination phase.
return repo.listDeletedIds(ctx, since.Add(-globals.RefreshReadLookbackDuration))
}
return pagination.ListRefreshPage(ctx, grantsHash, pageSize, filterItemFn, listItemsFn, repo.estimatedCount, listDeletedIdsFn, tok)
}

File diff suppressed because it is too large Load Diff

@ -10,13 +10,12 @@ import (
"sort"
"strings"
"testing"
"time"
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/perms"
"github.com/hashicorp/boundary/internal/types/scope"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/stretchr/testify/assert"
@ -55,126 +54,25 @@ func TestRepo(t testing.TB, conn *db.DB, rootWrapper wrapping.Wrapper, opt ...Op
return repo
}
func testPublicId(t testing.TB, prefix string) string {
t.Helper()
publicId, err := db.NewPublicId(t.Context(), prefix)
require.NoError(t, err)
return publicId
}
// TestAppToken creates an app token for testing with the specified grants.
// TODO: Implement TestAppToken once AppToken functionality is added
func TestAppToken(t *testing.T, repo *Repository, scopeId string, user *iam.User, timeToStaleSeconds uint32, expirationTime *timestamp.Timestamp, grants []string, grantThisScope bool, grantScope string) *AppToken {
func TestCreateAppToken(t *testing.T, repo *Repository, token *AppToken) *AppToken {
t.Helper()
testToken := &AppToken{
PublicId: testPublicId(t, "apt_"),
ScopeId: scopeId,
Description: "test app token",
CreatedByUserId: user.PublicId,
TimeToStaleSeconds: timeToStaleSeconds,
ExpirationTime: expirationTime,
// Assign a name and description if not set
if token.Name == "" {
token.Name = fmt.Sprintf("Test App Token %s", time.Now().Format("0405.000000"))
}
tempTestAddGrants(t, repo, testToken, grants, grantThisScope, grantScope)
return testToken
}
// tempTestAddGrants is a temporary test function to add grants to the database for testing
// TODO: Replace with proper AppToken creation function once AppToken functionality is added
func tempTestAddGrants(t *testing.T, repo *Repository, token *AppToken, grants []string, grantThisScope bool, grantScope string) {
t.Helper()
ctx := t.Context()
require := require.New(t)
// Determine which table to insert into based on scope prefix
var insertTokenSQL, insertPermissionSQL string
insertGrantSQL := `
insert into app_token_permission_grant (permission_id, raw_grant, canonical_grant)
values ($1, $2, $3)
`
switch {
case strings.HasPrefix(token.ScopeId, globals.GlobalPrefix):
insertTokenSQL = `
insert into app_token_global (public_id, scope_id, name, description, created_by_user_id, time_to_stale_seconds, expiration_time)
values ($1, $2, $3, $4, $5, $6, $7)
`
insertPermissionSQL = `
insert into app_token_permission_global (private_id, app_token_id, description, grant_this_scope, grant_scope)
values ($1, $2, $3, $4, $5)
`
case strings.HasPrefix(token.ScopeId, globals.OrgPrefix):
insertTokenSQL = `
insert into app_token_org (public_id, scope_id, name, description, created_by_user_id, time_to_stale_seconds, expiration_time)
values ($1, $2, $3, $4, $5, $6, $7)
`
insertPermissionSQL = `
insert into app_token_permission_org (private_id, app_token_id, description, grant_this_scope, grant_scope)
values ($1, $2, $3, $4, $5)
`
case strings.HasPrefix(token.ScopeId, globals.ProjectPrefix):
insertTokenSQL = `
insert into app_token_project (public_id, scope_id, name, description, created_by_user_id, time_to_stale_seconds, expiration_time)
values ($1, $2, $3, $4, $5, $6, $7)
`
insertPermissionSQL = `
insert into app_token_permission_project (private_id, app_token_id, description, grant_this_scope)
values ($1, $2, $3, $4)
`
default:
t.Fatalf("invalid scope id: %s", token.ScopeId)
if token.Description == "" {
token.Description = "Test App Token Description"
}
// Insert the app token
name := fmt.Sprintf("Test App Token %s", token.PublicId)
_, err := repo.writer.Exec(ctx, insertTokenSQL, []any{token.PublicId, token.ScopeId, name, "test app token", token.CreatedByUserId, token.TimeToStaleSeconds, token.ExpirationTime})
require.NoError(err)
// Create a permission for this token
permissionId := testPublicId(t, "aptp_")
// Default to 'descendants' if not specified for global/org scopes
if grantScope == "" && (strings.HasPrefix(token.ScopeId, globals.GlobalPrefix) || strings.HasPrefix(token.ScopeId, globals.OrgPrefix)) {
grantScope = globals.GrantScopeDescendants
// If no expiration time is set, set it to 1 hour from now
if token.ExpirationTime == nil {
token.ExpirationTime = timestamp.New(time.Now().Add(1 * time.Hour))
}
var permArgs []any
if strings.HasPrefix(token.ScopeId, globals.ProjectPrefix) {
// Project permissions don't have grant_scope column
permArgs = []any{permissionId, token.PublicId, "test permission", grantThisScope}
} else {
permArgs = []any{permissionId, token.PublicId, "test permission", grantThisScope, grantScope}
}
_, err = repo.writer.Exec(ctx, insertPermissionSQL, permArgs)
require.NoError(err)
// Parse and insert each grant
for _, grant := range grants {
// Parse the grant to get canonical form
perm, err := perms.Parse(ctx, perms.GrantTuple{
RoleScopeId: token.ScopeId,
GrantScopeId: token.ScopeId,
Grant: grant,
}, perms.WithSkipFinalValidation(true))
require.NoError(err)
canonicalGrant := perm.CanonicalString()
// Insert into iam_grant lookup table (required for query JOINs)
// The database trigger will automatically extract and set the resource type
_, err = repo.writer.Exec(ctx, `
insert into iam_grant (canonical_grant)
values ($1)
on conflict (canonical_grant) do nothing
`, []any{canonicalGrant})
require.NoError(err)
// Insert the grant with both raw_grant and canonical_grant
_, err = repo.writer.Exec(ctx, insertGrantSQL, []any{permissionId, grant, canonicalGrant})
require.NoError(err)
}
createdToken, err := repo.CreateAppToken(t.Context(), token)
require.NoError(t, err)
return createdToken
}
// these will eventually expand to cover org and proj
@ -415,18 +313,31 @@ func testCheckAppTokenCipher(t *testing.T, repo *Repository, appTokenId string)
return nil
}
// tempTestRevokeGlobalAppToken is a temporary test function to revoke a global app token
// tempTestRevokeAppToken is a temporary test function to revoke a global app token
// TODO: Replace with proper AppToken.Revoke function once added
func tempTestRevokeGlobalAppToken(t *testing.T, repo *Repository, tokenId string) {
func tempTestRevokeAppToken(t *testing.T, repo *Repository, tokenId, scopeId string) {
t.Helper()
ctx := t.Context()
require := require.New(t)
_, err := repo.writer.Exec(ctx, `
update app_token_global
execSQL := `
update app_token_%s
set revoked = true, update_time = now()
where public_id = $1
`, []any{tokenId})
`
var table string
switch {
case strings.HasPrefix(scopeId, globals.GlobalPrefix):
table = "global"
case strings.HasPrefix(scopeId, globals.OrgPrefix):
table = "org"
case strings.HasPrefix(scopeId, globals.ProjectPrefix):
table = "project"
default:
t.Fatalf("invalid scope id: %s", scopeId)
}
_, err := repo.writer.Exec(ctx, fmt.Sprintf(execSQL, table), []any{tokenId})
require.NoError(err)
}

Loading…
Cancel
Save