Initial QSfera import

This commit is contained in:
Курнат Андрей
2026-06-07 10:20:04 +03:00
commit 2315f25754
16485 changed files with 4826827 additions and 0 deletions
@@ -0,0 +1,695 @@
package defaults
import (
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/qsfera/server/services/settings/pkg/config"
)
const (
// BundleUUIDRoleAdmin represents the admin role
BundleUUIDRoleAdmin = "71881883-1768-46bd-a24d-a356a2afdf7f"
// BundleUUIDRoleSpaceAdmin represents the space admin role
BundleUUIDRoleSpaceAdmin = "2aadd357-682c-406b-8874-293091995fdd"
// BundleUUIDRoleUser represents the user role.
BundleUUIDRoleUser = "d7beeea8-8ff4-406b-8fb6-ab2dd81e6b11"
// BundleUUIDRoleUserLight represents the user light role.
BundleUUIDRoleUserLight = "38071a68-456a-4553-846a-fa67bf5596cc"
// BundleUUIDRoleGuest represents the guest role.
BundleUUIDRoleGuest = "38071a68-456a-4553-846a-fa67bf5596cc"
// BundleUUIDProfile represents the user profile.
BundleUUIDProfile = "2a506de7-99bd-4f0d-994e-c38e72c28fd9"
// BundleUUIDServiceAccount represents the service account role.
BundleUUIDServiceAccount = "bcceed81-c610-49cc-ab77-39a024e8da12"
// SettingUUIDProfileLanguage is the hardcoded setting UUID for the user profile language
SettingUUIDProfileLanguage = "aa8cfbe5-95d4-4f7e-a032-c3c01f5f062f"
// SettingUUIDProfileDisableNotifications is the hardcoded setting UUID for the disable notifications setting
SettingUUIDProfileDisableNotifications = "33ffb5d6-cd07-4dc0-afb0-84f7559ae438"
// SettingUUIDProfileAutoAcceptShares is the hardcoded setting UUID for the disable notifications setting
SettingUUIDProfileAutoAcceptShares = "ec3ed4a3-3946-4efc-8f9f-76d38b12d3a9"
// SettingUUIDProfileEmailSendingInterval is the hardcoded setting UUID for the email sending interval setting
SettingUUIDProfileEmailSendingInterval = "08dec2fe-3f97-42a9-9d1b-500855e92f25"
// SettingUUIDProfileEventShareCreated it the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventShareCreated = "872d8ef6-6f2a-42ab-af7d-f53cc81d7046"
// SettingUUIDProfileEventShareRemoved is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventShareRemoved = "d7484394-8321-4c84-9677-741ba71e1f80"
// SettingUUIDProfileEventShareExpired is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventShareExpired = "e1aa0b7c-1b0f-4072-9325-c643c89fee4e"
// SettingUUIDProfileEventSpaceShared is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventSpaceShared = "694d5ee1-a41c-448c-8d14-396b95d2a918"
// SettingUUIDProfileEventSpaceUnshared is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventSpaceUnshared = "26c20e0e-98df-4483-8a77-759b3a766af0"
// SettingUUIDProfileEventSpaceMembershipExpired is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventSpaceMembershipExpired = "7275921e-b737-4074-ba91-3c2983be3edd"
// SettingUUIDProfileEventSpaceDisabled is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventSpaceDisabled = "eb5c716e-03be-42c6-9ed1-1105d24e109f"
// SettingUUIDProfileEventSpaceDeleted is the hardcoded setting UUID for the send in app setting
SettingUUIDProfileEventSpaceDeleted = "094ceca9-5a00-40ba-bb1a-bbc7bccd39ee"
// SettingUUIDProfileEventPostprocessingStepFinished is the hardcoded setting UUID for the send in mail setting
SettingUUIDProfileEventPostprocessingStepFinished = "fe0a3011-d886-49c8-b797-33d02fa426ef"
)
// GenerateBundlesDefaultRoles bootstraps the default roles.
func GenerateBundlesDefaultRoles() []*settingsmsg.Bundle {
return []*settingsmsg.Bundle{
generateBundleAdminRole(),
generateBundleUserRole(),
generateBundleUserLightRole(),
generateBundleProfileRequest(),
generateBundleSpaceAdminRole(),
}
}
// GenerateDefaultProfileBundle return the default profile bundle.
func GenerateDefaultProfileBundle() *settingsmsg.Bundle {
return generateBundleProfileRequest()
}
// ServiceAccountBundle returns the service account bundle
func ServiceAccountBundle() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDServiceAccount,
Name: "service-account",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "qsfera-roles",
DisplayName: "Service Account",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
AccountManagementPermission(All),
ChangeLogoPermission(All),
CreatePublicLinkPermission(All),
CreateSharePermission(All),
CreateSpacesPermission(All),
DeletePersonalSpacesPermission(All),
DeleteProjectSpacesPermission(All),
DeleteReadOnlyPublicLinkPasswordPermission(All),
GroupManagementPermission(All),
LanguageManagementPermission(All),
ListFavoritesPermission(All),
ListSpacesPermission(All),
ManageSpacePropertiesPermission(All),
RoleManagementPermission(All),
SetPersonalSpaceQuotaPermission(All),
SetProjectSpaceQuotaPermission(All),
SettingsManagementPermission(All),
SpaceAbilityPermission(All),
WriteFavoritesPermission(All),
// TODO: add more permissions? remove some?
},
}
}
func generateBundleAdminRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleAdmin,
Name: "admin",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "qsfera-roles",
DisplayName: "Admin",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
AccountManagementPermission(All),
AutoAcceptSharesPermission(Own),
ChangeLogoPermission(All),
CreatePublicLinkPermission(All),
CreateSharePermission(All),
CreateSpacesPermission(All),
DeletePersonalSpacesPermission(All),
DeleteProjectSpacesPermission(All),
DeleteReadOnlyPublicLinkPasswordPermission(All),
DisableEmailNotificationsPermission(Own),
ProfileEmailSendingIntervalPermission(Own),
ProfileEventShareCreatedPermission(Own),
ProfileEventShareRemovedPermission(Own),
ProfileEventShareExpiredPermission(Own),
ProfileEventSpaceSharedPermission(Own),
ProfileEventSpaceUnsharedPermission(Own),
ProfileEventSpaceMembershipExpiredPermission(Own),
ProfileEventSpaceDisabledPermission(Own),
ProfileEventSpaceDeletedPermission(Own),
ProfileEventPostprocessingStepFinishedPermission(Own),
GroupManagementPermission(All),
LanguageManagementPermission(All),
ListFavoritesPermission(Own),
ListSpacesPermission(All),
ManageSpacePropertiesPermission(All),
RoleManagementPermission(All),
SetPersonalSpaceQuotaPermission(All),
SetProjectSpaceQuotaPermission(All),
SettingsManagementPermission(All),
SpaceAbilityPermission(All),
WebOfficeManagementPermssion(All),
WriteFavoritesPermission(Own),
},
}
}
func generateBundleSpaceAdminRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleSpaceAdmin,
Name: "spaceadmin",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "qsfera-roles",
DisplayName: "Space Admin",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
AutoAcceptSharesPermission(Own),
CreatePublicLinkPermission(All),
CreateSharePermission(All),
CreateSpacesPermission(All),
DeleteProjectSpacesPermission(All),
DeleteReadOnlyPublicLinkPasswordPermission(All),
DisableEmailNotificationsPermission(Own),
ProfileEmailSendingIntervalPermission(Own),
ProfileEventShareCreatedPermission(Own),
ProfileEventShareRemovedPermission(Own),
ProfileEventShareExpiredPermission(Own),
ProfileEventSpaceSharedPermission(Own),
ProfileEventSpaceUnsharedPermission(Own),
ProfileEventSpaceMembershipExpiredPermission(Own),
ProfileEventSpaceDisabledPermission(Own),
ProfileEventSpaceDeletedPermission(Own),
ProfileEventPostprocessingStepFinishedPermission(Own),
LanguageManagementPermission(Own),
ListFavoritesPermission(Own),
ListSpacesPermission(All),
ManageSpacePropertiesPermission(All),
SelfManagementPermission(Own),
SetProjectSpaceQuotaPermission(All),
SpaceAbilityPermission(All),
WriteFavoritesPermission(Own),
},
}
}
func generateBundleUserRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleUser,
Name: "user",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "qsfera-roles",
DisplayName: "User",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
AutoAcceptSharesPermission(Own),
CreatePublicLinkPermission(All),
CreateSharePermission(All),
CreateSpacesPermission(Own),
DisableEmailNotificationsPermission(Own),
ProfileEmailSendingIntervalPermission(Own),
ProfileEventShareCreatedPermission(Own),
ProfileEventShareRemovedPermission(Own),
ProfileEventShareExpiredPermission(Own),
ProfileEventSpaceSharedPermission(Own),
ProfileEventSpaceUnsharedPermission(Own),
ProfileEventSpaceMembershipExpiredPermission(Own),
ProfileEventSpaceDisabledPermission(Own),
ProfileEventSpaceDeletedPermission(Own),
ProfileEventPostprocessingStepFinishedPermission(Own),
LanguageManagementPermission(Own),
ListFavoritesPermission(Own),
SelfManagementPermission(Own),
WriteFavoritesPermission(Own),
},
}
}
func generateBundleUserLightRole() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDRoleUserLight,
Name: "user-light",
Type: settingsmsg.Bundle_TYPE_ROLE,
Extension: "qsfera-roles",
DisplayName: "User Light",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Settings: []*settingsmsg.Setting{
AutoAcceptSharesPermission(Own),
DisableEmailNotificationsPermission(Own),
ProfileEmailSendingIntervalPermission(Own),
LanguageManagementPermission(Own),
},
}
}
func generateBundleProfileRequest() *settingsmsg.Bundle {
return &settingsmsg.Bundle{
Id: BundleUUIDProfile,
Name: "profile",
Extension: "qsfera-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,
},
{
Id: SettingUUIDProfileDisableNotifications,
Name: "disable-email-notifications",
DisplayName: "Disable Email Notifications",
Description: "Disable email notifications",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_BoolValue{BoolValue: &settingsmsg.Bool{Default: false, Label: "disable notifications"}},
},
{
Id: SettingUUIDProfileAutoAcceptShares,
Name: "auto-accept-shares",
DisplayName: "Auto accept shares",
Description: "Automatically accept shares",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_BoolValue{BoolValue: &settingsmsg.Bool{Default: true, Label: "auto accept shares"}},
},
{
Id: SettingUUIDProfileEmailSendingInterval,
Name: "email-sending-interval-options",
DisplayName: TemplateEmailSendingInterval,
Description: TemplateEmailSendingIntervalDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &sendEmailOptions,
},
{
Id: SettingUUIDProfileEventShareCreated,
Name: "event-share-created-options",
DisplayName: TemplateShareCreated,
Description: TemplateShareCreatedDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailTrue,
},
},
},
},
{
Id: SettingUUIDProfileEventShareRemoved,
Name: "event-share-removed-options",
DisplayName: TemplateShareRemoved,
Description: TemplateShareRemovedDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailFalseDisabled,
},
},
},
},
{
Id: SettingUUIDProfileEventShareExpired,
Name: "event-share-expired-options",
DisplayName: TemplateShareExpired,
Description: TemplateShareExpiredDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailTrue,
},
},
},
},
{
Id: SettingUUIDProfileEventSpaceShared,
Name: "event-space-shared-options",
DisplayName: TemplateSpaceShared,
Description: TemplateSpaceSharedDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailTrue,
},
},
},
},
{
Id: SettingUUIDProfileEventSpaceUnshared,
Name: "event-space-unshared-options",
DisplayName: TemplateSpaceUnshared,
Description: TemplateSpaceUnsharedDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailTrue,
},
},
},
},
{
Id: SettingUUIDProfileEventSpaceMembershipExpired,
Name: "event-space-membership-expired-options",
DisplayName: TemplateSpaceMembershipExpired,
Description: TemplateSpaceMembershipExpiredDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailTrue,
},
},
},
},
{
Id: SettingUUIDProfileEventSpaceDisabled,
Name: "event-space-disabled-options",
DisplayName: TemplateSpaceDisabled,
Description: TemplateSpaceDisabledDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailFalseDisabled,
},
},
},
},
{
Id: SettingUUIDProfileEventSpaceDeleted,
Name: "event-space-deleted-options",
DisplayName: TemplateSpaceDeleted,
Description: TemplateSpaceDeletedDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailFalseDisabled,
},
},
},
},
{
Id: SettingUUIDProfileEventPostprocessingStepFinished,
Name: "event-postprocessing-step-finished-options",
DisplayName: TemplateFileRejected,
Description: TemplateFileRejectedDescription,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_USER,
},
Value: &settingsmsg.Setting_MultiChoiceCollectionValue{
MultiChoiceCollectionValue: &settingsmsg.MultiChoiceCollection{
Options: []*settingsmsg.MultiChoiceCollectionOption{
&optionInAppTrue,
&optionMailFalseDisabled,
},
},
},
},
},
}
}
var sendEmailOptions = settingsmsg.Setting_SingleChoiceValue{
SingleChoiceValue: &settingsmsg.SingleChoiceList{
Options: []*settingsmsg.ListOption{
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "instant",
},
},
DisplayValue: TemplateIntervalInstant,
Default: true,
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "daily",
},
},
DisplayValue: TemplateIntervalDaily,
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "weekly",
},
},
DisplayValue: TemplateIntervalWeekly,
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "never",
},
},
DisplayValue: TemplateIntervalNever,
},
},
},
}
var optionInAppTrue = settingsmsg.MultiChoiceCollectionOption{
Key: "in-app",
DisplayValue: "In-App",
Value: &settingsmsg.MultiChoiceCollectionOptionValue{
Option: &settingsmsg.MultiChoiceCollectionOptionValue_BoolValue{
BoolValue: &settingsmsg.Bool{
Default: true,
},
},
},
}
var optionMailTrue = settingsmsg.MultiChoiceCollectionOption{
Key: "mail",
DisplayValue: "Email",
Value: &settingsmsg.MultiChoiceCollectionOptionValue{
Option: &settingsmsg.MultiChoiceCollectionOptionValue_BoolValue{
BoolValue: &settingsmsg.Bool{
Default: true,
},
},
},
}
var optionMailFalseDisabled = settingsmsg.MultiChoiceCollectionOption{
Key: "mail",
Attribute: "disabled",
DisplayValue: "Email",
Value: &settingsmsg.MultiChoiceCollectionOptionValue{
Option: &settingsmsg.MultiChoiceCollectionOptionValue_BoolValue{
BoolValue: &settingsmsg.Bool{
Default: false,
},
},
},
}
// TODO: languageSetting needed?
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",
},
{
Value: &settingsmsg.ListOptionValue{
Option: &settingsmsg.ListOptionValue_StringValue{
StringValue: "zh",
},
},
DisplayValue: "汉语",
},
},
},
}
// DefaultRoleAssignments returns (as one might guess) the default role assignments
func DefaultRoleAssignments(cfg *config.Config) []*settingsmsg.UserRoleAssignment {
assignments := []*settingsmsg.UserRoleAssignment{}
if cfg.SetupDefaultAssignments {
assignments = []*settingsmsg.UserRoleAssignment{
// default users with role "user"
{
AccountUuid: "b1f74ec4-dd7e-11ef-a543-03775734d0f7",
RoleId: BundleUUIDRoleUser,
},
{
AccountUuid: "056fc874-dd7f-11ef-ba84-af6fca4b7289",
RoleId: BundleUUIDRoleUser,
},
{
AccountUuid: "60708dda-e897-11ef-919f-bbb7437d6ec2",
RoleId: BundleUUIDRoleUser,
},
{
// additional admin user
AccountUuid: "cd88bf9a-dd7f-11ef-a609-7f78deb2345f", // demo user "dennis"
RoleId: BundleUUIDRoleAdmin,
},
{
// default users with role "spaceadmin"
AccountUuid: "801abee4-dd7f-11ef-a324-83f55a754b62",
RoleId: BundleUUIDRoleSpaceAdmin,
},
{
// service user
AccountUuid: "service-user-id",
RoleId: BundleUUIDRoleAdmin,
},
}
}
if cfg.AdminUserID != "" {
// default admin user
assignments = append(assignments, &settingsmsg.UserRoleAssignment{
AccountUuid: cfg.AdminUserID,
RoleId: BundleUUIDRoleAdmin,
})
}
return assignments
}
@@ -0,0 +1,642 @@
package defaults
import settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
var (
// All is a convenience variable to set constraint to all
All = settingsmsg.Permission_CONSTRAINT_ALL
// Own is a convenience variable to set constraint to own
Own = settingsmsg.Permission_CONSTRAINT_OWN
)
// AccountManagementPermission is the permission to manage accounts
func AccountManagementPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "8e587774-d929-4215-910b-a317b1e80f73",
Name: "Accounts.ReadWrite",
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: c,
},
},
}
}
// AutoAcceptSharesPermission is the permission to enable share auto-accept
func AutoAcceptSharesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "4e41363c-a058-40a5-aec8-958897511209",
Name: "AutoAcceptShares.ReadWriteDisabled",
DisplayName: "enable/disable auto accept shares",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileAutoAcceptShares,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ChangeLogoPermission is the permission to change the logo
func ChangeLogoPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "ed83fc10-1f54-4a9e-b5a7-fb517f5f3e01",
Name: "Logo.Write",
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: c,
},
},
}
}
// CreatePublicLinkPermission is the permission to create public links
func CreatePublicLinkPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "11516bbd-7157-49e1-b6ac-d00c820f980b",
Name: "PublicLink.Write",
DisplayName: "Write publiclink",
Description: "This permission allows creating public links.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SHARE,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_WRITE,
Constraint: c,
},
},
}
}
// CreateSharePermission is the permission to create shares
func CreateSharePermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "069c08b1-e31f-4799-9ed6-194b310e7244",
Name: "Shares.Write",
DisplayName: "Write share",
Description: "This permission allows creating shares.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SHARE,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_WRITE,
Constraint: c,
},
},
}
}
// CreateSpacesPermission is the permission to create spaces
func CreateSpacesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "79e13b30-3e22-11eb-bc51-0b9f0bad9a58",
Name: "Drives.Create",
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: c,
},
},
}
}
// DeletePersonalSpacesPermission is the permission to delete personal spaces
func DeletePersonalSpacesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "5de9fe0a-4bc5-4a47-b758-28f370caf169",
Name: "Drives.DeletePersonal",
DisplayName: "Delete All Home Spaces",
Description: "This permission allows deleting home spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_DELETE,
Constraint: c,
},
},
}
}
// DeleteProjectSpacesPermission is the permission to delete project spaces
func DeleteProjectSpacesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "fb60b004-c1fa-4f09-bf87-55ce7d46ac61",
Name: "Drives.DeleteProject",
DisplayName: "Delete AllSpaces",
Description: "This permission allows deleting all spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_DELETE,
Constraint: c,
},
},
}
}
// DeleteReadOnlyPublicLinkPasswordPermission is the permission to delete read-only public link passwords
func DeleteReadOnlyPublicLinkPasswordPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "e9a697c5-c67b-40fc-982b-bcf628e9916d",
Name: "ReadOnlyPublicLinkPassword.Delete",
DisplayName: "Delete Read-Only Public link password",
Description: "This permission permits to opt out of a public link password enforcement.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SHARE,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_WRITE,
Constraint: c,
},
},
}
}
// DisableEmailNotificationsPermission is the permission to disable email notifications
func DisableEmailNotificationsPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "ad5bb5e5-dc13-4cd3-9304-09a424564ea8",
Name: "EmailNotifications.ReadWriteDisabled",
DisplayName: "Disable Email Notifications",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileDisableNotifications,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEmailSendingIntervalPermission is the permission to set the email sending interval
func ProfileEmailSendingIntervalPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "7dc204ee-799a-43b6-b85d-425fb3b1fa5a",
Name: "EmailSendingInterval.ReadWrite",
DisplayName: "Email Sending Interval",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEmailSendingInterval,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventShareCreatedPermission is
func ProfileEventShareCreatedPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "8a50540c-1cdd-481f-b85f-44654393c8f0",
Name: "Event.ShareCreated.ReadWrite",
DisplayName: "Event Share Created",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventShareCreated,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventShareRemovedPermission is the permission to set the email sending interval
func ProfileEventShareRemovedPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "5ef55465-8e39-4a6c-ba97-1d19f5b07116",
Name: "Event.ShareRemoved.ReadWrite",
DisplayName: "Event Share Removed",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventShareRemoved,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventShareExpiredPermission is the permission to set the email sending interval
func ProfileEventShareExpiredPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "7d4f961b-d471-451b-b1fd-ac6a9d59ce88",
Name: "Event.ShareExpired.ReadWrite",
DisplayName: "Event Share Expired",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventShareExpired,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventSpaceSharedPermission is the permission to set the email sending interval
func ProfileEventSpaceSharedPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "feb16d2c-614c-4f79-ac37-755a028f5616",
Name: "Event.SpaceShared.ReadWrite",
DisplayName: "Event Space Shared",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventSpaceShared,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventSpaceUnsharedPermission is the permission to set the email sending interval
func ProfileEventSpaceUnsharedPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "4f979732-631b-4f27-9be7-a89fb223a6d2",
Name: "Event.SpaceUnshared.ReadWrite",
DisplayName: "Event Space Unshared",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventSpaceUnshared,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventSpaceMembershipExpiredPermission is the permission to set the email sending interval
func ProfileEventSpaceMembershipExpiredPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "a3cc45bf-9720-4e08-b403-b9133fe33f0b",
Name: "Event.SpaceMembershipExpired.ReadWrite",
DisplayName: "Event Space Membership Expired",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventSpaceMembershipExpired,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventSpaceDisabledPermission is the permission to set the email sending interval
func ProfileEventSpaceDisabledPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "896194c2-5055-4ea3-94a3-0a1419187a00",
Name: "Event.SpaceDisabled.ReadWrite",
DisplayName: "Event Space Disabled",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventSpaceDisabled,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventSpaceDeletedPermission is the permission to set the email sending interval
func ProfileEventSpaceDeletedPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "2083c280-b140-4b73-a931-9a4af2931531",
Name: "Event.SpaceDeleted.ReadWrite",
DisplayName: "Event Space Deleted",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventSpaceDeleted,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ProfileEventPostprocessingStepFinishedPermission is the permission to set the email sending interval
func ProfileEventPostprocessingStepFinishedPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "27ba8e97-0bdf-4b18-97d4-df44c9568cda",
Name: "Event.PostprocessingStepFinished.ReadWrite",
DisplayName: "Event Postprocessing Step Finished",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileEventPostprocessingStepFinished,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// GroupManagementPermission is the permission to manage groups
func GroupManagementPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "522adfbe-5908-45b4-b135-41979de73245",
Name: "Groups.ReadWrite",
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: c,
},
},
}
}
// LanguageManagementPermission is the permission to manage the language
func LanguageManagementPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "7d81f103-0488-4853-bce5-98dcce36d649",
Name: "Language.ReadWrite",
DisplayName: "Permission to read and set the language",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
Id: SettingUUIDProfileLanguage,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// ListFavoritesPermission is the permission to list favorites
func ListFavoritesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "4ebaa725-bfaa-43c5-9817-78bc9994bde4",
Name: "Favorites.List",
DisplayName: "List Favorites",
Description: "This permission allows listing favorites.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
Constraint: c,
},
},
}
}
// ListSpacesPermission is the permission to list spaces
func ListSpacesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "016f6ddd-9501-4a0a-8ebe-64a20ee8ec82",
Name: "Drives.List",
DisplayName: "List All Spaces",
Description: "This permission allows listing all spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
Constraint: c,
},
},
}
}
// ManageSpacePropertiesPermission is the permission to manage space properties
func ManageSpacePropertiesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "b44b4054-31a2-42b8-bb71-968b15cfbd4f",
Name: "Drives.ReadWrite",
DisplayName: "Manage space properties",
Description: "This permission allows managing space properties such as name and description.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// RoleManagementPermission is the permission to manage roles
func RoleManagementPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "a53e601e-571f-4f86-8fec-d4576ef49c62",
Name: "Roles.ReadWrite",
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: c,
},
},
}
}
// SelfManagementPermission is the permission to manage itself
func SelfManagementPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "e03070e9-4362-4cc6-a872-1c7cb2eb2b8e",
Name: "Self.ReadWrite",
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: c,
},
},
}
}
// SetPersonalSpaceQuotaPermission is the permission to set the quota for personal spaces
func SetPersonalSpaceQuotaPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "4e6f9709-f9e7-44f1-95d4-b762d27b7896",
Name: "Drives.ReadWritePersonalQuota",
DisplayName: "Set Personal Space Quota",
Description: "This permission allows managing personal space quotas.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// SetProjectSpaceQuotaPermission is the permission to set the quota for project spaces
func SetProjectSpaceQuotaPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "977f0ae6-0da2-4856-93f3-22e0a8482489",
Name: "Drives.ReadWriteProjectQuota",
DisplayName: "Set Project Space Quota",
Description: "This permission allows managing project space quotas.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// SettingsManagementPermission is the permission to manage settings
func SettingsManagementPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "3d58f441-4a05-42f8-9411-ef5874528ae1",
Name: "Settings.ReadWrite",
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: c,
},
},
}
}
// SpaceAbilityPermission is the permission to enable or disable spaces
func SpaceAbilityPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "cf3faa8c-50d9-4f84-9650-ff9faf21aa9d",
Name: "Drives.ReadWriteEnabled",
DisplayName: "Space ability",
Description: "This permission allows enabling and disabling spaces.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
// WriteFavoritesPermission is the permission to mark/unmark files as favorites
func WriteFavoritesPermission(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "a54778fd-1c45-47f0-892d-655caf5236f2",
Name: "Favorites.Write",
DisplayName: "Write Favorites",
Description: "This permission allows marking files as favorites.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_FILE,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_WRITE,
Constraint: c,
},
},
}
}
// WebOfficManagementPermssion is the permission to mark/unmark files as favorites
func WebOfficeManagementPermssion(c settingsmsg.Permission_Constraint) *settingsmsg.Setting {
return &settingsmsg.Setting{
Id: "27a29046-a816-424f-bd71-2ffb9029162f",
Name: "WebOffice.Manage",
DisplayName: "Manage WebOffice",
Description: "This permission gives access to the admin features in the WebOffice suite.",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SYSTEM,
},
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READWRITE,
Constraint: c,
},
},
}
}
@@ -0,0 +1,55 @@
package defaults
import "github.com/qsfera/server/pkg/l10n"
// Translatable configuration options
var (
// name of the notification option 'Share Received'
TemplateShareCreated = l10n.Template("Share Received")
// description of the notification option 'Share Received'
TemplateShareCreatedDescription = l10n.Template("Notify when I have received a share")
// name of the notification option 'Share Removed'
TemplateShareRemoved = l10n.Template("Share Removed")
// description of the notification option 'Share Removed'
TemplateShareRemovedDescription = l10n.Template("Notify when a received share has been removed")
// name of the notification option 'Share Expired'
TemplateShareExpired = l10n.Template("Share Expired")
// description of the notification option 'Share Expired'
TemplateShareExpiredDescription = l10n.Template("Notify when a received share has expired")
// name of the notification option 'Space Shared'
TemplateSpaceShared = l10n.Template("Added as space member")
// description of the notification option 'Space Shared'
TemplateSpaceSharedDescription = l10n.Template("Notify when I have been added as a member to a space")
// name of the notification option 'Space Unshared'
TemplateSpaceUnshared = l10n.Template("Removed as space member")
// description of the notification option 'Space Unshared'
TemplateSpaceUnsharedDescription = l10n.Template("Notify when I have been removed as member from a space")
// name of the notification option 'Space Membership Expired'
TemplateSpaceMembershipExpired = l10n.Template("Space membership expired")
// description of the notification option 'Space Membership Expired'
TemplateSpaceMembershipExpiredDescription = l10n.Template("Notify when a space membership has expired")
// name of the notification option 'Space Disabled'
TemplateSpaceDisabled = l10n.Template("Space disabled")
// description of the notification option 'Space Disabled'
TemplateSpaceDisabledDescription = l10n.Template("Notify when a space I am member of has been disabled")
// name of the notification option 'Space Deleted'
TemplateSpaceDeleted = l10n.Template("Space deleted")
// description of the notification option 'Space Deleted'
TemplateSpaceDeletedDescription = l10n.Template("Notify when a space I am member of has been deleted")
// name of the notification option 'File Rejected'
TemplateFileRejected = l10n.Template("File rejected")
// description of the notification option 'File Rejected'
TemplateFileRejectedDescription = l10n.Template("Notify when a file I uploaded was rejected because of a virus infection or policy violation")
// name of the notification option 'Email Interval'
TemplateEmailSendingInterval = l10n.Template("Email sending interval")
// description of the notification option 'Email Interval'
TemplateEmailSendingIntervalDescription = l10n.Template("Selected value:")
// translation for the 'instant' email interval option
TemplateIntervalInstant = l10n.Template("Instant")
// translation for the 'daily' email interval option
TemplateIntervalDaily = l10n.Template("Daily")
// translation for the 'weekly' email interval option
TemplateIntervalWeekly = l10n.Template("Weekly")
// translation for the 'never' email interval option
TemplateIntervalNever = l10n.Template("Never")
)
@@ -0,0 +1,11 @@
package errortypes
// BundleNotFound is the error to use when a bundle is not found.
//
// Deprecated: use the genreric services/settings/pkg/settings.NotFound error
type BundleNotFound string
func (e BundleNotFound) Error() string { return "error: bundle not found: " + string(e) }
// IsBundleNotFound implements the IsBundleNotFound interface.
func (e BundleNotFound) IsBundleNotFound() {}
@@ -0,0 +1,189 @@
// Package store implements the go-micro store interface
package store
import (
"context"
"encoding/json"
"fmt"
"github.com/google/uuid"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"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"
)
// ListRoleAssignments loads and returns all role assignments matching the given assignment identifier.
func (s *Store) ListRoleAssignments(accountUUID string) ([]*settingsmsg.UserRoleAssignment, error) {
// shortcut for service accounts
for _, serviceAccountID := range s.cfg.ServiceAccountIDs {
if accountUUID == serviceAccountID {
return []*settingsmsg.UserRoleAssignment{
{
Id: uuid.NewString(), // should we hardcode this id too?
AccountUuid: accountUUID,
RoleId: defaults.BundleUUIDServiceAccount,
},
}, nil
}
}
s.Init()
ctx := context.TODO()
assIDs, err := s.mdc.ReadDir(ctx, accountPath(accountUUID))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.UserRoleAssignment, 0), nil
default:
return nil, err
}
ass := make([]*settingsmsg.UserRoleAssignment, 0, len(assIDs))
for _, assID := range assIDs {
b, err := s.mdc.SimpleDownload(ctx, assignmentPath(accountUUID, assID))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
a := &settingsmsg.UserRoleAssignment{}
err = json.Unmarshal(b, a)
if err != nil {
return nil, err
}
ass = append(ass, a)
}
return ass, nil
}
// ListRoleAssignmentsByRole returns all role assignmentes matching the give roleID
func (s *Store) ListRoleAssignmentsByRole(roleID string) ([]*settingsmsg.UserRoleAssignment, error) {
s.Init()
ctx := context.TODO()
accountIDs, err := s.mdc.ReadDir(ctx, accountsFolderLocation)
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.UserRoleAssignment, 0), nil
default:
return nil, err
}
assignments := make([]*settingsmsg.UserRoleAssignment, 0, len(accountIDs))
// This is very inefficient, with the current layout we need to iterated through all
// account folders and read each assignment file in there to check if that contains
// the give role ID.
for _, account := range accountIDs {
assignmentIDs, err := s.mdc.ReadDir(ctx, accountPath(account))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.UserRoleAssignment, 0), nil
default:
return nil, err
}
for _, assignmentID := range assignmentIDs {
b, err := s.mdc.SimpleDownload(ctx, assignmentPath(account, assignmentID))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
a := &settingsmsg.UserRoleAssignment{}
err = json.Unmarshal(b, a)
if err != nil {
return nil, err
}
if a.GetRoleId() == roleID {
assignments = append(assignments, a)
}
}
}
return assignments, 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) {
s.Init()
ctx := context.TODO()
// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
err := s.mdc.Delete(ctx, accountPath(accountUUID))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
// already gone, continue
default:
return nil, err
}
err = s.mdc.MakeDirIfNotExist(ctx, accountPath(accountUUID))
if err != nil {
return nil, err
}
ass := &settingsmsg.UserRoleAssignment{
Id: uuid.NewString(),
AccountUuid: accountUUID,
RoleId: roleID,
}
b, err := json.Marshal(ass)
if err != nil {
return nil, err
}
return ass, s.mdc.SimpleUpload(ctx, assignmentPath(accountUUID, ass.Id), b)
}
// RemoveRoleAssignment deletes the given role assignment from the existing assignments of the respective account.
func (s *Store) RemoveRoleAssignment(assignmentID string) error {
s.Init()
ctx := context.TODO()
accounts, err := s.mdc.ReadDir(ctx, accountsFolderLocation)
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound)
default:
return err
}
// TODO: use indexer to avoid spamming Metadata service
for _, accID := range accounts {
assIDs, err := s.mdc.ReadDir(ctx, accountPath(accID))
if err != nil {
// TODO: error?
continue
}
for _, assID := range assIDs {
if assID == assignmentID {
// as per https://github.com/owncloud/product/issues/103 "Each user can have exactly one role"
// we also have to delete the cached dir listing
return s.mdc.Delete(ctx, accountPath(accID))
}
}
}
return fmt.Errorf("assignmentID '%s' %w", assignmentID, settings.ErrNotFound)
}
func accountPath(accountUUID string) string {
return fmt.Sprintf("%s/%s", accountsFolderLocation, accountUUID)
}
func assignmentPath(accountUUID string, assignmentID string) string {
return fmt.Sprintf("%s/%s/%s", accountsFolderLocation, accountUUID, assignmentID)
}
@@ -0,0 +1,279 @@
package store
import (
"log"
"sync"
"testing"
"github.com/google/uuid"
olog "github.com/qsfera/server/pkg/log"
"github.com/qsfera/server/pkg/shared"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/qsfera/server/services/settings/pkg/config/defaults"
"github.com/stretchr/testify/require"
)
var (
alan = "00000000-0000-0000-0000-000000000001"
mary = "00000000-0000-0000-0000-000000000002"
dennis = "00000000-0000-0000-0000-000000000003"
role1 = "11111111-1111-1111-1111-111111111111"
role2 = "22222222-2222-2222-2222-222222222222"
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: "qsfera-settings",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_BUNDLE,
},
Settings: []*settingsmsg.Setting{
{
Id: "updateID",
Name: "update",
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_UPDATE,
},
},
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_SETTING,
},
},
{
Id: "readID",
Name: "read",
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
},
},
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_BUNDLE,
},
},
},
},
{
Id: "44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
Type: settingsmsg.Bundle_TYPE_ROLE,
DisplayName: "another",
Name: "ANOTHER_TEST_ROLE",
Extension: "qsfera-settings",
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_BUNDLE,
},
Settings: []*settingsmsg.Setting{
{
Id: "readID",
Name: "read",
Value: &settingsmsg.Setting_PermissionValue{
PermissionValue: &settingsmsg.Permission{
Operation: settingsmsg.Permission_OPERATION_READ,
},
},
},
},
},
}
)
func initStore() *Store {
s := &Store{
Logger: logger,
l: &sync.Mutex{},
cfg: defaults.DefaultConfig(),
}
s.cfg.Commons = &shared.Commons{
AdminUserID: uuid.NewString(),
}
_ = NewMDC(s)
return s
}
func setupRoles(s *Store) {
for i := range bundles {
if _, err := s.WriteBundle(bundles[i]); err != nil {
log.Fatal("error initializing ", err)
}
}
}
func TestAssignmentUniqueness(t *testing.T) {
var scenarios = []struct {
name string
userID string
firstRole string
secondRole string
}{
{
"roles assignments",
alan,
"f36db5e6-a03c-40df-8413-711c67e40b47",
"44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
s := initStore()
setupRoles(s)
firstAssignment, err := s.WriteRoleAssignment(scenario.userID, scenario.firstRole)
require.NoError(t, err)
require.Equal(t, firstAssignment.RoleId, scenario.firstRole)
// TODO: check entry exists
list, err := s.ListRoleAssignments(scenario.userID)
require.NoError(t, err)
require.Equal(t, 1, len(list))
require.Equal(t, list[0].RoleId, scenario.firstRole)
// creating another assignment shouldn't add another entry, as we support max one role per user.
// assigning the second role should remove the old
secondAssignment, err := s.WriteRoleAssignment(scenario.userID, scenario.secondRole)
require.NoError(t, err)
require.Equal(t, secondAssignment.RoleId, scenario.secondRole)
list, err = s.ListRoleAssignments(scenario.userID)
require.NoError(t, err)
require.Equal(t, 1, len(list))
require.Equal(t, list[0].RoleId, scenario.secondRole)
})
}
}
func TestListRoleAssignmentByRole(t *testing.T) {
type assignment struct {
userID string
roleID string
}
var scenarios = []struct {
name string
assignments []assignment
queryRole string
numResults int
}{
{
name: "just 2 assignments",
assignments: []assignment{
{
userID: alan,
roleID: role1,
}, {
userID: mary,
roleID: role1,
},
},
queryRole: role1,
numResults: 2,
},
{
name: "no assignments match",
assignments: []assignment{
{
userID: alan,
roleID: role1,
}, {
userID: mary,
roleID: role1,
},
},
queryRole: role2,
numResults: 0,
},
{
name: "only one assignment matches",
assignments: []assignment{
{
userID: alan,
roleID: role1,
}, {
userID: mary,
roleID: role1,
}, {
userID: dennis,
roleID: role2,
},
},
queryRole: role2,
numResults: 1,
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
s := initStore()
setupRoles(s)
for _, a := range scenario.assignments {
ass, err := s.WriteRoleAssignment(a.userID, a.roleID)
require.NoError(t, err)
require.Equal(t, ass.RoleId, a.roleID)
}
list, err := s.ListRoleAssignmentsByRole(scenario.queryRole)
require.NoError(t, err)
require.Equal(t, scenario.numResults, len(list))
for _, ass := range list {
require.Equal(t, ass.RoleId, scenario.queryRole)
}
})
}
}
func TestDeleteAssignment(t *testing.T) {
var scenarios = []struct {
name string
userID string
firstRole string
secondRole string
}{
{
"roles assignments",
alan,
"f36db5e6-a03c-40df-8413-711c67e40b47",
"44f1a664-0a7f-461a-b0be-5b59e46bbc7a",
},
}
for _, scenario := range scenarios {
t.Run(scenario.name, func(t *testing.T) {
s := initStore()
setupRoles(s)
assignment, err := s.WriteRoleAssignment(scenario.userID, scenario.firstRole)
require.NoError(t, err)
require.Equal(t, assignment.RoleId, scenario.firstRole)
// TODO: uncomment
// require.True(t, mdc.IDExists(assignment.RoleId))
list, err := s.ListRoleAssignments(scenario.userID)
require.NoError(t, err)
require.Equal(t, 1, len(list))
require.Equal(t, assignment.Id, list[0].Id)
err = s.RemoveRoleAssignment(assignment.Id)
require.NoError(t, err)
// TODO: uncomment
// require.False(t, mdc.IDExists(assignment.RoleId))
list, err = s.ListRoleAssignments(scenario.userID)
require.NoError(t, err)
require.Equal(t, 0, len(list))
err = s.RemoveRoleAssignment(assignment.Id)
require.Error(t, err)
// TODO: do we want a custom error message?
})
}
}
@@ -0,0 +1,165 @@
// Package store implements the go-micro store interface
package store
import (
"context"
"encoding/json"
"errors"
"fmt"
"github.com/google/uuid"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"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"
)
// 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) {
s.Init()
ctx := context.TODO()
if len(bundleIDs) == 0 {
bIDs, err := s.mdc.ReadDir(ctx, bundleFolderLocation)
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.Bundle, 0), nil
default:
return nil, err
}
bundleIDs = bIDs
}
var bundles []*settingsmsg.Bundle
for _, id := range bundleIDs {
if id == defaults.BundleUUIDServiceAccount {
bundles = append(bundles, defaults.ServiceAccountBundle())
continue
}
b, err := s.mdc.SimpleDownload(ctx, bundlePath(id))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
bundle := &settingsmsg.Bundle{}
err = json.Unmarshal(b, bundle)
if err != nil {
return nil, err
}
if bundle.Type == bundleType {
bundles = append(bundles, bundle)
}
}
return bundles, nil
}
// ReadBundle tries to find a bundle by the given id from the metadata service
func (s *Store) ReadBundle(bundleID string) (*settingsmsg.Bundle, error) {
// shortcut for service accounts
if bundleID == defaults.BundleUUIDServiceAccount {
return defaults.ServiceAccountBundle(), nil
}
s.Init()
ctx := context.TODO()
b, err := s.mdc.SimpleDownload(ctx, bundlePath(bundleID))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return nil, fmt.Errorf("bundleID '%s' %w", bundleID, settings.ErrNotFound)
default:
return nil, err
}
bundle := &settingsmsg.Bundle{}
return bundle, json.Unmarshal(b, bundle)
}
// ReadSetting tries to find a setting by the given id from the metadata service
func (s *Store) ReadSetting(settingID string) (*settingsmsg.Setting, error) {
s.Init()
ctx := context.TODO()
ids, err := s.mdc.ReadDir(ctx, bundleFolderLocation)
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return nil, fmt.Errorf("settingID '%s' %w", settingID, settings.ErrNotFound)
default:
return nil, err
}
// TODO: avoid spamming metadata service
for _, id := range ids {
b, err := s.ReadBundle(id)
if err != nil {
if errors.Is(err, settings.ErrNotFound) {
continue
}
return nil, err
}
for _, setting := range b.Settings {
if setting.Id == settingID {
return setting, nil
}
}
}
return nil, fmt.Errorf("settingID '%s' %w", settingID, settings.ErrNotFound)
}
// WriteBundle sends the givens record to the metadataclient. returns `record` for legacy reasons
func (s *Store) WriteBundle(record *settingsmsg.Bundle) (*settingsmsg.Bundle, error) {
s.Init()
ctx := context.TODO()
b, err := json.Marshal(record)
if err != nil {
return nil, err
}
return record, s.mdc.SimpleUpload(ctx, bundlePath(record.Id), b)
}
// AddSettingToBundle adds the given setting to the bundle with the given bundleID.
func (s *Store) AddSettingToBundle(bundleID string, setting *settingsmsg.Setting) (*settingsmsg.Setting, error) {
s.Init()
b, err := s.ReadBundle(bundleID)
if err != nil {
if !errors.Is(err, settings.ErrNotFound) {
return nil, err
}
b = new(settingsmsg.Bundle)
b.Id = bundleID
b.Type = settingsmsg.Bundle_TYPE_DEFAULT
}
if setting.Id == "" {
setting.Id = uuid.NewString()
}
b.Settings = append(b.Settings, setting)
_, err = s.WriteBundle(b)
return setting, err
}
// RemoveSettingFromBundle removes the setting from the bundle with the given ids.
func (s *Store) RemoveSettingFromBundle(bundleID string, settingID string) error {
fmt.Println("RemoveSettingFromBundle not implemented")
return errors.New("not implemented")
}
func bundlePath(id string) string {
return fmt.Sprintf("%s/%s", bundleFolderLocation, id)
}
@@ -0,0 +1,200 @@
package store
import (
"testing"
"github.com/google/uuid"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/stretchr/testify/require"
)
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,
},
},
},
},
},
},
}
var (
appendTestBundleID = uuid.NewString()
appendTestSetting1 = &settingsmsg.Setting{
Id: "append-test-setting-1",
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,
},
},
}
appendTestSetting2 = &settingsmsg.Setting{
Id: "append-test-setting-2",
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 := initStore()
for i := range bundleScenarios {
b := bundleScenarios[i]
t.Run(b.name, func(t *testing.T) {
_, err := s.WriteBundle(b.bundle)
require.NoError(t, err)
bundle, err := s.ReadBundle(b.bundle.Id)
require.NoError(t, err)
require.Equal(t, b.bundle, bundle)
})
}
// check that ListBundles only returns bundles with type DEFAULT
bundles, err := s.ListBundles(settingsmsg.Bundle_TYPE_DEFAULT, []string{})
require.NoError(t, err)
for i := range bundles {
require.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})
require.NoError(t, err)
require.Equal(t, 1, len(filteredBundles))
if len(filteredBundles) == 1 {
require.Equal(t, bundle2, filteredBundles[0].Id)
}
// check that ListRoles only returns bundles with type ROLE
roles, err := s.ListBundles(settingsmsg.Bundle_TYPE_ROLE, []string{})
require.NoError(t, err)
for i := range roles {
require.Equal(t, settingsmsg.Bundle_TYPE_ROLE, roles[i].Type)
}
// check that ReadSetting works
setting, err := s.ReadSetting(setting1)
require.NoError(t, err)
require.Equal(t, "test-desc-1", setting.Description) // could be tested better ;)
}
func TestAppendSetting(t *testing.T) {
s := initStore()
setupRoles(s)
// appending to non existing bundle creates new
_, err := s.AddSettingToBundle(appendTestBundleID, appendTestSetting1)
require.NoError(t, err)
b, err := s.ReadBundle(appendTestBundleID)
require.NoError(t, err)
require.Len(t, b.Settings, 1)
_, err = s.AddSettingToBundle(appendTestBundleID, appendTestSetting2)
require.NoError(t, err)
b, err = s.ReadBundle(appendTestBundleID)
require.NoError(t, err)
require.Len(t, b.Settings, 2)
}
@@ -0,0 +1,170 @@
package store
import (
"context"
"path"
olog "github.com/qsfera/server/pkg/log"
"github.com/qsfera/server/services/settings/pkg/config"
"github.com/opencloud-eu/reva/v2/pkg/store"
"github.com/vmihailenco/msgpack/v5"
microstore "go-micro.dev/v4/store"
)
// CachedMDC is cache for the metadataclient
type CachedMDC struct {
cfg *config.Config
logger olog.Logger
next MetadataClient
filesCache microstore.Store
dirsCache microstore.Store
}
// SimpleDownload caches the answer from SimpleDownload or returns the cached one
func (c *CachedMDC) SimpleDownload(ctx context.Context, id string) ([]byte, error) {
if b, err := c.filesCache.Read(id); err == nil && len(b) == 1 {
return b[0].Value, nil
}
b, err := c.next.SimpleDownload(ctx, id)
if err != nil {
return nil, err
}
err = c.filesCache.Write(&microstore.Record{
Key: id,
Value: b,
Expiry: c.cfg.Metadata.Cache.TTL,
})
if err != nil {
c.logger.Error().Err(err).Msg("SimpleDownload: failed to update to files cache")
}
return b, nil
}
// SimpleUpload caches the answer from SimpleUpload and invalidates the cache
func (c *CachedMDC) SimpleUpload(ctx context.Context, id string, content []byte) error {
b, err := c.filesCache.Read(id)
if err == nil && len(b) == 1 && string(b[0].Value) == string(content) {
// no need to bug mdc
return nil
}
err = c.next.SimpleUpload(ctx, id, content)
if err != nil {
return err
}
// invalidate caches
if err = c.dirsCache.Delete(path.Dir(id)); err != nil {
c.logger.Error().Err(err).Msg("failed to clear dirs cache")
}
err = c.filesCache.Write(&microstore.Record{
Key: id,
Value: content,
Expiry: c.cfg.Metadata.Cache.TTL,
})
if err != nil {
c.logger.Error().Err(err).Msg("SimpleUpload: failed to update to files cache")
}
return nil
}
// Delete invalidates the cache when operation was successful
func (c *CachedMDC) Delete(ctx context.Context, id string) error {
if err := c.next.Delete(ctx, id); err != nil {
return err
}
// invalidate caches
_ = c.removePrefix(c.filesCache, id)
_ = c.removePrefix(c.dirsCache, id)
return nil
}
// ReadDir caches the response from ReadDir or returnes the cached one
func (c *CachedMDC) ReadDir(ctx context.Context, id string) ([]string, error) {
i, err := c.dirsCache.Read(id)
if err == nil && len(i) == 1 {
var ret []string
if err = msgpack.Unmarshal(i[0].Value, &ret); err == nil {
return ret, nil
}
c.logger.Error().Err(err).Msg("failed to unmarshal entry from dirs cache")
}
s, err := c.next.ReadDir(ctx, id)
if err != nil {
return nil, err
}
var value []byte
if value, err = msgpack.Marshal(s); err != nil {
c.logger.Error().Err(err).Msg("failed to marshal ReadDir result for dirs cache")
return s, err
}
err = c.dirsCache.Write(&microstore.Record{
Key: id,
Value: value,
Expiry: c.cfg.Metadata.Cache.TTL,
})
if err != nil {
c.logger.Error().Err(err).Msg("ReadDir: failed to update dirs cache")
}
return s, err
}
// MakeDirIfNotExist invalidates the cache
func (c *CachedMDC) MakeDirIfNotExist(ctx context.Context, id string) error {
err := c.next.MakeDirIfNotExist(ctx, id)
if err != nil {
return err
}
// invalidate caches
if err = c.dirsCache.Delete(path.Dir(id)); err != nil {
c.logger.Error().Err(err).Msg("failed to clear dirs cache")
}
return nil
}
// Init instantiates the caches
func (c *CachedMDC) Init(ctx context.Context, id string) error {
c.dirsCache = store.Create(
store.Store(c.cfg.Metadata.Cache.Store),
store.TTL(c.cfg.Metadata.Cache.TTL),
microstore.Nodes(c.cfg.Metadata.Cache.Nodes...),
microstore.Database(c.cfg.Metadata.Cache.Database),
microstore.Table(c.cfg.Metadata.Cache.DirectoryTable),
store.DisablePersistence(c.cfg.Metadata.Cache.DisablePersistence),
store.Authentication(c.cfg.Metadata.Cache.AuthUsername, c.cfg.Metadata.Cache.AuthPassword),
)
c.filesCache = store.Create(
store.Store(c.cfg.Metadata.Cache.Store),
store.TTL(c.cfg.Metadata.Cache.TTL),
microstore.Nodes(c.cfg.Metadata.Cache.Nodes...),
microstore.Database(c.cfg.Metadata.Cache.Database),
microstore.Table(c.cfg.Metadata.Cache.FileTable),
store.DisablePersistence(c.cfg.Metadata.Cache.DisablePersistence),
store.Authentication(c.cfg.Metadata.Cache.AuthUsername, c.cfg.Metadata.Cache.AuthPassword),
)
return c.next.Init(ctx, id)
}
func (c *CachedMDC) removePrefix(cache microstore.Store, prefix string) error {
c.logger.Debug().Str("prefix", prefix).Msg("removePrefix")
keys, err := cache.List(microstore.ListPrefix(prefix))
if err != nil {
c.logger.Error().Err(err).Msg("failed to list cache entries")
}
for _, k := range keys {
c.logger.Debug().Str("key", k).Msg("removePrefix")
if err := cache.Delete(k); err != nil {
c.logger.Error().Err(err).Msg("failed to remove prefix from cache")
return err
}
}
return nil
}
@@ -0,0 +1,72 @@
package store
import (
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/qsfera/server/services/settings/pkg/settings"
"github.com/qsfera/server/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
}
@@ -0,0 +1,29 @@
package store
import (
"testing"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/stretchr/testify/require"
)
func TestPermission(t *testing.T) {
s := initStore()
setupRoles(s)
// bunldes are initialized within init func
p, err := s.ReadPermissionByID("readID", []string{"f36db5e6-a03c-40df-8413-711c67e40b47"})
require.NoError(t, err)
require.Equal(t, settingsmsg.Permission_OPERATION_READ, p.Operation)
p, err = s.ReadPermissionByName("read", []string{"f36db5e6-a03c-40df-8413-711c67e40b47"})
require.NoError(t, err)
require.Equal(t, settingsmsg.Permission_OPERATION_READ, p.Operation)
pms, err := s.ListPermissionsByResource(&settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_BUNDLE,
}, []string{"f36db5e6-a03c-40df-8413-711c67e40b47"})
require.NoError(t, err)
require.Len(t, pms, 1)
require.Equal(t, settingsmsg.Permission_OPERATION_READ, pms[0].Operation)
}
@@ -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
}
@@ -0,0 +1,190 @@
package store
import (
"context"
"encoding/json"
"strings"
"testing"
. "github.com/onsi/gomega"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/qsfera/server/services/settings/pkg/config/defaults"
rdefaults "github.com/qsfera/server/services/settings/pkg/store/defaults"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
)
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"
)
// use "unit" or "integration" do define test type. You need a running КуСфера instance for integration tests
var testtype = "unit"
// MockedMetadataClient mocks the metadataservice inmemory
type MockedMetadataClient struct {
data map[string][]byte
}
// NewMDC instantiates a mocked MetadataClient
func NewMDC(s *Store) error {
var mdc MetadataClient
switch testtype {
case "unit":
mdc = &MockedMetadataClient{data: make(map[string][]byte)}
case "integration":
mdc = NewMetadataClient(defaults.DefaultConfig().Metadata)
}
return s.initMetadataClient(mdc)
}
// SimpleDownload returns errtypes.NotFound if not found
func (m *MockedMetadataClient) SimpleDownload(_ context.Context, id string) ([]byte, error) {
if data, ok := m.data[id]; ok {
return data, nil
}
return nil, errtypes.NotFound("not found")
}
// SimpleUpload can't error
func (m *MockedMetadataClient) SimpleUpload(_ context.Context, id string, content []byte) error {
m.data[id] = content
return nil
}
// Delete can't error either
func (m *MockedMetadataClient) Delete(_ context.Context, id string) error {
for k := range m.data {
if strings.HasPrefix(k, id) {
delete(m.data, k)
}
}
return nil
}
// ReadDir returns nil, nil if not found
func (m *MockedMetadataClient) ReadDir(_ context.Context, id string) ([]string, error) {
var out []string
for k := range m.data {
if strings.HasPrefix(k, id) {
dir := strings.TrimPrefix(k, id+"/")
// filter subfolders the lame way
s := strings.Trim(strings.SplitAfter(dir, "/")[0], "/")
out = append(out, s)
}
}
return out, nil
}
// MakeDirIfNotExist does nothing
func (*MockedMetadataClient) MakeDirIfNotExist(_ context.Context, _ string) error {
return nil
}
// Init does nothing
func (*MockedMetadataClient) Init(_ context.Context, _ string) error {
return nil
}
// IDExists is a helper to check if an id exists
func (m *MockedMetadataClient) IDExists(id string) bool {
_, ok := m.data[id]
return ok
}
// IDHasContent returns true if the value stored under id has the given content (converted to string)
func (m *MockedMetadataClient) IDHasContent(id string, content []byte) bool {
return string(m.data[id]) == string(content)
}
// TestAdminUserIDInit test the happy path during initialization
func TestAdminUserIDInit(t *testing.T) {
RegisterTestingT(t)
s := &Store{
cfg: defaults.DefaultConfig(),
}
s.cfg.Bundles = rdefaults.GenerateBundlesDefaultRoles()
s.cfg.AdminUserID = "admin"
// the first assignment is always happening during the initialisation of the metadata client
err := NewMDC(s)
Expect(err).To(BeNil())
assID, err := s.mdc.ReadDir(context.TODO(), accountPath(s.cfg.AdminUserID))
Expect(len(assID)).To(Equal(1))
ass, err := s.mdc.SimpleDownload(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]))
Expect(ass).ToNot(BeNil())
assignment := &settingsmsg.UserRoleAssignment{}
err = json.Unmarshal(ass, assignment)
Expect(err).To(BeNil())
Expect(assignment.RoleId).To(Equal(rdefaults.BundleUUIDRoleAdmin))
}
// TestAdminUserIDUpdate test the update on following initialisations
func TestAdminUserIDUpdate(t *testing.T) {
RegisterTestingT(t)
s := &Store{
cfg: defaults.DefaultConfig(),
}
s.cfg.Bundles = rdefaults.GenerateBundlesDefaultRoles()
s.cfg.AdminUserID = "admin"
// the first assignment is always happening during the initialisation of the metadata client
err := NewMDC(s)
Expect(err).To(BeNil())
// read assignment
assID, err := s.mdc.ReadDir(context.TODO(), accountPath(s.cfg.AdminUserID))
Expect(len(assID)).To(Equal(1))
// set assignment to user role
userRoleAssignment := &settingsmsg.UserRoleAssignment{
AccountUuid: s.cfg.AdminUserID,
RoleId: rdefaults.BundleUUIDRoleUser,
}
b, err := json.Marshal(userRoleAssignment)
err = s.mdc.Delete(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]))
Expect(err).To(BeNil())
err = s.mdc.SimpleUpload(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]), b)
Expect(err).To(BeNil())
// this happens on every Read / Write on the store
// the actual init is only done if the metadata client has not been initialized before
// this normally needs a restart of the service
err = s.initMetadataClient(s.mdc)
Expect(err).To(BeNil())
// read assignment id, changes every time the assignment is written
assID, err = s.mdc.ReadDir(context.TODO(), accountPath(s.cfg.AdminUserID))
Expect(len(assID)).To(Equal(1))
// check if the assignment is the admin role again
ass, err := s.mdc.SimpleDownload(context.TODO(), assignmentPath(s.cfg.AdminUserID, assID[0]))
Expect(ass).ToNot(BeNil())
assignment := &settingsmsg.UserRoleAssignment{}
err = json.Unmarshal(ass, assignment)
Expect(err).To(BeNil())
Expect(assignment.RoleId).To(Equal(rdefaults.BundleUUIDRoleAdmin))
}
@@ -0,0 +1,141 @@
// Package store implements the go-micro store interface
package store
import (
"context"
"encoding/json"
"fmt"
"github.com/google/uuid"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/qsfera/server/services/settings/pkg/settings"
"github.com/opencloud-eu/reva/v2/pkg/errtypes"
)
// 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) {
s.Init()
ctx := context.TODO()
vIDs, err := s.mdc.ReadDir(ctx, valuesFolderLocation)
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return make([]*settingsmsg.Value, 0), nil
default:
return nil, err
}
// TODO: refine logic not to spam metadata service
var values []*settingsmsg.Value
for _, vid := range vIDs {
b, err := s.mdc.SimpleDownload(ctx, valuePath(vid))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
v := &settingsmsg.Value{}
err = json.Unmarshal(b, v)
if err != nil {
return nil, err
}
if bundleID != "" && v.BundleId != bundleID {
continue
}
if v.AccountUuid == "" {
values = append(values, v)
continue
}
if v.AccountUuid == accountUUID {
values = append(values, v)
continue
}
}
return values, nil
}
// ReadValue tries to find a value by the given valueId within the dataPath
func (s *Store) ReadValue(valueID string) (*settingsmsg.Value, error) {
s.Init()
ctx := context.TODO()
b, err := s.mdc.SimpleDownload(ctx, valuePath(valueID))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
return nil, fmt.Errorf("valueID '%s' %w", valueID, settings.ErrNotFound)
default:
return nil, err
}
val := &settingsmsg.Value{}
return val, json.Unmarshal(b, val)
}
// ReadValueByUniqueIdentifiers tries to find a value given a set of unique identifiers
func (s *Store) ReadValueByUniqueIdentifiers(accountUUID, settingID string) (*settingsmsg.Value, error) {
if settingID == "" {
return nil, fmt.Errorf("settingID can not be empty %w", settings.ErrNotFound)
}
s.Init()
ctx := context.TODO()
vIDs, err := s.mdc.ReadDir(ctx, valuesFolderLocation)
if err != nil {
return nil, err
}
for _, vid := range vIDs {
b, err := s.mdc.SimpleDownload(ctx, valuePath(vid))
switch err.(type) {
case nil:
// continue
case errtypes.NotFound:
continue
default:
return nil, err
}
v := &settingsmsg.Value{}
err = json.Unmarshal(b, v)
if err != nil {
return nil, err
}
if v.AccountUuid == accountUUID && v.SettingId == settingID {
return v, nil
}
}
return nil, settings.ErrNotFound
}
// WriteValue writes the given value into a file within the dataPath
func (s *Store) WriteValue(value *settingsmsg.Value) (*settingsmsg.Value, error) {
s.Init()
ctx := context.TODO()
if value.Id == "" {
value.Id = uuid.NewString()
}
b, err := json.Marshal(value)
if err != nil {
return nil, err
}
return value, s.mdc.SimpleUpload(ctx, valuePath(value.Id), b)
}
func valuePath(id string) string {
return fmt.Sprintf("%s/%s", valuesFolderLocation, id)
}
@@ -0,0 +1,104 @@
package store
import (
"testing"
settingsmsg "github.com/qsfera/server/protogen/gen/qsfera/messages/settings/v0"
"github.com/stretchr/testify/require"
)
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: bundle2,
SettingId: setting2,
AccountUuid: accountUUID1,
Resource: &settingsmsg.Resource{
Type: settingsmsg.Resource_TYPE_FILE,
Id: "adfba82d-919a-41c3-9cd1-5a3f83b2bf76",
},
Value: &settingsmsg.Value_StringValue{
StringValue: "tralala",
},
},
},
{
name: "value without accountUUID",
value: &settingsmsg.Value{
Id: value3,
BundleId: bundle3,
SettingId: setting2,
AccountUuid: "",
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) {
for i := range valueScenarios {
index := i
t.Run(valueScenarios[index].name, func(t *testing.T) {
s := initStore()
setupRoles(s)
value := valueScenarios[index].value
v, err := s.WriteValue(value)
require.NoError(t, err)
require.Equal(t, value, v)
v, err = s.ReadValue(value.Id)
require.NoError(t, err)
require.Equal(t, value, v)
})
}
}
func TestListValues(t *testing.T) {
s := initStore()
setupRoles(s)
for _, v := range valueScenarios {
_, err := s.WriteValue(v.value)
require.NoError(t, err)
}
// empty accountid returns only values with empty accountud
vs, err := s.ListValues("", "")
require.NoError(t, err)
require.Len(t, vs, 1)
// filled accountid returns matching and empty accountUUID values
vs, err = s.ListValues("", accountUUID1)
require.NoError(t, err)
require.Len(t, vs, 3)
// filled bundleid only returns matching values
vs, err = s.ListValues(bundle3, accountUUID1)
require.NoError(t, err)
require.Len(t, vs, 1)
}
@@ -0,0 +1,6 @@
package store
import (
// init filesystem store
_ "github.com/qsfera/server/services/settings/pkg/store/metadata"
)