2020-03-11 18:31:33 +00:00
|
|
|
package tenant
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"encoding/json"
|
|
|
|
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2"
|
2021-09-13 19:12:35 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kit/platform"
|
2020-04-03 17:39:20 +00:00
|
|
|
"github.com/influxdata/influxdb/v2/kv"
|
2020-03-11 18:31:33 +00:00
|
|
|
)
|
|
|
|
|
2020-07-01 11:08:20 +00:00
|
|
|
var urmBucket = []byte("userresourcemappingsv1")
|
2020-03-11 18:31:33 +00:00
|
|
|
|
2020-04-02 09:01:44 +00:00
|
|
|
// NOTE(affo): On URM creation, we check that the user exists.
|
|
|
|
// We do not check that the resource it is pointing to exists.
|
|
|
|
// This decision takes into account that different resources could not be in the same store.
|
|
|
|
// To perform that kind of check, we must rely on the service layer.
|
|
|
|
// However, we do not want having the storage layer depend on the service layer above.
|
2020-03-11 18:31:33 +00:00
|
|
|
func (s *Store) CreateURM(ctx context.Context, tx kv.Tx, urm *influxdb.UserResourceMapping) error {
|
2020-04-02 09:01:44 +00:00
|
|
|
if _, err := s.GetUser(ctx, tx, urm.UserID); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2020-03-11 18:31:33 +00:00
|
|
|
if err := s.uniqueUserResourceMapping(ctx, tx, urm); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
v, err := json.Marshal(urm)
|
|
|
|
if err != nil {
|
|
|
|
return ErrUnprocessableMapping(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
key, err := userResourceKey(urm.ResourceID, urm.UserID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := tx.Bucket(urmBucket)
|
|
|
|
if err != nil {
|
|
|
|
return UnavailableURMServiceError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := b.Put(key, v); err != nil {
|
|
|
|
return UnavailableURMServiceError(err)
|
|
|
|
}
|
|
|
|
|
2020-03-27 18:36:25 +00:00
|
|
|
// insert urm into by user index
|
|
|
|
userID, err := urm.UserID.Encode()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
if err := s.urmByUserIndex.Insert(tx, userID, key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-03-11 18:31:33 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Store) ListURMs(ctx context.Context, tx kv.Tx, filter influxdb.UserResourceMappingFilter, opt ...influxdb.FindOptions) ([]*influxdb.UserResourceMapping, error) {
|
|
|
|
ms := []*influxdb.UserResourceMapping{}
|
|
|
|
|
|
|
|
b, err := tx.Bucket(urmBucket)
|
|
|
|
if err != nil {
|
|
|
|
return nil, UnavailableURMServiceError(err)
|
|
|
|
}
|
|
|
|
|
2020-03-27 18:36:25 +00:00
|
|
|
filterFn := func(m *influxdb.UserResourceMapping) bool {
|
|
|
|
return (!filter.UserID.Valid() || (filter.UserID == m.UserID)) &&
|
|
|
|
(!filter.ResourceID.Valid() || (filter.ResourceID == m.ResourceID)) &&
|
|
|
|
(filter.UserType == "" || (filter.UserType == m.UserType)) &&
|
|
|
|
(filter.ResourceType == "" || (filter.ResourceType == m.ResourceType))
|
|
|
|
}
|
|
|
|
|
|
|
|
if filter.UserID.Valid() {
|
2020-07-14 18:40:04 +00:00
|
|
|
var (
|
|
|
|
// urm by user index lookup
|
|
|
|
userID, _ = filter.UserID.Encode()
|
|
|
|
seen int
|
|
|
|
)
|
2020-07-10 15:48:11 +00:00
|
|
|
|
2020-10-30 12:06:24 +00:00
|
|
|
err := s.urmByUserIndex.Walk(ctx, tx, userID, func(k, v []byte) (bool, error) {
|
2020-03-27 18:36:25 +00:00
|
|
|
m := &influxdb.UserResourceMapping{}
|
|
|
|
if err := json.Unmarshal(v, m); err != nil {
|
2020-10-30 12:06:24 +00:00
|
|
|
return false, CorruptURMError(err)
|
2020-03-27 18:36:25 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 18:40:04 +00:00
|
|
|
// respect offset parameter
|
|
|
|
reachedOffset := (len(opt) == 0 || seen >= opt[0].Offset)
|
2020-10-30 12:06:24 +00:00
|
|
|
if reachedOffset && filterFn(m) {
|
2020-03-27 18:36:25 +00:00
|
|
|
ms = append(ms, m)
|
|
|
|
}
|
|
|
|
|
2020-07-14 18:40:04 +00:00
|
|
|
seen++
|
|
|
|
|
2020-10-30 12:06:24 +00:00
|
|
|
return (len(opt) == 0 || opt[0].Limit <= 0 || len(ms) < opt[0].Limit), nil
|
|
|
|
})
|
2020-03-27 18:36:25 +00:00
|
|
|
|
2020-10-30 12:06:24 +00:00
|
|
|
return ms, err
|
2020-03-27 18:36:25 +00:00
|
|
|
}
|
|
|
|
|
2020-03-11 18:31:33 +00:00
|
|
|
// for now the best we can do is use the resourceID if we have that as a forward cursor option
|
|
|
|
var prefix []byte
|
|
|
|
var cursorOptions []kv.CursorOption
|
|
|
|
|
|
|
|
if filter.ResourceID.Valid() {
|
|
|
|
p, err := userResourcePrefixKey(filter.ResourceID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
prefix = p
|
|
|
|
cursorOptions = append(cursorOptions, kv.WithCursorPrefix(p))
|
|
|
|
}
|
|
|
|
cur, err := b.ForwardCursor(prefix, cursorOptions...)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
defer cur.Close()
|
|
|
|
|
|
|
|
for k, v := cur.Next(); k != nil; k, v = cur.Next() {
|
|
|
|
m := &influxdb.UserResourceMapping{}
|
|
|
|
if err := json.Unmarshal(v, m); err != nil {
|
|
|
|
return nil, CorruptURMError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
// check to see if it matches the filter
|
2020-03-27 18:36:25 +00:00
|
|
|
if filterFn(m) {
|
2020-03-11 18:31:33 +00:00
|
|
|
ms = append(ms, m)
|
|
|
|
}
|
|
|
|
|
2020-07-14 18:40:04 +00:00
|
|
|
if len(opt) > 0 && opt[0].Limit > 0 && len(ms) >= opt[0].Limit {
|
2020-03-11 18:31:33 +00:00
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ms, cur.Err()
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func (s *Store) GetURM(ctx context.Context, tx kv.Tx, resourceID, userID platform.ID) (*influxdb.UserResourceMapping, error) {
|
2020-03-11 18:31:33 +00:00
|
|
|
key, err := userResourceKey(resourceID, userID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := tx.Bucket(urmBucket)
|
|
|
|
if err != nil {
|
|
|
|
return nil, UnavailableURMServiceError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
val, err := b.Get(key)
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
m := &influxdb.UserResourceMapping{}
|
|
|
|
if err := json.Unmarshal(val, m); err != nil {
|
|
|
|
return nil, CorruptURMError(err)
|
|
|
|
}
|
|
|
|
return m, nil
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func (s *Store) DeleteURM(ctx context.Context, tx kv.Tx, resourceID, userID platform.ID) error {
|
2020-03-11 18:31:33 +00:00
|
|
|
key, err := userResourceKey(resourceID, userID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := tx.Bucket(urmBucket)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-03-27 18:36:25 +00:00
|
|
|
// remove user resource mapping from by user index
|
|
|
|
uid, err := userID.Encode()
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
if err := s.urmByUserIndex.Delete(tx, uid, key); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2020-03-11 18:31:33 +00:00
|
|
|
return b.Delete(key)
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func userResourcePrefixKey(resourceID platform.ID) ([]byte, error) {
|
2020-03-11 18:31:33 +00:00
|
|
|
encodedResourceID, err := resourceID.Encode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrInvalidURMID
|
|
|
|
}
|
|
|
|
return encodedResourceID, nil
|
|
|
|
}
|
|
|
|
|
2021-03-30 18:10:02 +00:00
|
|
|
func userResourceKey(resourceID, userID platform.ID) ([]byte, error) {
|
2020-03-11 18:31:33 +00:00
|
|
|
encodedResourceID, err := resourceID.Encode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrInvalidURMID
|
|
|
|
}
|
|
|
|
|
|
|
|
encodedUserID, err := userID.Encode()
|
|
|
|
if err != nil {
|
|
|
|
return nil, ErrInvalidURMID
|
|
|
|
}
|
|
|
|
|
|
|
|
key := make([]byte, len(encodedResourceID)+len(encodedUserID))
|
|
|
|
copy(key, encodedResourceID)
|
|
|
|
copy(key[len(encodedResourceID):], encodedUserID)
|
|
|
|
|
|
|
|
return key, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func (s *Store) uniqueUserResourceMapping(ctx context.Context, tx kv.Tx, m *influxdb.UserResourceMapping) error {
|
|
|
|
key, err := userResourceKey(m.ResourceID, m.UserID)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := tx.Bucket(urmBucket)
|
|
|
|
if err != nil {
|
|
|
|
return UnavailableURMServiceError(err)
|
|
|
|
}
|
|
|
|
|
|
|
|
_, err = b.Get(key)
|
|
|
|
if !kv.IsNotFound(err) {
|
|
|
|
return NonUniqueMappingError(m.UserID)
|
|
|
|
}
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|