Skip to content

Commit

Permalink
Auth token pagination (#4113)
Browse files Browse the repository at this point in the history
* Add pagination support for auth tokens

* change analyze count estimate comment

* adjust queries for efficiency and security

* even more efficient queries
  • Loading branch information
mikemountain authored and johanbrandhorst committed Jan 4, 2024
1 parent 8b479a2 commit aa931e0
Show file tree
Hide file tree
Showing 18 changed files with 2,214 additions and 168 deletions.
47 changes: 45 additions & 2 deletions internal/authtoken/authtoken.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ import (
"github.com/hashicorp/boundary/globals"
"github.com/hashicorp/boundary/internal/authtoken/store"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/db/timestamp"
"github.com/hashicorp/boundary/internal/errors"
"github.com/hashicorp/boundary/internal/gen/controller/tokens"
"github.com/hashicorp/boundary/internal/kms"
"github.com/hashicorp/boundary/internal/types/resource"
wrapping "github.com/hashicorp/go-kms-wrapping/v2"
"github.com/hashicorp/go-kms-wrapping/v2/extras/structwrapping"
"github.com/hashicorp/go-secure-stdlib/base62"
Expand Down Expand Up @@ -56,8 +58,8 @@ type AuthToken struct {
tableName string `gorm:"-"`
}

func (s *AuthToken) clone() *AuthToken {
cp := proto.Clone(s.AuthToken)
func (at *AuthToken) clone() *AuthToken {
cp := proto.Clone(at.AuthToken)
return &AuthToken{
AuthToken: cp.(*store.AuthToken),
}
Expand Down Expand Up @@ -167,3 +169,44 @@ func EncryptToken(ctx context.Context, kmsCache *kms.Kms, scopeId, publicId, tok

return globals.ServiceTokenV1 + encoded, nil
}

// GetResourceType returns the resource type of the AuthToken
func (at AuthToken) GetResourceType() resource.Type {
return resource.AuthToken
}

func (at AuthToken) GetUpdateTime() *timestamp.Timestamp {
return at.UpdateTime
}

func (at AuthToken) GetCreateTime() *timestamp.Timestamp {
return at.CreateTime
}

// GetDescription returns an empty string so that
// AuthToken will satisfy resource requirements
func (at AuthToken) GetDescription() string {
return ""
}

// GetName returns an empty string so that
// AuthToken will satisfy resource requirements
func (at AuthToken) GetName() string {
return ""
}

// GetVersion returns 0 so that
// AuthToken will satisfy resource requirements
func (at AuthToken) GetVersion() uint32 {
return 0
}

type deletedAuthToken struct {
PublicId string `gorm:"primary_key"`
DeleteTime *timestamp.Timestamp
}

// TableName returns the tablename to override the default gorm table name
func (s *deletedAuthToken) TableName() string {
return "auth_token_deleted"
}
10 changes: 10 additions & 0 deletions internal/authtoken/options.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"github.com/hashicorp/boundary/internal/auth/password"
"github.com/hashicorp/boundary/internal/db"
"github.com/hashicorp/boundary/internal/iam"
"github.com/hashicorp/boundary/internal/pagination"
)

var (
Expand Down Expand Up @@ -38,6 +39,7 @@ type options struct {
withPublicId string
withPasswordOptions []password.Option
withIamOptions []iam.Option
withStartPageAfterItem pagination.Item
}

func getDefaultOptions() options {
Expand Down Expand Up @@ -116,3 +118,11 @@ func WithIamOptions(with ...iam.Option) Option {
o.withIamOptions = with
}
}

// WithStartPageAfterItem is used to paginate over the results.
// The next page will start after the provided item.
func WithStartPageAfterItem(item pagination.Item) Option {
return func(o *options) {
o.withStartPageAfterItem = item
}
}
182 changes: 182 additions & 0 deletions internal/authtoken/query.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,182 @@
// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1

package authtoken

const (
estimateCountAuthTokens = `
select reltuples::bigint as estimate from pg_class where oid in ('auth_token'::regclass)
`
listAuthTokensTemplate = `
with auth_tokens as (
select public_id,
auth_account_id,
create_time,
update_time,
approximate_last_access_time,
expiration_time,
status
from auth_token
order by create_time desc, public_id asc
limit %d
),
auth_accounts as (
select public_id,
auth_method_id,
scope_id,
iam_user_id,
iam_user_scope_id
from auth_account
where %s
and public_id in (select auth_account_id from auth_tokens)
),
final as (
select at.public_id,
at.auth_account_id,
aa.auth_method_id,
aa.scope_id,
aa.iam_user_id,
aa.iam_user_scope_id,
at.create_time,
at.update_time,
at.approximate_last_access_time,
at.expiration_time,
at.status
from auth_tokens at
join auth_accounts aa on aa.public_id = at.auth_account_id
)
select *
from final
order by create_time desc, public_id asc;
`
listAuthTokensPageTemplate = `
with auth_tokens as (
select public_id,
auth_account_id,
create_time,
update_time,
approximate_last_access_time,
expiration_time,
status
from auth_token
where (create_time, public_id) < (@last_item_create_time, @last_item_id)
order by create_time desc, public_id asc
limit %d
),
auth_accounts as (
select public_id,
auth_method_id,
scope_id,
iam_user_id,
iam_user_scope_id
from auth_account
where %s
and public_id in (select auth_account_id from auth_tokens)
),
final as (
select at.public_id,
at.auth_account_id,
aa.auth_method_id,
aa.scope_id,
aa.iam_user_id,
aa.iam_user_scope_id,
at.create_time,
at.update_time,
at.approximate_last_access_time,
at.expiration_time,
at.status
from auth_tokens at
join auth_accounts aa on aa.public_id = at.auth_account_id
)
select *
from final
order by create_time desc, public_id asc;
`
refreshAuthTokensTemplate = `
with auth_tokens as (
select public_id,
auth_account_id,
create_time,
update_time,
approximate_last_access_time,
expiration_time,
status
from auth_token
where update_time > @updated_after_time
order by update_time desc, public_id asc
limit %d
),
auth_accounts as (
select public_id,
auth_method_id,
scope_id,
iam_user_id,
iam_user_scope_id
from auth_account
where %s
and public_id in (select auth_account_id from auth_tokens)
),
final as (
select at.public_id,
at.auth_account_id,
aa.auth_method_id,
aa.scope_id,
aa.iam_user_id,
aa.iam_user_scope_id,
at.create_time,
at.update_time,
at.approximate_last_access_time,
at.expiration_time,
at.status
from auth_tokens at
join auth_accounts aa on aa.public_id = at.auth_account_id
)
select *
from final
order by update_time desc, public_id asc;
`
refreshAuthTokensPageTemplate = `
with auth_tokens as (
select public_id,
auth_account_id,
create_time,
update_time,
approximate_last_access_time,
expiration_time,
status
from auth_token
where update_time > @updated_after_time
and (update_time, public_id) < (@last_item_update_time, @last_item_id)
order by update_time desc, public_id asc
limit %d
),
auth_accounts as (
select public_id,
auth_method_id,
scope_id,
iam_user_id,
iam_user_scope_id
from auth_account
where %s
and public_id in (select auth_account_id from auth_tokens)
),
final as (
select at.public_id,
at.auth_account_id,
aa.auth_method_id,
aa.scope_id,
aa.iam_user_id,
aa.iam_user_scope_id,
at.create_time,
at.update_time,
at.approximate_last_access_time,
at.expiration_time,
at.status
from auth_tokens at
join auth_accounts aa on aa.public_id = at.auth_account_id
)
select *
from final
order by update_time desc, public_id asc;
`
)
Loading

0 comments on commit aa931e0

Please sign in to comment.