settings: Remove deprecated filesystem backend

We changed the default to `metadata` before the 2.0 release. This PR
alos removes the associated `SETTINGS_STORE_TYPE` and
`SETTINGS_DATA_PATH` settings as they are no longer needed with
`metadata` as the only available backend.
This commit is contained in:
Ralf Haferkamp
2024-05-13 11:54:37 +02:00
committed by Ralf Haferkamp
parent 4177f43cdb
commit 5b38fa149d
20 changed files with 9 additions and 1681 deletions

View File

@@ -0,0 +1,6 @@
Change: The `filesystem` backend for the settings service has been removed
The only remaining backend for the settings service is `metadata`, which has been the
default backend since ocis 2.0
https://github.com/owncloud/ocis/pull/9138

View File

@@ -12,12 +12,7 @@ The settings service is currently used for managing the:
As an example, user profile settings that can be changed in the Web UI must be persistent.
The settings service supports two different backends for persisting the data. The backend can be set via the `SETTINGS_STORE_TYPE` environment variable. Supported values are:
* `metadata`: The default. This backend persists the settings data via the `storage-system` service.
* `filesystem`: (deprecated) This backend persists the settings data in a directory on the local filesystem.
The directory can be configured with `SETTINGS_DATA_PATH`. This backend is **not** suitable for running
multiple intances of the `settings` service in a scale-out deployment and was therefore deprecated.
The settings service persists the settings data via the `storage-system` service.
<!--- Note: The diagramm is outdate, leaving it here for a future rework
The diagram shows how the settings service integrates into oCIS:
@@ -37,7 +32,7 @@ graph TD
## Caching
When using `SETTINGS_STORE_TYPE=metadata`, the `settings` service caches the results of queries against the storage backend to provide faster responses. The content of this cache is independent of the cache used in the `storage-system` service as it caches directory listing and settings content stored in files.
The `settings` service caches the results of queries against the storage backend to provide faster responses. The content of this cache is independent of the cache used in the `storage-system` service as it caches directory listing and settings content stored in files.
The store used for the cache can be configured using the `SETTINGS_CACHE_STORE` environment variable. Possible stores are:
- `memory`: Basic in-memory store and the default.

View File

@@ -25,8 +25,6 @@ type Config struct {
GRPCClientTLS *shared.GRPCClientTLS `yaml:"grpc_client_tls"`
GrpcClient client.Client `yaml:"-"`
StoreType string `yaml:"store_type" env:"SETTINGS_STORE_TYPE" desc:"Store type configures the persistency driver. Supported values are 'metadata' and 'filesystem'. Note that the value 'filesystem' is considered deprecated." introductionVersion:"pre5.0"`
DataPath string `yaml:"data_path" env:"SETTINGS_DATA_PATH" desc:"The directory where the filesystem storage will store ocis settings. If not defined, the root directory derives from $OCIS_BASE_DATA_PATH:/settings." introductionVersion:"pre5.0"`
Metadata Metadata `yaml:"metadata_config"`
BundlesPath string `yaml:"bundles_path" env:"SETTINGS_BUNDLES_PATH" desc:"The path to a JSON file with a list of bundles. If not defined, the default bundles will be loaded." introductionVersion:"pre5.0"`
Bundles []*settingsmsg.Bundle `yaml:"-"`

View File

@@ -3,11 +3,9 @@ package defaults
import (
"encoding/json"
"os"
"path"
"strings"
"time"
"github.com/owncloud/ocis/v2/ocis-pkg/config/defaults"
"github.com/owncloud/ocis/v2/ocis-pkg/structs"
"github.com/owncloud/ocis/v2/services/settings/pkg/config"
rdefaults "github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
@@ -49,8 +47,6 @@ func DefaultConfig() *config.Config {
Addr: "127.0.0.1:9191",
Namespace: "com.owncloud.api",
},
StoreType: "metadata", // use metadata or filesystem
DataPath: path.Join(defaults.BaseDataPath(), "settings"),
SetupDefaultAssignments: false,
Metadata: config.Metadata{
GatewayAddress: "com.owncloud.api.storage-system",

View File

@@ -17,7 +17,6 @@ import (
"github.com/owncloud/ocis/v2/services/settings/pkg/config"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
"github.com/owncloud/ocis/v2/services/settings/pkg/store/defaults"
filestore "github.com/owncloud/ocis/v2/services/settings/pkg/store/filesystem"
metastore "github.com/owncloud/ocis/v2/services/settings/pkg/store/metadata"
merrors "go-micro.dev/v4/errors"
"go-micro.dev/v4/metadata"
@@ -40,17 +39,7 @@ func NewService(cfg *config.Config, logger log.Logger) settings.ServiceHandler {
logger: logger,
}
switch cfg.StoreType {
default:
fallthrough
case "metadata":
service.manager = metastore.New(cfg)
case "filesystem":
fmt.Println("WARNING: filesystem store is deprecated and will be removed in the future. Please use metadata store instead.")
service.manager = filestore.New(cfg)
// TODO: if we want to further support filesystem store it should use default permissions from store/defaults/defaults.go instead using this duplicate
service.RegisterDefaultRoles()
}
service.manager = metastore.New(cfg)
return service
}
@@ -101,45 +90,6 @@ func (g Service) CheckPermission(ctx context.Context, req *cs3permissions.CheckP
}, nil
}
// RegisterDefaultRoles composes default roles and saves them. Skipped if the roles already exist.
func (g Service) RegisterDefaultRoles() {
// FIXME: we're writing default roles per service start (i.e. twice at the moment, for http and grpc server). has to happen only once.
for _, role := range generateBundlesDefaultRoles() {
bundleID := role.Extension + "." + role.Id
// check if the role already exists
bundle, _ := g.manager.ReadBundle(role.Id)
if bundle != nil {
g.logger.Debug().Str("bundleID", bundleID).Msg("bundle already exists. skipping.")
continue
}
// create the role
_, err := g.manager.WriteBundle(role)
if err != nil {
g.logger.Error().Err(err).Str("bundleID", bundleID).Msg("failed to register bundle")
}
g.logger.Debug().Str("bundleID", bundleID).Msg("successfully registered bundle")
}
for _, req := range generatePermissionRequests() {
_, err := g.manager.AddSettingToBundle(req.GetBundleId(), req.GetSetting())
if err != nil {
g.logger.Error().
Err(err).
Str("bundleID", req.GetBundleId()).
Interface("setting", req.GetSetting()).
Msg("failed to register permission")
}
}
if g.config.SetupDefaultAssignments {
for _, req := range g.defaultRoleAssignments() {
if _, err := g.manager.WriteRoleAssignment(req.AccountUuid, req.RoleId); err != nil {
g.logger.Error().Err(err).Msg("failed to register role assignment")
}
}
}
}
// TODO: check permissions on every request
// SaveBundle implements the BundleServiceHandler interface

View File

@@ -1,10 +1,5 @@
package svc
import (
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
settingssvc "github.com/owncloud/ocis/v2/protogen/gen/ocis/services/settings/v0"
)
const (
// BundleUUIDRoleAdmin represents the admin role
BundleUUIDRoleAdmin = "71881883-1768-46bd-a24d-a356a2afdf7f"
@@ -64,567 +59,3 @@ const (
// ChangeLogoPermissionName is the hardcoded setting name for the change-logo permission
ChangeLogoPermissionName string = "change-logo"
)
// generateBundlesDefaultRoles bootstraps the default roles.
func generateBundlesDefaultRoles() []*settingsmsg.Bundle {
return []*settingsmsg.Bundle{
generateBundleAdminRole(),
generateBundleSpaceAdminRole(),
generateBundleUserRole(),
generateBundleGuestRole(),
generateBundleProfileRequest(),
}
}
func generateBundleAdminRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleAdmin,
Name: "admin",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "ocis-roles",
DisplayName: "Admin",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{},
}
}
func generateBundleSpaceAdminRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleSpaceAdmin,
Name: "spaceadmin",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "ocis-roles",
DisplayName: "Space Admin",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{},
}
}
func generateBundleUserRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleUser,
Name: "user",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "ocis-roles",
DisplayName: "User",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{},
}
}
func generateBundleGuestRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleGuest,
Name: "guest",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "ocis-roles",
DisplayName: "Guest",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{},
}
}
var languageSetting = settingsmsg.Setting_SingleChoiceValue{
SingleChoiceValue: &settingsmsg.SingleChoiceList{
Options: []*settingsmsg.ListOption{
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "bg",
},
},
DisplayValue: "български",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "cs",
},
},
DisplayValue: "Czech",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "de",
},
},
DisplayValue: "Deutsch",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "en",
},
},
DisplayValue: "English",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "es",
},
},
DisplayValue: "Español",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "fr",
},
},
DisplayValue: "Français",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "gl",
},
},
DisplayValue: "Galego",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "it",
},
},
DisplayValue: "Italiano",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "nl",
},
},
DisplayValue: "Nederlands",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "ko",
},
},
DisplayValue: "한국어",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "sq",
},
},
DisplayValue: "Shqipja",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "sv",
},
},
DisplayValue: "Svenska",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "tr",
},
},
DisplayValue: "Türkçe",
},
},
},
}
func generateBundleProfileRequest() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: "2a506de7-99bd-4f0d-994e-c38e72c28fd9",
Name: "profile",
Extension: "ocis-accounts",
Type: settingsmsg.Bundle_TYPE_DEFAULT,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
DisplayName: "Profile",
Settings: []*settingsmsg.Setting{
{
Id: SettingUUIDProfileLanguage,
Name: "language",
DisplayName: "Language",
Description: "User language",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &languageSetting,
},
},
}
}
func generatePermissionRequests() []*settingssvc.AddSettingToBundleRequest {
return []*settingssvc.AddSettingToBundleRequest{
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: RoleManagementPermissionID,
Name: RoleManagementPermissionName,
DisplayName: "Role Management",
Description: "This permission gives full access to everything that is related to role management.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
Id: "all",
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: SettingsManagementPermissionID,
Name: SettingsManagementPermissionName,
DisplayName: "Settings Management",
Description: "This permission gives full access to everything that is related to settings management.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
Id: "all",
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: "7d81f103-0488-4853-bce5-98dcce36d649",
Name: "language-readwrite",
DisplayName: "Permission to read and set the language (anyone)",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileLanguage,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleUser,
Setting: &settingsmsg.Setting{
Id: "640e00d2-4df8-41bd-b1c2-9f30a01e0e99",
Name: "language-readwrite",
DisplayName: "Permission to read and set the language (self)",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileLanguage,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
{
BundleId: BundleUUIDRoleGuest,
Setting: &settingsmsg.Setting{
Id: "ca878636-8b1a-4fae-8282-8617a4c13597",
Name: "language-readwrite",
DisplayName: "Permission to read and set the language (self)",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileLanguage,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: AccountManagementPermissionID,
Name: AccountManagementPermissionName,
DisplayName: "Account Management",
Description: "This permission gives full access to everything that is related to account management.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
Id: "all",
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: GroupManagementPermissionID,
Name: GroupManagementPermissionName,
DisplayName: "Group Management",
Description: "This permission gives full access to everything that is related to group management.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_GROUP,
Id: "all",
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleUser,
Setting: &settingsmsg.Setting{
Id: SelfManagementPermissionID,
Name: SelfManagementPermissionName,
DisplayName: "Self Management",
Description: "This permission gives access to self management.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
Id: "me",
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: SetSpaceQuotaPermissionID,
Name: SetSpaceQuotaPermissionName,
DisplayName: "Set Space Quota",
Description: "This permission allows managing space quotas.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleUser,
Setting: &settingsmsg.Setting{
Id: CreateSpacePermissionID,
Name: CreateSpacePermissionName,
DisplayName: "Create own Space",
Description: "This permission allows creating a space owned by the current user.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM, // TODO resource type space? self? me? own?
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_CREATE,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: CreateSpacePermissionID,
Name: CreateSpacePermissionName,
DisplayName: "Create Space",
Description: "This permission allows creating new spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: ListAllSpacesPermissionID,
Name: ListAllSpacesPermissionName,
DisplayName: "List All Spaces",
Description: "This permission allows list all spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleAdmin,
Setting: &settingsmsg.Setting{
Id: ChangeLogoPermissionID,
Name: ChangeLogoPermissionName,
DisplayName: "Change logo",
Description: "This permission permits to change the system logo.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleSpaceAdmin,
Setting: &settingsmsg.Setting{
Id: CreateSpacePermissionID,
Name: CreateSpacePermissionName,
DisplayName: "Create Space",
Description: "This permission allows creating new spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleSpaceAdmin,
Setting: &settingsmsg.Setting{
Id: SetSpaceQuotaPermissionID,
Name: SetSpaceQuotaPermissionName,
DisplayName: "Set Space Quota",
Description: "This permission allows managing space quotas.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleSpaceAdmin,
Setting: &settingsmsg.Setting{
Id: ListAllSpacesPermissionID,
Name: ListAllSpacesPermissionName,
DisplayName: "List All Spaces",
Description: "This permission allows list all spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
Constraint: settingsmsg.Permission_CONSTRAINT_ALL,
},
},
},
},
{
BundleId: BundleUUIDRoleSpaceAdmin,
Setting: &settingsmsg.Setting{
Id: "640e00d2-4df8-41bd-b1c2-9f30a01e0e99",
Name: "language-readwrite",
DisplayName: "Permission to read and set the language (self)",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileLanguage,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
{
BundleId: BundleUUIDRoleSpaceAdmin,
Setting: &settingsmsg.Setting{
Id: SelfManagementPermissionID,
Name: SelfManagementPermissionName,
DisplayName: "Self Management",
Description: "This permission gives access to self management.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
Id: "me",
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
}
}
func (g Service) defaultRoleAssignments() []*settingsmsg.UserRoleAssignment {
return []*settingsmsg.UserRoleAssignment{
// default admin users
{
AccountUuid: g.config.AdminUserID,
RoleId: BundleUUIDRoleAdmin,
},
// default users with role "user"
{
AccountUuid: "4c510ada-c86b-4815-8820-42cdf82c3d51", // demo user "einstein"
RoleId: BundleUUIDRoleUser,
}, {
AccountUuid: "f7fbf8c8-139b-4376-b307-cf0a8c2d0d9c", // demo user "marie"
RoleId: BundleUUIDRoleUser,
},
// default users with role "spaceadmin"
{
AccountUuid: "058bff95-6708-4fe5-91e4-9ea3d377588b", // demo user "moss"
RoleId: BundleUUIDRoleSpaceAdmin,
}, {
AccountUuid: "534bb038-6f9d-4093-946f-133be61fa4e7", // demo user "katherine"
RoleId: BundleUUIDRoleSpaceAdmin,
},
}
}

View File

@@ -1,66 +0,0 @@
// Package store implements the go-micro store interface
package store
import (
"os"
"path/filepath"
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
)
// ListRoleAssignments loads and returns all role assignments matching the given assignment identifier.
func (s Store) ListRoleAssignments(accountUUID string) ([]*settingsmsg.UserRoleAssignment, error) {
var records []*settingsmsg.UserRoleAssignment
assignmentsFolder := s.buildFolderPathForRoleAssignments(false)
assignmentFiles, err := os.ReadDir(assignmentsFolder)
if err != nil {
return nil, nil
}
for _, assignmentFile := range assignmentFiles {
record := settingsmsg.UserRoleAssignment{}
err = s.parseRecordFromFile(&record, filepath.Join(assignmentsFolder, assignmentFile.Name()))
if err == nil {
if record.AccountUuid == accountUUID {
records = append(records, &record)
}
}
}
return records, nil
}
// WriteRoleAssignment appends the given role assignment to the existing assignments of the respective account.
func (s Store) WriteRoleAssignment(accountUUID, roleID string) (*settingsmsg.UserRoleAssignment, error) {
// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
list, err := s.ListRoleAssignments(accountUUID)
if err != nil {
return nil, err
}
if len(list) > 0 {
filePath := s.buildFilePathForRoleAssignment(list[0].Id, true)
if err := os.Remove(filePath); err != nil {
return nil, err
}
}
assignment := &settingsmsg.UserRoleAssignment{
Id: uuid.Must(uuid.NewV4()).String(),
AccountUuid: accountUUID,
RoleId: roleID,
}
filePath := s.buildFilePathForRoleAssignment(assignment.Id, true)
if err := s.writeRecordToFile(assignment, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("request contents written to file: %v", filePath)
return assignment, nil
}
// RemoveRoleAssignment deletes the given role assignment from the existing assignments of the respective account.
func (s Store) RemoveRoleAssignment(assignmentID string) error {
filePath := s.buildFilePathForRoleAssignment(assignmentID, false)
return os.Remove(filePath)
}

View File

@@ -1,178 +0,0 @@
package store
import (
"errors"
"log"
"os"
"path/filepath"
"testing"
olog "github.com/owncloud/ocis/v2/ocis-pkg/log"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/stretchr/testify/assert"
)
var (
einstein = "a4d07560-a670-4be9-8d60-9b547751a208"
//marie = "3c054db3-eec1-4ca4-b985-bc56dcf560cb"
s = Store{
dataPath: dataRoot,
Logger: logger,
}
logger = olog.NewLogger(
olog.Color(true),
olog.Pretty(true),
olog.Level("info"),
)
bundles = []*settingsmsg.Bundle{
{
Id: "f36db5e6-a03c-40df-8413-711c67e40b47",
Type: settingsmsg.Bundle_TYPE_ROLE,
DisplayName: "test role - reads | update",
Name: "TEST_ROLE",
Extension: "ocis-settings",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_BUNDLE,
},
Settings: []*settingsmsg.Setting{
{
Name: "update",
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_UPDATE,
},
},
},
{
Name: "read",
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
},
},
},
},
},
{
Id: "44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
Type: settingsmsg.Bundle_TYPE_ROLE,
DisplayName: "another",
Name: "ANOTHER_TEST_ROLE",
Extension: "ocis-settings",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_BUNDLE,
},
Settings: []*settingsmsg.Setting{
{
Name: "read",
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
},
},
},
},
},
}
)
func init() {
setupRoles()
}
func setupRoles() {
for i := range bundles {
if _, err := s.WriteBundle(bundles[i]); err != nil {
log.Fatal(err)
}
}
}
func TestAssignmentUniqueness(t *testing.T) {
var scenarios = []struct {
name string
userID string
firstRole string
secondRole string
}{
{
"roles assignments",
einstein,
"f36db5e6-a03c-40df-8413-711c67e40b47",
"44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
},
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.name, func(t *testing.T) {
firstAssignment, err := s.WriteRoleAssignment(scenario.userID, scenario.firstRole)
assert.NoError(t, err)
assert.Equal(t, firstAssignment.RoleId, scenario.firstRole)
assert.FileExists(t, filepath.Join(dataRoot, "assignments", firstAssignment.Id+".json"))
list, err := s.ListRoleAssignments(scenario.userID)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
// creating another assignment shouldn't add another entry, as we support max one role per user.
secondAssignment, err := s.WriteRoleAssignment(scenario.userID, scenario.secondRole)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
// assigning the second role should remove the old file and create a new one.
list, err = s.ListRoleAssignments(scenario.userID)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
assert.Equal(t, secondAssignment.RoleId, scenario.secondRole)
assert.NoFileExists(t, filepath.Join(dataRoot, "assignments", firstAssignment.Id+".json"))
assert.FileExists(t, filepath.Join(dataRoot, "assignments", secondAssignment.Id+".json"))
})
}
burnRoot()
}
func TestDeleteAssignment(t *testing.T) {
var scenarios = []struct {
name string
userID string
firstRole string
secondRole string
}{
{
"roles assignments",
einstein,
"f36db5e6-a03c-40df-8413-711c67e40b47",
"44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
},
}
for _, scenario := range scenarios {
scenario := scenario
t.Run(scenario.name, func(t *testing.T) {
assignment, err := s.WriteRoleAssignment(scenario.userID, scenario.firstRole)
assert.NoError(t, err)
assert.Equal(t, assignment.RoleId, scenario.firstRole)
assert.FileExists(t, filepath.Join(dataRoot, "assignments", assignment.Id+".json"))
list, err := s.ListRoleAssignments(scenario.userID)
assert.NoError(t, err)
assert.Equal(t, 1, len(list))
err = s.RemoveRoleAssignment(assignment.Id)
assert.NoError(t, err)
list, err = s.ListRoleAssignments(scenario.userID)
assert.NoError(t, err)
assert.Equal(t, 0, len(list))
err = s.RemoveRoleAssignment(assignment.Id)
merr := &os.PathError{}
assert.Equal(t, true, errors.As(err, &merr))
})
}
burnRoot()
}

View File

@@ -1,181 +0,0 @@
// Package store implements the go-micro store interface
package store
import (
"errors"
"fmt"
"os"
"path/filepath"
"sync"
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
)
var m = &sync.RWMutex{}
// ListBundles returns all bundles in the dataPath folder that match the given type.
func (s Store) ListBundles(bundleType settingsmsg.Bundle_Type, bundleIDs []string) ([]*settingsmsg.Bundle, error) {
// FIXME: list requests should be ran against a cache, not FS
m.RLock()
defer m.RUnlock()
bundlesFolder := s.buildFolderPathForBundles(false)
bundleFiles, err := os.ReadDir(bundlesFolder)
if err != nil {
return nil, err
}
records := make([]*settingsmsg.Bundle, 0, len(bundleFiles))
for _, bundleFile := range bundleFiles {
record := settingsmsg.Bundle{}
err = s.parseRecordFromFile(&record, filepath.Join(bundlesFolder, bundleFile.Name()))
if err != nil {
s.Logger.Warn().Msgf("error reading %v", bundleFile)
continue
}
if record.Type != bundleType {
continue
}
if len(bundleIDs) > 0 && !containsStr(record.Id, bundleIDs) {
continue
}
records = append(records, &record)
}
return records, nil
}
// containsStr checks if the strs slice contains str
func containsStr(str string, strs []string) bool {
for _, s := range strs {
if s == str {
return true
}
}
return false
}
// ReadBundle tries to find a bundle by the given id within the dataPath.
func (s Store) ReadBundle(bundleID string) (*settingsmsg.Bundle, error) {
m.RLock()
defer m.RUnlock()
filePath := s.buildFilePathForBundle(bundleID, false)
record := settingsmsg.Bundle{}
if err := s.parseRecordFromFile(&record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("read contents from file: %v", filePath)
return &record, nil
}
// ReadSetting tries to find a setting by the given id within the dataPath.
func (s Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
m.RLock()
defer m.RUnlock()
bundles, err := s.ListBundles(settingsmsg.Bundle_TYPE_DEFAULT, []string{})
if err != nil {
return nil, err
}
for _, bundle := range bundles {
for _, setting := range bundle.Settings {
if setting.Id == settingID {
return setting, nil
}
}
}
return nil, fmt.Errorf("could not read setting: %v", settingID)
}
// WriteBundle writes the given record into a file within the dataPath.
func (s Store) WriteBundle(record *settingsmsg.Bundle) (*settingsmsg.Bundle, error) {
// FIXME: locking should happen on the file here, not globally.
m.Lock()
defer m.Unlock()
if record.Id == "" {
record.Id = uuid.Must(uuid.NewV4()).String()
}
filePath := s.buildFilePathForBundle(record.Id, true)
if err := s.writeRecordToFile(record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("request contents written to file: %v", filePath)
return record, nil
}
// AddSettingToBundle adds the given setting to the bundle with the given bundleID.
func (s Store) AddSettingToBundle(bundleID string, setting *settingsmsg.Setting) (*settingsmsg.Setting, error) {
bundle, err := s.ReadBundle(bundleID)
if err != nil {
if !errors.Is(err, settings.ErrNotFound) {
return nil, err
}
bundle = new(settingsmsg.Bundle)
bundle.Id = bundleID
bundle.Type = settingsmsg.Bundle_TYPE_DEFAULT
}
if setting.Id == "" {
setting.Id = uuid.Must(uuid.NewV4()).String()
}
setSetting(bundle, setting)
_, err = s.WriteBundle(bundle)
if err != nil {
return nil, err
}
return setting, nil
}
// RemoveSettingFromBundle removes the setting from the bundle with the given ids.
func (s Store) RemoveSettingFromBundle(bundleID string, settingID string) error {
bundle, err := s.ReadBundle(bundleID)
if err != nil {
return err
}
if ok := removeSetting(bundle, settingID); ok {
if _, err := s.WriteBundle(bundle); err != nil {
return err
}
}
return nil
}
// indexOfSetting finds the index of the given setting within the given bundle.
// returns -1 if the setting was not found.
func indexOfSetting(bundle *settingsmsg.Bundle, settingID string) int {
for index := range bundle.Settings {
s := bundle.Settings[index]
if s.Id == settingID {
return index
}
}
return -1
}
// setSetting will append or overwrite the given setting within the given bundle
func setSetting(bundle *settingsmsg.Bundle, setting *settingsmsg.Setting) {
m.Lock()
defer m.Unlock()
index := indexOfSetting(bundle, setting.Id)
if index == -1 {
bundle.Settings = append(bundle.Settings, setting)
} else {
bundle.Settings[index] = setting
}
}
// removeSetting will remove the given setting from the given bundle
func removeSetting(bundle *settingsmsg.Bundle, settingID string) bool {
m.Lock()
defer m.Unlock()
index := indexOfSetting(bundle, settingID)
if index == -1 {
return false
}
bundle.Settings = append(bundle.Settings[:index], bundle.Settings[index+1:]...)
return true
}

View File

@@ -1,155 +0,0 @@
package store
import (
"testing"
olog "github.com/owncloud/ocis/v2/ocis-pkg/log"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/stretchr/testify/assert"
)
var bundleScenarios = []struct {
name string
bundle *settingsmsg.Bundle
}{
{
name: "generic-test-file-resource",
bundle: &settingsmsg.Bundle{
Id: bundle1,
Type: settingsmsg.Bundle_TYPE_DEFAULT,
Extension: extension1,
DisplayName: "test1",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_FILE,
Id: "beep",
},
Settings: []*settingsmsg.Setting{
{
Id: setting1,
Description: "test-desc-1",
DisplayName: "test-displayname-1",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_FILE,
Id: "bleep",
},
Value: &settingsmsg.Setting_IntValue{
IntValue: &settingsmsg.Int{
Min: 0,
Max: 42,
},
},
},
},
},
},
{
name: "generic-test-system-resource",
bundle: &settingsmsg.Bundle{
Id: bundle2,
Type: settingsmsg.Bundle_TYPE_DEFAULT,
Extension: extension2,
DisplayName: "test1",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
{
Id: setting2,
Description: "test-desc-2",
DisplayName: "test-displayname-2",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_IntValue{
IntValue: &settingsmsg.Int{
Min: 0,
Max: 42,
},
},
},
},
},
},
{
name: "generic-test-role-bundle",
bundle: &settingsmsg.Bundle{
Id: bundle3,
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: extension1,
DisplayName: "Role1",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
{
Id: setting3,
Description: "test-desc-3",
DisplayName: "test-displayname-3",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: setting1,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
Constraint: settingsmsg.Permission_CONSTRAINT_OWN,
},
},
},
},
},
},
}
func TestBundles(t *testing.T) {
s := Store{
dataPath: dataRoot,
Logger: olog.NewLogger(
olog.Color(true),
olog.Pretty(true),
olog.Level("info"),
),
}
// write bundles
for i := range bundleScenarios {
index := i
t.Run(bundleScenarios[index].name, func(t *testing.T) {
filePath := s.buildFilePathForBundle(bundleScenarios[index].bundle.Id, true)
if err := s.writeRecordToFile(bundleScenarios[index].bundle, filePath); err != nil {
t.Error(err)
}
assert.FileExists(t, filePath)
})
}
// check that ListBundles only returns bundles with type DEFAULT
bundles, err := s.ListBundles(settingsmsg.Bundle_TYPE_DEFAULT, []string{})
if err != nil {
t.Error(err)
}
for i := range bundles {
assert.Equal(t, settingsmsg.Bundle_TYPE_DEFAULT, bundles[i].Type)
}
// check that ListBundles filtered by an id only returns that bundle
filteredBundles, err := s.ListBundles(settingsmsg.Bundle_TYPE_DEFAULT, []string{bundle2})
if err != nil {
t.Error(err)
}
assert.Equal(t, 1, len(filteredBundles))
if len(filteredBundles) == 1 {
assert.Equal(t, bundle2, filteredBundles[0].Id)
}
// check that ListRoles only returns bundles with type ROLE
roles, err := s.ListBundles(settingsmsg.Bundle_TYPE_ROLE, []string{})
if err != nil {
t.Error(err)
}
for i := range roles {
assert.Equal(t, settingsmsg.Bundle_TYPE_ROLE, roles[i].Type)
}
burnRoot()
}

View File

@@ -1,60 +0,0 @@
package store
import (
"fmt"
"io"
"os"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
"google.golang.org/protobuf/encoding/protojson"
"google.golang.org/protobuf/proto"
)
// Unmarshal file into record
func (s Store) parseRecordFromFile(record proto.Message, filePath string) error {
_, err := os.Stat(filePath)
if err != nil {
return fmt.Errorf("%q: %w", filePath, settings.ErrNotFound)
}
file, err := os.Open(filePath)
if err != nil {
return err
}
defer file.Close()
b, err := io.ReadAll(file)
if err != nil {
return err
}
if len(b) == 0 {
return fmt.Errorf("%q: %w", filePath, settings.ErrNotFound)
}
if err := protojson.Unmarshal(b, record); err != nil {
return err
}
return nil
}
// Marshal record into file
func (s Store) writeRecordToFile(record proto.Message, filePath string) error {
file, err := os.Create(filePath)
if err != nil {
return err
}
defer file.Close()
v, err := protojson.Marshal(record)
if err != nil {
return err
}
_, err = file.Write(v)
if err != nil {
return err
}
return nil
}

View File

@@ -1,65 +0,0 @@
package store
import (
"os"
"path/filepath"
)
const folderNameBundles = "bundles"
const folderNameValues = "values"
const folderNameAssignments = "assignments"
// buildFolderPathForBundles builds the folder path for storing settings bundles. If mkdir is true, folders in the path will be created if necessary.
func (s Store) buildFolderPathForBundles(mkdir bool) string {
folderPath := filepath.Join(s.dataPath, folderNameBundles)
if mkdir {
s.ensureFolderExists(folderPath)
}
return folderPath
}
// buildFilePathForBundle builds a unique file name from the given params. If mkdir is true, folders in the path will be created if necessary.
func (s Store) buildFilePathForBundle(bundleID string, mkdir bool) string {
extensionFolder := s.buildFolderPathForBundles(mkdir)
return filepath.Join(extensionFolder, bundleID+".json")
}
// buildFolderPathForValues builds the folder path for storing settings values. If mkdir is true, folders in the path will be created if necessary.
func (s Store) buildFolderPathForValues(mkdir bool) string {
folderPath := filepath.Join(s.dataPath, folderNameValues)
if mkdir {
s.ensureFolderExists(folderPath)
}
return folderPath
}
// buildFilePathForValue builds a unique file name from the given params. If mkdir is true, folders in the path will be created if necessary.
func (s Store) buildFilePathForValue(valueID string, mkdir bool) string {
extensionFolder := s.buildFolderPathForValues(mkdir)
return filepath.Join(extensionFolder, valueID+".json")
}
// buildFolderPathForRoleAssignments builds the folder path for storing role assignments. If mkdir is true, folders in the path will be created if necessary.
func (s Store) buildFolderPathForRoleAssignments(mkdir bool) string {
roleAssignmentsFolder := filepath.Join(s.dataPath, folderNameAssignments)
if mkdir {
s.ensureFolderExists(roleAssignmentsFolder)
}
return roleAssignmentsFolder
}
// buildFilePathForRoleAssignment builds a unique file name from the given params. If mkdir is true, folders in the path will be created if necessary.
func (s Store) buildFilePathForRoleAssignment(assignmentID string, mkdir bool) string {
roleAssignmentsFolder := s.buildFolderPathForRoleAssignments(mkdir)
return filepath.Join(roleAssignmentsFolder, assignmentID+".json")
}
// ensureFolderExists checks if the given path is an existing folder and creates one if not existing
func (s Store) ensureFolderExists(path string) {
if _, err := os.Stat(path); os.IsNotExist(err) {
err = os.MkdirAll(path, 0700)
if err != nil {
s.Logger.Err(err).Msgf("Error creating folder %v", path)
}
}
}

