mirror of
https://github.com/portainer/portainer.git
synced 2025-07-30 18:59:41 +02:00
fix(users): optimize the /users/me API endpoint BE-11688 (#527)
Co-authored-by: andres-portainer <andres-portainer@users.noreply.github.com> Co-authored-by: LP B <xAt0mZ@users.noreply.github.com> Co-authored-by: JamesPlayer <james.player@portainer.io>
This commit is contained in:
parent
d5981a4be9
commit
fbabeb098f
7 changed files with 87 additions and 5 deletions
|
@ -6,8 +6,10 @@ import (
|
||||||
|
|
||||||
type ReadTransaction interface {
|
type ReadTransaction interface {
|
||||||
GetObject(bucketName string, key []byte, object any) error
|
GetObject(bucketName string, key []byte, object any) error
|
||||||
|
GetRawBytes(bucketName string, key []byte) ([]byte, error)
|
||||||
GetAll(bucketName string, obj any, append func(o any) (any, error)) error
|
GetAll(bucketName string, obj any, append func(o any) (any, error)) error
|
||||||
GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj any, append func(o any) (any, error)) error
|
GetAllWithKeyPrefix(bucketName string, keyPrefix []byte, obj any, append func(o any) (any, error)) error
|
||||||
|
KeyExists(bucketName string, key []byte) (bool, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Transaction interface {
|
type Transaction interface {
|
||||||
|
|
|
@ -244,6 +244,32 @@ func (connection *DbConnection) GetObject(bucketName string, key []byte, object
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (connection *DbConnection) GetRawBytes(bucketName string, key []byte) ([]byte, error) {
|
||||||
|
var value []byte
|
||||||
|
|
||||||
|
err := connection.ViewTx(func(tx portainer.Transaction) error {
|
||||||
|
var err error
|
||||||
|
value, err = tx.GetRawBytes(bucketName, key)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return value, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (connection *DbConnection) KeyExists(bucketName string, key []byte) (bool, error) {
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
err := connection.ViewTx(func(tx portainer.Transaction) error {
|
||||||
|
var err error
|
||||||
|
exists, err = tx.KeyExists(bucketName, key)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return exists, err
|
||||||
|
}
|
||||||
|
|
||||||
func (connection *DbConnection) getEncryptionKey() []byte {
|
func (connection *DbConnection) getEncryptionKey() []byte {
|
||||||
if !connection.isEncrypted {
|
if !connection.isEncrypted {
|
||||||
return nil
|
return nil
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
|
|
||||||
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
dserrors "github.com/portainer/portainer/api/dataservices/errors"
|
||||||
|
|
||||||
|
"github.com/pkg/errors"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
bolt "go.etcd.io/bbolt"
|
bolt "go.etcd.io/bbolt"
|
||||||
)
|
)
|
||||||
|
@ -31,6 +32,33 @@ func (tx *DbTransaction) GetObject(bucketName string, key []byte, object any) er
|
||||||
return tx.conn.UnmarshalObject(value, object)
|
return tx.conn.UnmarshalObject(value, object)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (tx *DbTransaction) GetRawBytes(bucketName string, key []byte) ([]byte, error) {
|
||||||
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
||||||
|
|
||||||
|
value := bucket.Get(key)
|
||||||
|
if value == nil {
|
||||||
|
return nil, fmt.Errorf("%w (bucket=%s, key=%s)", dserrors.ErrObjectNotFound, bucketName, keyToString(key))
|
||||||
|
}
|
||||||
|
|
||||||
|
if tx.conn.getEncryptionKey() != nil {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if value, err = decrypt(value, tx.conn.getEncryptionKey()); err != nil {
|
||||||
|
return value, errors.Wrap(err, "Failed decrypting object")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return value, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tx *DbTransaction) KeyExists(bucketName string, key []byte) (bool, error) {
|
||||||
|
bucket := tx.tx.Bucket([]byte(bucketName))
|
||||||
|
|
||||||
|
value := bucket.Get(key)
|
||||||
|
|
||||||
|
return value != nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (tx *DbTransaction) UpdateObject(bucketName string, key []byte, object any) error {
|
func (tx *DbTransaction) UpdateObject(bucketName string, key []byte, object any) error {
|
||||||
data, err := tx.conn.MarshalObject(object)
|
data, err := tx.conn.MarshalObject(object)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|
|
@ -9,6 +9,7 @@ import (
|
||||||
type BaseCRUD[T any, I constraints.Integer] interface {
|
type BaseCRUD[T any, I constraints.Integer] interface {
|
||||||
Create(element *T) error
|
Create(element *T) error
|
||||||
Read(ID I) (*T, error)
|
Read(ID I) (*T, error)
|
||||||
|
Exists(ID I) (bool, error)
|
||||||
ReadAll() ([]T, error)
|
ReadAll() ([]T, error)
|
||||||
Update(ID I, element *T) error
|
Update(ID I, element *T) error
|
||||||
Delete(ID I) error
|
Delete(ID I) error
|
||||||
|
@ -42,6 +43,19 @@ func (service BaseDataService[T, I]) Read(ID I) (*T, error) {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service BaseDataService[T, I]) Exists(ID I) (bool, error) {
|
||||||
|
var exists bool
|
||||||
|
|
||||||
|
err := service.Connection.ViewTx(func(tx portainer.Transaction) error {
|
||||||
|
var err error
|
||||||
|
exists, err = service.Tx(tx).Exists(ID)
|
||||||
|
|
||||||
|
return err
|
||||||
|
})
|
||||||
|
|
||||||
|
return exists, err
|
||||||
|
}
|
||||||
|
|
||||||
func (service BaseDataService[T, I]) ReadAll() ([]T, error) {
|
func (service BaseDataService[T, I]) ReadAll() ([]T, error) {
|
||||||
var collection = make([]T, 0)
|
var collection = make([]T, 0)
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,12 @@ func (service BaseDataServiceTx[T, I]) Read(ID I) (*T, error) {
|
||||||
return &element, nil
|
return &element, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (service BaseDataServiceTx[T, I]) Exists(ID I) (bool, error) {
|
||||||
|
identifier := service.Connection.ConvertToKey(int(ID))
|
||||||
|
|
||||||
|
return service.Tx.KeyExists(service.Bucket, identifier)
|
||||||
|
}
|
||||||
|
|
||||||
func (service BaseDataServiceTx[T, I]) ReadAll() ([]T, error) {
|
func (service BaseDataServiceTx[T, I]) ReadAll() ([]T, error) {
|
||||||
var collection = make([]T, 0)
|
var collection = make([]T, 0)
|
||||||
|
|
||||||
|
|
|
@ -243,8 +243,7 @@ func (bouncer *RequestBouncer) mwCheckPortainerAuthorizations(next http.Handler,
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = bouncer.dataStore.User().Read(tokenData.ID)
|
if ok, err := bouncer.dataStore.User().Exists(tokenData.ID); !ok {
|
||||||
if bouncer.dataStore.IsErrObjectNotFound(err) {
|
|
||||||
httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", httperrors.ErrUnauthorized)
|
httperror.WriteError(w, http.StatusUnauthorized, "Unauthorized", httperrors.ErrUnauthorized)
|
||||||
return
|
return
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
|
@ -322,9 +321,8 @@ func (bouncer *RequestBouncer) mwAuthenticateFirst(tokenLookups []tokenLookup, n
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, _ := bouncer.dataStore.User().Read(token.ID)
|
if ok, _ := bouncer.dataStore.User().Exists(token.ID); !ok {
|
||||||
if user == nil {
|
httperror.WriteError(w, http.StatusUnauthorized, "The authorization token is invalid", httperrors.ErrUnauthorized)
|
||||||
httperror.WriteError(w, http.StatusUnauthorized, "An authorization token is invalid", httperrors.ErrUnauthorized)
|
|
||||||
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
|
@ -151,6 +151,7 @@ func (s *stubUserService) UsersByRole(role portainer.UserRole) ([]portainer.User
|
||||||
func (s *stubUserService) Create(user *portainer.User) error { return nil }
|
func (s *stubUserService) Create(user *portainer.User) error { return nil }
|
||||||
func (s *stubUserService) Update(ID portainer.UserID, user *portainer.User) error { return nil }
|
func (s *stubUserService) Update(ID portainer.UserID, user *portainer.User) error { return nil }
|
||||||
func (s *stubUserService) Delete(ID portainer.UserID) error { return nil }
|
func (s *stubUserService) Delete(ID portainer.UserID) error { return nil }
|
||||||
|
func (s *stubUserService) Exists(ID portainer.UserID) (bool, error) { return false, nil }
|
||||||
|
|
||||||
// WithUsers testDatastore option that will instruct testDatastore to return provided users
|
// WithUsers testDatastore option that will instruct testDatastore to return provided users
|
||||||
func WithUsers(us []portainer.User) datastoreOption {
|
func WithUsers(us []portainer.User) datastoreOption {
|
||||||
|
@ -186,6 +187,9 @@ func (s *stubEdgeJobService) UpdateEdgeJobFunc(ID portainer.EdgeJobID, updateFun
|
||||||
}
|
}
|
||||||
func (s *stubEdgeJobService) Delete(ID portainer.EdgeJobID) error { return nil }
|
func (s *stubEdgeJobService) Delete(ID portainer.EdgeJobID) error { return nil }
|
||||||
func (s *stubEdgeJobService) GetNextIdentifier() int { return 0 }
|
func (s *stubEdgeJobService) GetNextIdentifier() int { return 0 }
|
||||||
|
func (s *stubEdgeJobService) Exists(ID portainer.EdgeJobID) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// WithEdgeJobs option will instruct testDatastore to return provided jobs
|
// WithEdgeJobs option will instruct testDatastore to return provided jobs
|
||||||
func WithEdgeJobs(js []portainer.EdgeJob) datastoreOption {
|
func WithEdgeJobs(js []portainer.EdgeJob) datastoreOption {
|
||||||
|
@ -426,6 +430,10 @@ func (s *stubStacksService) GetNextIdentifier() int {
|
||||||
return len(s.stacks)
|
return len(s.stacks)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *stubStacksService) Exists(ID portainer.StackID) (bool, error) {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
// WithStacks option will instruct testDatastore to return provided stacks
|
// WithStacks option will instruct testDatastore to return provided stacks
|
||||||
func WithStacks(stacks []portainer.Stack) datastoreOption {
|
func WithStacks(stacks []portainer.Stack) datastoreOption {
|
||||||
return func(d *testDatastore) {
|
return func(d *testDatastore) {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue