Initial QSfera import
This commit is contained in:
@@ -0,0 +1,233 @@
|
||||
// Package store implements the go-micro store interface
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"log"
|
||||
"sync"
|
||||
|
||||
"github.com/google/uuid"
|
||||
olog "github.com/qsfera/server/pkg/log"
|
||||
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
|
||||
"github.com/qsfera/server/services/settings/pkg/config"
|
||||
"github.com/qsfera/server/services/settings/pkg/settings"
|
||||
"github.com/qsfera/server/services/settings/pkg/store/defaults"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
|
||||
"github.com/opencloud-eu/reva/v2/pkg/storage/utils/metadata"
|
||||
)
|
||||
|
||||
var (
|
||||
// Name is the default name for the settings store
|
||||
Name = "qsfera-settings"
|
||||
managerName = "metadata"
|
||||
settingsSpaceID = "f1bdd61a-da7c-49fc-8203-0558109d1b4f" // uuid.NewString()
|
||||
rootFolderLocation = "settings"
|
||||
bundleFolderLocation = "settings/bundles"
|
||||
accountsFolderLocation = "settings/accounts"
|
||||
valuesFolderLocation = "settings/values"
|
||||
)
|
||||
|
||||
// MetadataClient is the interface to talk to metadata service
|
||||
type MetadataClient interface {
|
||||
SimpleDownload(ctx context.Context, id string) ([]byte, error)
|
||||
SimpleUpload(ctx context.Context, id string, content []byte) error
|
||||
Delete(ctx context.Context, id string) error
|
||||
ReadDir(ctx context.Context, id string) ([]string, error)
|
||||
MakeDirIfNotExist(ctx context.Context, id string) error
|
||||
Init(ctx context.Context, id string) error
|
||||
}
|
||||
|
||||
// Store interacts with the filesystem to manage settings information
|
||||
type Store struct {
|
||||
Logger olog.Logger
|
||||
|
||||
mdc MetadataClient
|
||||
cfg *config.Config
|
||||
|
||||
l *sync.Mutex
|
||||
}
|
||||
|
||||
// Init initialize the store once, later calls are noops
|
||||
func (s *Store) Init() {
|
||||
if s.mdc != nil {
|
||||
return
|
||||
}
|
||||
|
||||
s.l.Lock()
|
||||
defer s.l.Unlock()
|
||||
|
||||
if s.mdc != nil {
|
||||
return
|
||||
}
|
||||
|
||||
mdc := &CachedMDC{
|
||||
next: NewMetadataClient(s.cfg.Metadata),
|
||||
cfg: s.cfg,
|
||||
logger: s.Logger,
|
||||
}
|
||||
if err := s.initMetadataClient(mdc); err != nil {
|
||||
s.Logger.Error().Err(err).Msg("error initializing metadata client")
|
||||
}
|
||||
}
|
||||
|
||||
// New creates a new store
|
||||
func New(cfg *config.Config) settings.Manager {
|
||||
s := Store{
|
||||
Logger: olog.Configure(cfg.Service.Name, cfg.Commons, cfg.LogLevel),
|
||||
cfg: cfg,
|
||||
l: &sync.Mutex{},
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
// NewMetadataClient returns the MetadataClient
|
||||
func NewMetadataClient(cfg config.Metadata) MetadataClient {
|
||||
mdc, err := metadata.NewCS3Storage(cfg.GatewayAddress, cfg.StorageAddress, cfg.SystemUserID, cfg.SystemUserIDP, cfg.SystemUserAPIKey)
|
||||
if err != nil {
|
||||
log.Fatal("error connecting to mdc:", err)
|
||||
}
|
||||
return mdc
|
||||
|
||||
}
|
||||
|
||||
// we need to lazy initialize the MetadataClient because metadata service might not be ready
|
||||
func (s *Store) initMetadataClient(mdc MetadataClient) error {
|
||||
ctx := context.TODO()
|
||||
err := mdc.Init(ctx, settingsSpaceID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, p := range []string{
|
||||
rootFolderLocation,
|
||||
accountsFolderLocation,
|
||||
bundleFolderLocation,
|
||||
valuesFolderLocation,
|
||||
} {
|
||||
err = mdc.MakeDirIfNotExist(ctx, p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range s.cfg.Bundles {
|
||||
b, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mdc.SimpleUpload(ctx, bundlePath(p.Id), b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for _, p := range defaults.DefaultRoleAssignments(s.cfg) {
|
||||
accountUUID := p.AccountUuid
|
||||
roleID := p.RoleId
|
||||
err = mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
assIDs, err := mdc.ReadDir(ctx, accountPath(accountUUID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
adminUserID := accountUUID == s.cfg.AdminUserID
|
||||
if len(assIDs) > 0 && !adminUserID {
|
||||
// There is already a role assignment for this ID, skip to the next
|
||||
continue
|
||||
}
|
||||
// for the adminUserID we need to check if the user has the admin role every time
|
||||
if adminUserID {
|
||||
err = s.userMustHaveAdminRole(accountUUID, assIDs, mdc)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
ass := &settingsmsg.UserRoleAssignment{
|
||||
Id: uuid.NewString(),
|
||||
AccountUuid: accountUUID,
|
||||
RoleId: roleID,
|
||||
}
|
||||
|
||||
b, err := json.Marshal(ass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = mdc.SimpleUpload(ctx, assignmentPath(accountUUID, ass.Id), b)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s.mdc = mdc
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Store) userMustHaveAdminRole(accountUUID string, assIDs []string, mdc MetadataClient) error {
|
||||
ctx := context.TODO()
|
||||
var hasAdminRole bool
|
||||
|
||||
// load the assignments from the store and check if the admin role is already assigned
|
||||
for _, assID := range assIDs {
|
||||
b, err := mdc.SimpleDownload(ctx, assignmentPath(accountUUID, assID))
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
// continue
|
||||
case errtypes.NotFound:
|
||||
continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
a := &settingsmsg.UserRoleAssignment{}
|
||||
err = json.Unmarshal(b, a)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if a.RoleId == defaults.BundleUUIDRoleAdmin {
|
||||
hasAdminRole = true
|
||||
}
|
||||
}
|
||||
|
||||
// delete old role assignment and set admin role
|
||||
if !hasAdminRole {
|
||||
err := mdc.Delete(ctx, accountPath(accountUUID))
|
||||
switch err.(type) {
|
||||
case nil:
|
||||
// continue
|
||||
case errtypes.NotFound:
|
||||
// already gone, continue
|
||||
default:
|
||||
return err
|
||||
}
|
||||
|
||||
err = mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ass := &settingsmsg.UserRoleAssignment{
|
||||
Id: uuid.NewString(),
|
||||
AccountUuid: accountUUID,
|
||||
RoleId: defaults.BundleUUIDRoleAdmin,
|
||||
}
|
||||
b, err := json.Marshal(ass)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return mdc.SimpleUpload(ctx, assignmentPath(accountUUID, ass.Id), b)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
settings.Registry[managerName] = New
|
||||
}
|
||||
Reference in New Issue
Block a user