View File

@@ -1,72 +0,0 @@
package store
import (
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
"github.com/owncloud/ocis/v2/services/settings/pkg/util"
)
// ListPermissionsByResource collects all permissions from the provided roleIDs that match the requested resource
func (s Store) ListPermissionsByResource(resource *settingsmsg.Resource, roleIDs []string) ([]*settingsmsg.Permission, error) {
records := make([]*settingsmsg.Permission, 0)
for _, roleID := range roleIDs {
role, err := s.ReadBundle(roleID)
if err != nil {
s.Logger.Debug().Str("roleID", roleID).Msg("role not found, skipping")
continue
}
records = append(records, extractPermissionsByResource(resource, role)...)
}
return records, nil
}
// ReadPermissionByID finds the permission in the roles, specified by the provided roleIDs
func (s Store) ReadPermissionByID(permissionID string, roleIDs []string) (*settingsmsg.Permission, error) {
for _, roleID := range roleIDs {
role, err := s.ReadBundle(roleID)
if err != nil {
s.Logger.Debug().Str("roleID", roleID).Msg("role not found, skipping")
continue
}
for _, permission := range role.Settings {
if permission.Id == permissionID {
if value, ok := permission.Value.(*settingsmsg.Setting_PermissionValue); ok {
return value.PermissionValue, nil
}
}
}
}
return nil, nil
}
// ReadPermissionByName finds the permission in the roles, specified by the provided roleIDs
func (s Store) ReadPermissionByName(name string, roleIDs []string) (*settingsmsg.Permission, error) {
for _, roleID := range roleIDs {
role, err := s.ReadBundle(roleID)
if err != nil {
s.Logger.Debug().Str("roleID", roleID).Msg("role not found, skipping")
continue
}
for _, permission := range role.Settings {
if permission.Name == name {
if value, ok := permission.Value.(*settingsmsg.Setting_PermissionValue); ok {
return value.PermissionValue, nil
}
}
}
}
return nil, settings.ErrNotFound
}
// extractPermissionsByResource collects all permissions from the provided role that match the requested resource
func extractPermissionsByResource(resource *settingsmsg.Resource, role *settingsmsg.Bundle) []*settingsmsg.Permission {
permissions := make([]*settingsmsg.Permission, 0)
for _, setting := range role.Settings {
if value, ok := setting.Value.(*settingsmsg.Setting_PermissionValue); ok {
if util.IsResourceMatched(setting.Resource, resource) {
permissions = append(permissions, value.PermissionValue)
}
}
}
return permissions
}

View File

@@ -1 +0,0 @@
checks = ["all", "-ST1003", "-ST1000", "-SA1019"]

View File

@@ -1,50 +0,0 @@
// Package store implements the go-micro store interface
package store
import (
"os"
olog "github.com/owncloud/ocis/v2/ocis-pkg/log"
"github.com/owncloud/ocis/v2/services/settings/pkg/config"
"github.com/owncloud/ocis/v2/services/settings/pkg/settings"
)
var (
// Name is the default name for the settings store
Name = "ocis-settings"
managerName = "filesystem"
)
// Store interacts with the filesystem to manage settings information
type Store struct {
dataPath string
Logger olog.Logger
}
// New creates a new store
func New(cfg *config.Config) settings.Manager {
s := Store{
//Logger: olog.NewLogger(
// olog.Color(cfg.Log.Color),
// olog.Pretty(cfg.Log.Pretty),
// olog.Level(cfg.Log.Level),
// olog.File(cfg.Log.File),
//),
}
if _, err := os.Stat(cfg.DataPath); err != nil {
s.Logger.Info().Msgf("creating container on %v", cfg.DataPath)
err = os.MkdirAll(cfg.DataPath, 0700)
if err != nil {
s.Logger.Err(err).Msgf("providing container on %v", cfg.DataPath)
}
}
s.dataPath = cfg.DataPath
return &s
}
func init() {
settings.Registry[managerName] = New
}

View File

@@ -1,39 +0,0 @@
package store
import (
"os"
"path/filepath"
)
const (
// account UUIDs
accountUUID1 = "c4572da7-6142-4383-8fc6-efde3d463036"
//accountUUID2 = "e11f9769-416a-427d-9441-41a0e51391d7"
//accountUUID3 = "633ecd77-1980-412a-8721-bf598a330bb4"
// extension names
extension1 = "test-extension-1"
extension2 = "test-extension-2"
// bundle ids
bundle1 = "2f06addf-4fd2-49d5-8f71-00fbd3a3ec47"
bundle2 = "2d745744-749c-4286-8e92-74a24d8331c5"
bundle3 = "d8fd27d1-c00b-4794-a658-416b756a72ff"
// setting ids
setting1 = "c7ebbc8b-d15a-4f2e-9d7d-d6a4cf858d1a"
setting2 = "3fd9a3d9-20b7-40d4-9294-b22bb5868c10"
setting3 = "24bb9535-3df4-42f1-a622-7c0562bec99f"
// value ids
value1 = "fd3b6221-dc13-4a22-824d-2480495f1cdb"
value2 = "2a0bd9b0-ca1d-491a-8c56-d2ddfd68ded8"
//value3 = "b42702d2-5e4d-4d73-b133-e1f9e285355e"
dataRoot = "/tmp/herecomesthesun"
)
func burnRoot() {
os.RemoveAll(filepath.Join(dataRoot, "values"))
os.RemoveAll(filepath.Join(dataRoot, "bundles"))
}

View File

@@ -1,109 +0,0 @@
// Package store implements the go-micro store interface
package store
import (
"fmt"
"os"
"path/filepath"
"github.com/gofrs/uuid"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
)
// ListValues reads all values that match the given bundleId and accountUUID.
// If the bundleId is empty, it's ignored for filtering.
// If the accountUUID is empty, only values with empty accountUUID are returned.
// If the accountUUID is not empty, values with an empty or with a matching accountUUID are returned.
func (s Store) ListValues(bundleID, accountUUID string) ([]*settingsmsg.Value, error) {
valuesFolder := s.buildFolderPathForValues(false)
valueFiles, err := os.ReadDir(valuesFolder)
if err != nil {
return nil, err
}
records := make([]*settingsmsg.Value, 0, len(valueFiles))
for _, valueFile := range valueFiles {
record := settingsmsg.Value{}
err := s.parseRecordFromFile(&record, filepath.Join(valuesFolder, valueFile.Name()))
if err != nil {
s.Logger.Warn().Msgf("error reading %v", valueFile)
continue
}
if bundleID != "" && record.BundleId != bundleID {
continue
}
// if requested accountUUID empty -> fetch all system level values
if accountUUID == "" && record.AccountUuid != "" {
continue
}
// if requested accountUUID empty -> fetch all individual + all system level values
if accountUUID != "" && record.AccountUuid != "" && record.AccountUuid != accountUUID {
continue
}
records = append(records, &record)
}
return records, nil
}
// ReadValue tries to find a value by the given valueId within the dataPath
func (s Store) ReadValue(valueID string) (*settingsmsg.Value, error) {
filePath := s.buildFilePathForValue(valueID, false)
record := settingsmsg.Value{}
if err := s.parseRecordFromFile(&record, filePath); err != nil {
return nil, err
}
s.Logger.Debug().Msgf("read contents from file: %v", filePath)
return &record, nil
}
// ReadValueByUniqueIdentifiers tries to find a value given a set of unique identifiers
func (s Store) ReadValueByUniqueIdentifiers(accountUUID, settingID string) (*settingsmsg.Value, error) {
valuesFolder := s.buildFolderPathForValues(false)
files, err := os.ReadDir(valuesFolder)
if err != nil {
return nil, err
}
for i := range files {
if !files[i].IsDir() {
r := settingsmsg.Value{}
s.Logger.Debug().Msgf("reading contents from file: %v", filepath.Join(valuesFolder, files[i].Name()))
if err := s.parseRecordFromFile(&r, filepath.Join(valuesFolder, files[i].Name())); err != nil {
s.Logger.Debug().Msgf("match found: %v", filepath.Join(valuesFolder, files[i].Name()))
return nil, err
}
// if value saved without accountUUID, then it's a global value
if r.AccountUuid == "" && r.SettingId == settingID {
return &r, nil
}
// if value saved with accountUUID, then it's a user specific value
if r.AccountUuid == accountUUID && r.SettingId == settingID {
return &r, nil
}
}
}
return nil, fmt.Errorf("could not read value by settingID=%v and accountID=%v", settingID, accountUUID)
}
// WriteValue writes the given value into a file within the dataPath
func (s Store) WriteValue(value *settingsmsg.Value) (*settingsmsg.Value, error) {
s.Logger.Debug().Str("value", value.String()).Msg("writing value")
if value.Id == "" {
value.Id = uuid.Must(uuid.NewV4()).String()
}
// modify value depending on associated resource
if value.Resource.Type == settingsmsg.Resource_TYPE_SYSTEM {
value.AccountUuid = ""
}
// write the value
filePath := s.buildFilePathForValue(value.Id, true)
if err := s.writeRecordToFile(value, filePath); err != nil {
return nil, err
}
return value, nil
}

View File

@@ -1,70 +0,0 @@
package store
import (
"testing"
olog "github.com/owncloud/ocis/v2/ocis-pkg/log"
settingsmsg "github.com/owncloud/ocis/v2/protogen/gen/ocis/messages/settings/v0"
"github.com/stretchr/testify/assert"
)
var valueScenarios = []struct {
name string
value *settingsmsg.Value
}{
{
name: "generic-test-with-system-resource",
value: &settingsmsg.Value{
Id: value1,
BundleId: bundle1,
SettingId: setting1,
AccountUuid: accountUUID1,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Value_StringValue{
StringValue: "lalala",
},
},
},
{
name: "generic-test-with-file-resource",
value: &settingsmsg.Value{
Id: value2,
BundleId: bundle1,
SettingId: setting2,
AccountUuid: accountUUID1,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_FILE,
Id: "adfba82d-919a-41c3-9cd1-5a3f83b2bf76",
},
Value: &settingsmsg.Value_StringValue{
StringValue: "tralala",
},
},
},
}
func TestValues(t *testing.T) {
s := Store{
dataPath: dataRoot,
Logger: olog.NewLogger(
olog.Color(true),
olog.Pretty(true),
olog.Level("info"),
),
}
for i := range valueScenarios {
index := i
t.Run(valueScenarios[index].name, func(t *testing.T) {
filePath := s.buildFilePathForValue(valueScenarios[index].value.Id, true)
if err := s.writeRecordToFile(valueScenarios[index].value, filePath); err != nil {
t.Error(err)
}
assert.FileExists(t, filePath)
})
}
burnRoot()
}

View File

@@ -2,6 +2,5 @@ package store
import (
// init filesystem store
_ "github.com/owncloud/ocis/v2/services/settings/pkg/store/filesystem"
_ "github.com/owncloud/ocis/v2/services/settings/pkg/store/metadata"
)

View File

@@ -14,7 +14,6 @@ services:
STORAGE_USERS_DRIVER_OCIS_ROOT: /srv/app/tmp/ocis/storage/users
STORAGE_SYSTEM_DRIVER_OCIS_ROOT: /srv/app/tmp/ocis/storage/metadata
SHARING_USER_JSON_FILE: /srv/app/tmp/ocis/shares.json
SETTINGS_DATA_PATH: "/srv/app/tmp/ocis/settings"
PROXY_ENABLE_BASIC_AUTH: "true"
WEB_UI_CONFIG_FILE: /drone/src/tests/config/drone/ocis-config.json
ACCOUNTS_HASH_DIFFICULTY: 4