mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-06 13:57:41 +00:00
feat: store assets in database (#3290)
* feat: use database as asset storage * being only uploading assets if allowed * tests * fixes * cleanup after merge * renaming * various fixes * fix: change to repository event types and removed unused code * feat: set default features * error handling * error handling and naming * fix tests * fix tests * fix merge * rename
This commit is contained in:
parent
b949b8fc65
commit
4a0d61d75a
@ -2,21 +2,36 @@ package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
command "github.com/caos/zitadel/internal/command/v2"
|
||||
"database/sql"
|
||||
_ "embed"
|
||||
)
|
||||
|
||||
type DefaultInstance struct {
|
||||
cmd *command.Command
|
||||
InstanceSetup command.InstanceSetup
|
||||
const (
|
||||
createAssets = `
|
||||
CREATE TABLE system.assets (
|
||||
instance_id TEXT,
|
||||
asset_type TEXT,
|
||||
resource_owner TEXT,
|
||||
name TEXT,
|
||||
content_type TEXT,
|
||||
hash TEXT AS (md5(data)) STORED,
|
||||
data BYTES,
|
||||
updated_at TIMESTAMPTZ,
|
||||
|
||||
PRIMARY KEY (instance_id, resource_owner, name)
|
||||
);
|
||||
`
|
||||
)
|
||||
|
||||
type AssetTable struct {
|
||||
dbClient *sql.DB
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
_, err := mig.cmd.SetUpInstance(ctx, &mig.InstanceSetup)
|
||||
|
||||
func (mig *AssetTable) Execute(ctx context.Context) error {
|
||||
_, err := mig.dbClient.ExecContext(ctx, createAssets)
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) String() string {
|
||||
return "02_default_instance"
|
||||
func (mig *AssetTable) String() string {
|
||||
return "02_assets"
|
||||
}
|
||||
|
22
cmd/admin/setup/03.go
Normal file
22
cmd/admin/setup/03.go
Normal file
@ -0,0 +1,22 @@
|
||||
package setup
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/command/v2"
|
||||
)
|
||||
|
||||
type DefaultInstance struct {
|
||||
cmd *command.Command
|
||||
InstanceSetup command.InstanceSetup
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) Execute(ctx context.Context) error {
|
||||
_, err := mig.cmd.SetUpInstance(ctx, &mig.InstanceSetup)
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (mig *DefaultInstance) String() string {
|
||||
return "03_default_instance"
|
||||
}
|
@ -35,8 +35,9 @@ func MustNewConfig(v *viper.Viper) *Config {
|
||||
}
|
||||
|
||||
type Steps struct {
|
||||
S1ProjectionTable *ProjectionTable
|
||||
S2DefaultInstance *DefaultInstance
|
||||
s1ProjectionTable *ProjectionTable
|
||||
s2AssetsTable *AssetTable
|
||||
S3DefaultInstance *DefaultInstance
|
||||
}
|
||||
|
||||
func MustNewSteps(v *viper.Viper) *Steps {
|
||||
|
@ -9,7 +9,7 @@ import (
|
||||
"github.com/spf13/viper"
|
||||
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
command "github.com/caos/zitadel/internal/command/v2"
|
||||
"github.com/caos/zitadel/internal/command/v2"
|
||||
"github.com/caos/zitadel/internal/database"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/migration"
|
||||
@ -46,12 +46,14 @@ func Setup(config *Config, steps *Steps) {
|
||||
|
||||
cmd := command.New(eventstoreClient, "localhost", config.SystemDefaults)
|
||||
|
||||
steps.S2DefaultInstance.cmd = cmd
|
||||
steps.S1ProjectionTable = &ProjectionTable{dbClient: dbClient}
|
||||
steps.S2DefaultInstance.InstanceSetup.Zitadel.IsDevMode = !config.ExternalSecure
|
||||
steps.S2DefaultInstance.InstanceSetup.Zitadel.BaseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
steps.s1ProjectionTable = &ProjectionTable{dbClient: dbClient}
|
||||
steps.s2AssetsTable = &AssetTable{dbClient: dbClient}
|
||||
steps.S3DefaultInstance.cmd = cmd
|
||||
steps.S3DefaultInstance.InstanceSetup.Zitadel.IsDevMode = !config.ExternalSecure
|
||||
steps.S3DefaultInstance.InstanceSetup.Zitadel.BaseURL = http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
|
||||
ctx := context.Background()
|
||||
migration.Migrate(ctx, eventstoreClient, steps.S1ProjectionTable)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.S2DefaultInstance)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.s1ProjectionTable)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.s2AssetsTable)
|
||||
migration.Migrate(ctx, eventstoreClient, steps.S3DefaultInstance)
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
S2DefaultInstance:
|
||||
S3DefaultInstance:
|
||||
InstanceSetup:
|
||||
Org:
|
||||
Name: ZITADEL
|
||||
@ -13,6 +13,29 @@ S2DefaultInstance:
|
||||
Gender:
|
||||
Phone:
|
||||
Password: Password1!
|
||||
Features:
|
||||
TierName: Default Tier
|
||||
TierDescription: ""
|
||||
State: 1 #active
|
||||
StateDescription: ""
|
||||
Retention: 8760h #1year
|
||||
LoginPolicyFactors: true
|
||||
LoginPolicyIDP: true
|
||||
LoginPolicyPasswordless: true
|
||||
LoginPolicyRegistration: true
|
||||
LoginPolicyUsernameLogin: true
|
||||
LoginPolicyPasswordReset: true
|
||||
PasswordComplexityPolicy: true
|
||||
LabelPolicyPrivateLabel: true
|
||||
LabelPolicyWatermark: true
|
||||
CustomDomain: true
|
||||
PrivacyPolicy: true
|
||||
MetadataUser: true
|
||||
CustomTextMessage: true
|
||||
CustomTextLogin: true
|
||||
LockoutPolicy: true
|
||||
ActionsAllowed: 2 #ActionsAllowedUnlimited
|
||||
MaxActions: #not necessary because of ActionsAllowedUnlimited
|
||||
PasswordComplexityPolicy:
|
||||
MinLength: 8
|
||||
HasLowercase: true
|
||||
|
@ -94,12 +94,6 @@ func startZitadel(config *Config, masterKey string) error {
|
||||
return err
|
||||
}
|
||||
|
||||
var storage static.Storage
|
||||
//TODO: enable when storage is implemented again
|
||||
//if *assetsEnabled {
|
||||
//storage, err = config.AssetStorage.Config.NewStorage()
|
||||
//logging.Log("MAIN-Bfhe2").OnError(err).Fatal("Unable to start asset storage")
|
||||
//}
|
||||
eventstoreClient, err := eventstore.Start(dbClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start eventstore for queries: %w", err)
|
||||
@ -114,6 +108,11 @@ func startZitadel(config *Config, masterKey string) error {
|
||||
if err != nil {
|
||||
return fmt.Errorf("error starting authz repo: %w", err)
|
||||
}
|
||||
|
||||
storage, err := config.AssetStorage.NewStorage(dbClient)
|
||||
if err != nil {
|
||||
return fmt.Errorf("cannot start asset storage client: %w", err)
|
||||
}
|
||||
webAuthNConfig := webauthn.Config{
|
||||
ID: config.ExternalDomain,
|
||||
Origin: http_util.BuildHTTP(config.ExternalDomain, config.ExternalPort, config.ExternalSecure),
|
||||
@ -163,13 +162,13 @@ func startAPIs(ctx context.Context, router *mux.Router, commands *command.Comman
|
||||
return err
|
||||
}
|
||||
|
||||
apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator, store, queries))
|
||||
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader)
|
||||
apis.RegisterHandler(assets.HandlerPrefix, assets.NewHandler(commands, verifier, config.InternalAuthZ, id.SonyFlakeGenerator, store, queries, instanceInterceptor.Handler))
|
||||
|
||||
userAgentInterceptor, err := middleware.NewUserAgentHandler(config.UserAgentCookie, keys.UserAgentCookieKey, config.ExternalDomain, id.SonyFlakeGenerator, config.ExternalSecure)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
instanceInterceptor := middleware.InstanceInterceptor(queries, config.HTTP1HostHeader)
|
||||
|
||||
issuer := oidc.Issuer(config.ExternalDomain, config.ExternalPort, config.ExternalSecure)
|
||||
oidcProvider, err := oidc.NewProvider(ctx, config.OIDC, issuer, login.DefaultLoggedOutPath, commands, queries, authRepo, config.SystemDefaults.KeyConfig, keys.OIDC, keys.OIDCKey, eventstore, dbClient, keyChan, userAgentInterceptor, instanceInterceptor.Handler)
|
||||
|
@ -163,7 +163,7 @@ func (m *Styling) generateStylingFile(policy *iam_model.LabelPolicyView) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return m.uploadFilesToBucket(policy.AggregateID, "text/css", reader, size)
|
||||
return m.uploadFilesToStorage(policy.InstanceID, policy.AggregateID, "text/css", reader, size)
|
||||
}
|
||||
|
||||
func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64, error) {
|
||||
@ -245,9 +245,10 @@ const fontFaceTemplate = `
|
||||
}
|
||||
`
|
||||
|
||||
func (m *Styling) uploadFilesToBucket(aggregateID, contentType string, reader io.Reader, size int64) error {
|
||||
func (m *Styling) uploadFilesToStorage(instanceID, aggregateID, contentType string, reader io.Reader, size int64) error {
|
||||
fileName := domain.CssPath + "/" + domain.CssVariablesFileName
|
||||
_, err := m.static.PutObject(context.Background(), aggregateID, fileName, contentType, reader, size, true)
|
||||
//TODO: handle location as soon as possible
|
||||
_, err := m.static.PutObject(context.Background(), instanceID, "", aggregateID, fileName, contentType, static.ObjectTypeStyling, reader, size)
|
||||
return err
|
||||
}
|
||||
|
||||
|
@ -1,24 +1,21 @@
|
||||
package assets
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
sentryhttp "github.com/getsentry/sentry-go/http"
|
||||
"github.com/gorilla/mux"
|
||||
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
http_util "github.com/caos/zitadel/internal/api/http"
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
@ -54,26 +51,27 @@ func (h *Handler) Storage() static.Storage {
|
||||
}
|
||||
|
||||
type Uploader interface {
|
||||
Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error
|
||||
UploadAsset(ctx context.Context, info string, asset *command.AssetUpload, commands *command.Commands) error
|
||||
ObjectName(data authz.CtxData) (string, error)
|
||||
BucketName(data authz.CtxData) string
|
||||
ResourceOwner(instance authz.Instance, data authz.CtxData) string
|
||||
ContentTypeAllowed(contentType string) bool
|
||||
MaxFileSize() int64
|
||||
ObjectType() static.ObjectType
|
||||
}
|
||||
|
||||
type Downloader interface {
|
||||
ObjectName(ctx context.Context, path string) (string, error)
|
||||
BucketName(ctx context.Context, id string) string
|
||||
ResourceOwner(ctx context.Context, ownerPath string) string
|
||||
}
|
||||
|
||||
type ErrorHandler func(http.ResponseWriter, *http.Request, error, int)
|
||||
|
||||
func DefaultErrorHandler(w http.ResponseWriter, r *http.Request, err error, code int) {
|
||||
logging.Log("ASSET-g5ef1").WithError(err).WithField("uri", r.RequestURI).Error("error occurred on asset api")
|
||||
logging.WithFields("uri", r.RequestURI).WithError(err).Warn("error occurred on asset api")
|
||||
http.Error(w, err.Error(), code)
|
||||
}
|
||||
|
||||
func NewHandler(commands *command.Commands, verifier *authz.TokenVerifier, authConfig authz.Config, idGenerator id.Generator, storage static.Storage, queries *query.Queries) http.Handler {
|
||||
func NewHandler(commands *command.Commands, verifier *authz.TokenVerifier, authConfig authz.Config, idGenerator id.Generator, storage static.Storage, queries *query.Queries, instanceInterceptor func(handler http.Handler) http.Handler) http.Handler {
|
||||
h := &Handler{
|
||||
commands: commands,
|
||||
errorHandler: DefaultErrorHandler,
|
||||
@ -85,9 +83,9 @@ func NewHandler(commands *command.Commands, verifier *authz.TokenVerifier, authC
|
||||
|
||||
verifier.RegisterServer("Management-API", "assets", AssetsService_AuthMethods) //TODO: separate api?
|
||||
router := mux.NewRouter()
|
||||
router.Use(sentryhttp.New(sentryhttp.Options{}).Handle)
|
||||
router.Use(sentryhttp.New(sentryhttp.Options{}).Handle, instanceInterceptor)
|
||||
RegisterRoutes(router, h)
|
||||
router.PathPrefix("/{id}").Methods("GET").HandlerFunc(DownloadHandleFunc(h, h.GetFile()))
|
||||
router.PathPrefix("/{owner}").Methods("GET").HandlerFunc(DownloadHandleFunc(h, h.GetFile()))
|
||||
return router
|
||||
}
|
||||
|
||||
@ -101,8 +99,8 @@ func (l *publicFileDownloader) ObjectName(_ context.Context, path string) (strin
|
||||
return path, nil
|
||||
}
|
||||
|
||||
func (l *publicFileDownloader) BucketName(_ context.Context, id string) string {
|
||||
return id
|
||||
func (l *publicFileDownloader) ResourceOwner(_ context.Context, ownerPath string) string {
|
||||
return ownerPath
|
||||
}
|
||||
|
||||
const maxMemory = 2 << 20
|
||||
@ -120,7 +118,7 @@ func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWrit
|
||||
}
|
||||
defer func() {
|
||||
err = file.Close()
|
||||
logging.Log("UPLOAD-GDg34").OnError(err).Warn("could not close file")
|
||||
logging.OnError(err).Warn("could not close file")
|
||||
}()
|
||||
contentType := handler.Header.Get("content-type")
|
||||
size := handler.Size
|
||||
@ -133,24 +131,21 @@ func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWrit
|
||||
return
|
||||
}
|
||||
|
||||
bucketName := uploader.BucketName(ctxData)
|
||||
resourceOwner := uploader.ResourceOwner(authz.GetInstance(ctx), ctxData)
|
||||
objectName, err := uploader.ObjectName(ctxData)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
cleanedFile, cleanedSize, err := removeExif(file, size, contentType)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("remove exif error: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
uploadInfo := &command.AssetUpload{
|
||||
ResourceOwner: resourceOwner,
|
||||
ObjectName: objectName,
|
||||
ContentType: contentType,
|
||||
ObjectType: uploader.ObjectType(),
|
||||
File: file,
|
||||
Size: size,
|
||||
}
|
||||
|
||||
info, err := s.Commands().UploadAsset(ctx, bucketName, objectName, contentType, cleanedFile, cleanedSize)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
err = uploader.Callback(ctx, info, ctxData.OrgID, s.Commands())
|
||||
err = uploader.UploadAsset(ctx, ctxData.OrgID, uploadInfo, s.Commands())
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("upload failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
@ -164,11 +159,11 @@ func DownloadHandleFunc(s AssetsService, downloader Downloader) func(http.Respon
|
||||
return
|
||||
}
|
||||
ctx := r.Context()
|
||||
id := mux.Vars(r)["id"]
|
||||
bucketName := downloader.BucketName(ctx, id)
|
||||
ownerPath := mux.Vars(r)["owner"]
|
||||
resourceOwner := downloader.ResourceOwner(ctx, ownerPath)
|
||||
path := ""
|
||||
if id != "" {
|
||||
path = strings.Split(r.RequestURI, id+"/")[1]
|
||||
if ownerPath != "" {
|
||||
path = strings.Split(r.RequestURI, ownerPath+"/")[1]
|
||||
}
|
||||
objectName, err := downloader.ObjectName(ctx, path)
|
||||
if err != nil {
|
||||
@ -176,49 +171,33 @@ func DownloadHandleFunc(s AssetsService, downloader Downloader) func(http.Respon
|
||||
return
|
||||
}
|
||||
if objectName == "" {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("file not found: %v", objectName), http.StatusNotFound)
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("file not found: %v", path), http.StatusNotFound)
|
||||
return
|
||||
}
|
||||
reader, getInfo, err := s.Storage().GetObject(ctx, bucketName, objectName)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("download failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
if err = GetAsset(w, r, resourceOwner, objectName, s.Storage()); err != nil {
|
||||
s.ErrorHandler()(w, r, err, http.StatusInternalServerError)
|
||||
}
|
||||
data, err := ioutil.ReadAll(reader)
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("download failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
info, err := getInfo()
|
||||
if err != nil {
|
||||
s.ErrorHandler()(w, r, fmt.Errorf("download failed: %v", err), http.StatusInternalServerError)
|
||||
return
|
||||
}
|
||||
w.Header().Set("content-length", strconv.FormatInt(info.Size, 10))
|
||||
w.Header().Set("content-type", info.ContentType)
|
||||
w.Header().Set("ETag", info.ETag)
|
||||
w.Write(data)
|
||||
}
|
||||
}
|
||||
|
||||
func removeExif(file io.Reader, size int64, contentType string) (io.Reader, int64, error) {
|
||||
if !isAllowedContentType(contentType) {
|
||||
return file, size, nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := buf.ReadFrom(file)
|
||||
func GetAsset(w http.ResponseWriter, r *http.Request, resourceOwner, objectName string, storage static.Storage) error {
|
||||
data, getInfo, err := storage.GetObject(r.Context(), authz.GetInstance(r.Context()).InstanceID(), resourceOwner, objectName)
|
||||
if err != nil {
|
||||
return file, 0, err
|
||||
return fmt.Errorf("download failed: %v", err)
|
||||
}
|
||||
data, err := exifremove.Remove(buf.Bytes())
|
||||
info, err := getInfo()
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
return fmt.Errorf("download failed: %v", err)
|
||||
}
|
||||
return bytes.NewReader(data), int64(len(data)), nil
|
||||
}
|
||||
|
||||
func isAllowedContentType(contentType string) bool {
|
||||
return strings.HasSuffix(contentType, "png") ||
|
||||
strings.HasSuffix(contentType, "jpg") ||
|
||||
strings.HasSuffix(contentType, "jpeg")
|
||||
if info.Hash == r.Header.Get(http_util.IfNoneMatch) {
|
||||
w.WriteHeader(304)
|
||||
return nil
|
||||
}
|
||||
w.Header().Set(http_util.ContentLength, strconv.FormatInt(info.Size, 10))
|
||||
w.Header().Set(http_util.ContentType, info.ContentType)
|
||||
w.Header().Set(http_util.LastModified, info.LastModified.Format(time.RFC1123))
|
||||
w.Header().Set(http_util.Etag, info.Hash)
|
||||
_, err = w.Write(data)
|
||||
logging.New().OnError(err).Error("error writing response for asset")
|
||||
return nil
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
Services:
|
||||
IAM:
|
||||
Prefix: "/iam"
|
||||
Prefix: "/instance"
|
||||
Methods:
|
||||
DefaultLabelPolicyLogo:
|
||||
Path: "/policy/label/logo"
|
||||
@ -116,4 +116,4 @@ Services:
|
||||
- Name: Get
|
||||
Comment:
|
||||
Type: download
|
||||
Permission: authenticated
|
||||
Permission: authenticated
|
||||
|
@ -9,6 +9,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyLogo() Uploader {
|
||||
@ -44,6 +45,10 @@ func (l *labelPolicyLogoUploader) ContentTypeAllowed(contentType string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) ObjectType() static.ObjectType {
|
||||
return static.ObjectTypeStyling
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
@ -60,27 +65,27 @@ func (l *labelPolicyLogoUploader) ObjectName(_ authz.CtxData) (string, error) {
|
||||
return prefix + "-" + suffixID, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) BucketName(ctxData authz.CtxData) string {
|
||||
func (l *labelPolicyLogoUploader) ResourceOwner(instance authz.Instance, ctxData authz.CtxData) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
return instance.InstanceID()
|
||||
}
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
func (l *labelPolicyLogoUploader) UploadAsset(ctx context.Context, orgID string, upload *command.AssetUpload, commands *command.Commands) error {
|
||||
if l.defaultPolicy {
|
||||
if l.darkMode {
|
||||
_, err := commands.AddLogoDarkDefaultLabelPolicy(ctx, info.Key)
|
||||
_, err := commands.AddLogoDarkDefaultLabelPolicy(ctx, upload)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddLogoDefaultLabelPolicy(ctx, info.Key)
|
||||
_, err := commands.AddLogoDefaultLabelPolicy(ctx, upload)
|
||||
return err
|
||||
}
|
||||
if l.darkMode {
|
||||
_, err := commands.AddLogoDarkLabelPolicy(ctx, orgID, info.Key)
|
||||
_, err := commands.AddLogoDarkLabelPolicy(ctx, orgID, upload)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddLogoLabelPolicy(ctx, orgID, info.Key)
|
||||
_, err := commands.AddLogoLabelPolicy(ctx, orgID, upload)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -134,8 +139,8 @@ func (l *labelPolicyLogoDownloader) ObjectName(ctx context.Context, path string)
|
||||
return policy.Light.LogoURL, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyLogoDownloader) BucketName(ctx context.Context, id string) string {
|
||||
return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.query)
|
||||
func (l *labelPolicyLogoDownloader) ResourceOwner(ctx context.Context, _ string) string {
|
||||
return getLabelPolicyResourceOwner(ctx, l.defaultPolicy, l.preview, l.query)
|
||||
}
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyIcon() Uploader {
|
||||
@ -171,6 +176,10 @@ func (l *labelPolicyIconUploader) ContentTypeAllowed(contentType string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) ObjectType() static.ObjectType {
|
||||
return static.ObjectTypeStyling
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
@ -187,28 +196,28 @@ func (l *labelPolicyIconUploader) ObjectName(_ authz.CtxData) (string, error) {
|
||||
return prefix + "-" + suffixID, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) BucketName(ctxData authz.CtxData) string {
|
||||
func (l *labelPolicyIconUploader) ResourceOwner(instance authz.Instance, ctxData authz.CtxData) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
return instance.InstanceID()
|
||||
}
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
func (l *labelPolicyIconUploader) UploadAsset(ctx context.Context, orgID string, upload *command.AssetUpload, commands *command.Commands) error {
|
||||
if l.defaultPolicy {
|
||||
if l.darkMode {
|
||||
_, err := commands.AddIconDarkDefaultLabelPolicy(ctx, info.Key)
|
||||
_, err := commands.AddIconDarkDefaultLabelPolicy(ctx, upload)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddIconDefaultLabelPolicy(ctx, info.Key)
|
||||
_, err := commands.AddIconDefaultLabelPolicy(ctx, upload)
|
||||
return err
|
||||
}
|
||||
|
||||
if l.darkMode {
|
||||
_, err := commands.AddIconDarkLabelPolicy(ctx, orgID, info.Key)
|
||||
_, err := commands.AddIconDarkLabelPolicy(ctx, orgID, upload)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddIconLabelPolicy(ctx, orgID, info.Key)
|
||||
_, err := commands.AddIconLabelPolicy(ctx, orgID, upload)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -262,8 +271,8 @@ func (l *labelPolicyIconDownloader) ObjectName(ctx context.Context, path string)
|
||||
return policy.Light.IconURL, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyIconDownloader) BucketName(ctx context.Context, id string) string {
|
||||
return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.query)
|
||||
func (l *labelPolicyIconDownloader) ResourceOwner(ctx context.Context, _ string) string {
|
||||
return getLabelPolicyResourceOwner(ctx, l.defaultPolicy, l.preview, l.query)
|
||||
}
|
||||
|
||||
func (h *Handler) UploadDefaultLabelPolicyFont() Uploader {
|
||||
@ -290,6 +299,10 @@ func (l *labelPolicyFontUploader) ContentTypeAllowed(contentType string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) ObjectType() static.ObjectType {
|
||||
return static.ObjectTypeStyling
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
@ -303,19 +316,19 @@ func (l *labelPolicyFontUploader) ObjectName(_ authz.CtxData) (string, error) {
|
||||
return prefix + "-" + suffixID, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) BucketName(ctxData authz.CtxData) string {
|
||||
func (l *labelPolicyFontUploader) ResourceOwner(instance authz.Instance, ctxData authz.CtxData) string {
|
||||
if l.defaultPolicy {
|
||||
return domain.IAMID
|
||||
return instance.InstanceID()
|
||||
}
|
||||
return ctxData.OrgID
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
func (l *labelPolicyFontUploader) UploadAsset(ctx context.Context, orgID string, upload *command.AssetUpload, commands *command.Commands) error {
|
||||
if l.defaultPolicy {
|
||||
_, err := commands.AddFontDefaultLabelPolicy(ctx, info.Key)
|
||||
_, err := commands.AddFontDefaultLabelPolicy(ctx, upload)
|
||||
return err
|
||||
}
|
||||
_, err := commands.AddFontLabelPolicy(ctx, orgID, info.Key)
|
||||
_, err := commands.AddFontLabelPolicy(ctx, orgID, upload)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -349,8 +362,8 @@ func (l *labelPolicyFontDownloader) ObjectName(ctx context.Context, path string)
|
||||
return policy.FontURL, nil
|
||||
}
|
||||
|
||||
func (l *labelPolicyFontDownloader) BucketName(ctx context.Context, id string) string {
|
||||
return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.query)
|
||||
func (l *labelPolicyFontDownloader) ResourceOwner(ctx context.Context, _ string) string {
|
||||
return getLabelPolicyResourceOwner(ctx, l.defaultPolicy, l.preview, l.query)
|
||||
}
|
||||
|
||||
func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, queries *query.Queries) (*query.LabelPolicy, error) {
|
||||
@ -366,16 +379,16 @@ func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, queries *q
|
||||
return queries.ActiveLabelPolicyByOrg(ctx, authz.GetCtxData(ctx).OrgID)
|
||||
}
|
||||
|
||||
func getLabelPolicyBucketName(ctx context.Context, defaultPolicy, preview bool, queries *query.Queries) string {
|
||||
func getLabelPolicyResourceOwner(ctx context.Context, defaultPolicy, preview bool, queries *query.Queries) string {
|
||||
if defaultPolicy {
|
||||
return domain.IAMID
|
||||
return authz.GetInstance(ctx).InstanceID()
|
||||
}
|
||||
policy, err := getLabelPolicy(ctx, defaultPolicy, preview, queries)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
if policy.IsDefault {
|
||||
return domain.IAMID
|
||||
return authz.GetInstance(ctx).InstanceID()
|
||||
}
|
||||
return authz.GetCtxData(ctx).OrgID
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
func (h *Handler) UploadMyUserAvatar() Uploader {
|
||||
@ -27,6 +28,10 @@ func (l *myHumanAvatarUploader) ContentTypeAllowed(contentType string) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) ObjectType() static.ObjectType {
|
||||
return static.ObjectTypeUserAvatar
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) MaxFileSize() int64 {
|
||||
return l.maxSize
|
||||
}
|
||||
@ -35,12 +40,12 @@ func (l *myHumanAvatarUploader) ObjectName(ctxData authz.CtxData) (string, error
|
||||
return domain.GetHumanAvatarAssetPath(ctxData.UserID), nil
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) BucketName(ctxData authz.CtxData) string {
|
||||
return ctxData.OrgID
|
||||
func (l *myHumanAvatarUploader) ResourceOwner(_ authz.Instance, ctxData authz.CtxData) string {
|
||||
return ctxData.ResourceOwner
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarUploader) Callback(ctx context.Context, info *domain.AssetInfo, orgID string, commands *command.Commands) error {
|
||||
_, err := commands.AddHumanAvatar(ctx, orgID, authz.GetCtxData(ctx).UserID, info.Key)
|
||||
func (l *myHumanAvatarUploader) UploadAsset(ctx context.Context, orgID string, upload *command.AssetUpload, commands *command.Commands) error {
|
||||
_, err := commands.AddHumanAvatar(ctx, orgID, authz.GetCtxData(ctx).UserID, upload)
|
||||
return err
|
||||
}
|
||||
|
||||
@ -54,6 +59,6 @@ func (l *myHumanAvatarDownloader) ObjectName(ctx context.Context, path string) (
|
||||
return domain.GetHumanAvatarAssetPath(authz.GetCtxData(ctx).UserID), nil
|
||||
}
|
||||
|
||||
func (l *myHumanAvatarDownloader) BucketName(ctx context.Context, id string) string {
|
||||
return authz.GetCtxData(ctx).OrgID
|
||||
func (l *myHumanAvatarDownloader) ResourceOwner(ctx context.Context, _ string) string {
|
||||
return authz.GetCtxData(ctx).ResourceOwner
|
||||
}
|
||||
|
@ -22,6 +22,9 @@ const (
|
||||
ForwardedFor = "x-forwarded-for"
|
||||
XUserAgent = "x-user-agent"
|
||||
XGrpcWeb = "x-grpc-web"
|
||||
IfNoneMatch = "If-None-Match"
|
||||
LastModified = "Last-Modified"
|
||||
Etag = "Etag"
|
||||
|
||||
ContentSecurityPolicy = "content-security-policy"
|
||||
XXSSProtection = "x-xss-protection"
|
||||
|
@ -1,11 +1,12 @@
|
||||
package login
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/assets"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type dynamicResourceData struct {
|
||||
@ -25,74 +26,11 @@ func (l *Login) handleDynamicResources(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
bucketName := authz.GetInstance(r.Context()).InstanceID()
|
||||
resourceOwner := authz.GetInstance(r.Context()).InstanceID()
|
||||
if data.OrgID != "" && !data.DefaultPolicy {
|
||||
bucketName = data.OrgID
|
||||
resourceOwner = data.OrgID
|
||||
}
|
||||
|
||||
etag := r.Header.Get("If-None-Match")
|
||||
asset, info, err := l.getStatic(r.Context(), bucketName, data.FileName)
|
||||
if info != nil && info.ETag == etag {
|
||||
w.WriteHeader(304)
|
||||
return
|
||||
}
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
//TODO: enable again when assets are implemented again
|
||||
_ = asset
|
||||
//w.Header().Set("content-length", strconv.FormatInt(info.Size, 10))
|
||||
//w.Header().Set("content-type", info.ContentType)
|
||||
//w.Header().Set("ETag", info.ETag)
|
||||
//w.Write(asset)
|
||||
}
|
||||
|
||||
func (l *Login) getStatic(ctx context.Context, bucketName, fileName string) ([]byte, *domain.AssetInfo, error) {
|
||||
s := new(staticAsset)
|
||||
//TODO: enable again when assets are implemented again
|
||||
//key := bucketName + "-" + fileName
|
||||
//err := l.staticCache.Get(key, s)
|
||||
//if err == nil && s.Info != nil && (s.Info.Expiration.After(time.Now().Add(-1 * time.Minute))) { //TODO: config?
|
||||
// return s.Data, s.Info, nil
|
||||
//}
|
||||
|
||||
//info, err := l.staticStorage.GetObjectInfo(ctx, bucketName, fileName)
|
||||
//if err != nil {
|
||||
// if caos_errs.IsNotFound(err) {
|
||||
// return nil, nil, err
|
||||
// }
|
||||
// return s.Data, s.Info, err
|
||||
//}
|
||||
//if s.Info != nil && s.Info.ETag == info.ETag {
|
||||
// if info.Expiration.After(s.Info.Expiration) {
|
||||
// s.Info = info
|
||||
// //l.cacheStatic(bucketName, fileName, s)
|
||||
// }
|
||||
// return s.Data, s.Info, nil
|
||||
//}
|
||||
//
|
||||
//reader, _, err := l.staticStorage.GetObject(ctx, bucketName, fileName)
|
||||
//if err != nil {
|
||||
// return s.Data, s.Info, err
|
||||
//}
|
||||
//s.Data, err = ioutil.ReadAll(reader)
|
||||
//if err != nil {
|
||||
// return nil, nil, err
|
||||
//}
|
||||
//s.Info = info
|
||||
//l.cacheStatic(bucketName, fileName, s)
|
||||
return s.Data, s.Info, nil
|
||||
}
|
||||
|
||||
//TODO: enable again when assets are implemented again
|
||||
//
|
||||
//func (l *Login) cacheStatic(bucketName, fileName string, s *staticAsset) {
|
||||
// key := bucketName + "-" + fileName
|
||||
// err := l.staticCache.Set(key, &s)
|
||||
// logging.Log("HANDLER-dfht2").OnError(err).Warnf("caching of asset %s: %s failed", bucketName, fileName)
|
||||
//}
|
||||
|
||||
type staticAsset struct {
|
||||
Data []byte
|
||||
Info *domain.AssetInfo
|
||||
err = assets.GetAsset(w, r, resourceOwner, data.FileName, l.staticStorage)
|
||||
logging.WithFields("file", data.FileName, "org", resourceOwner).OnError(err).Warn("asset in login could not be served")
|
||||
}
|
||||
|
@ -124,10 +124,7 @@ func (c *Commands) ActivateDefaultLabelPolicy(ctx context.Context) (*domain.Obje
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddLogoDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-3m20c", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
func (c *Commands) AddLogoDefaultLabelPolicy(ctx context.Context, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -136,8 +133,12 @@ func (c *Commands) AddLogoDefaultLabelPolicy(ctx context.Context, storageKey str
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-Qw0pd", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "INSTANCE-3m20c", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyLogoAddedEvent(ctx, instanceAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyLogoAddedEvent(ctx, instanceAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -158,7 +159,7 @@ func (c *Commands) RemoveLogoDefaultLabelPolicy(ctx context.Context) (*domain.Ob
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-Xc8Kf", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
|
||||
err = c.RemoveAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.LogoKey)
|
||||
err = c.removeAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.LogoKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -174,10 +175,7 @@ func (c *Commands) RemoveLogoDefaultLabelPolicy(ctx context.Context) (*domain.Ob
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddIconDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-yxE4f", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
func (c *Commands) AddIconDefaultLabelPolicy(ctx context.Context, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -186,8 +184,12 @@ func (c *Commands) AddIconDefaultLabelPolicy(ctx context.Context, storageKey str
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-1yMx0", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "INSTANCE-yxE4f", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyIconAddedEvent(ctx, instanceAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyIconAddedEvent(ctx, instanceAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -207,7 +209,7 @@ func (c *Commands) RemoveIconDefaultLabelPolicy(ctx context.Context) (*domain.Ob
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-4M0qw", "Errors.IAM.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.IconKey)
|
||||
err = c.removeAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.IconKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -223,20 +225,21 @@ func (c *Commands) RemoveIconDefaultLabelPolicy(ctx context.Context) (*domain.Ob
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddLogoDarkDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-4fMs9", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
func (c *Commands) AddLogoDarkDefaultLabelPolicy(ctx context.Context, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-ZR9fs", "Errors.IAM.LabelPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-ZR9fs", "Errors.Instance.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "INSTANCE-4fMs9", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyLogoDarkAddedEvent(ctx, instanceAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyLogoDarkAddedEvent(ctx, instanceAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -254,9 +257,9 @@ func (c *Commands) RemoveLogoDarkDefaultLabelPolicy(ctx context.Context) (*domai
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-3FGds", "Errors.IAM.LabelPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-3FGds", "Errors.Instance.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.LogoDarkKey)
|
||||
err = c.removeAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.LogoDarkKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -272,20 +275,21 @@ func (c *Commands) RemoveLogoDarkDefaultLabelPolicy(ctx context.Context) (*domai
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddIconDarkDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-1cxM3", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
func (c *Commands) AddIconDarkDefaultLabelPolicy(ctx context.Context, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-vMsf9", "Errors.IAM.LabelPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-vMsf9", "Errors.Instance.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "INSTANCE-1cxM3", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyIconDarkAddedEvent(ctx, instanceAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyIconDarkAddedEvent(ctx, instanceAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -303,9 +307,9 @@ func (c *Commands) RemoveIconDarkDefaultLabelPolicy(ctx context.Context) (*domai
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-2nc7F", "Errors.IAM.LabelPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-2nc7F", "Errors.Instance.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.IconDarkKey)
|
||||
err = c.removeAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.IconDarkKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -321,20 +325,21 @@ func (c *Commands) RemoveIconDarkDefaultLabelPolicy(ctx context.Context) (*domai
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddFontDefaultLabelPolicy(ctx context.Context, storageKey string) (*domain.ObjectDetails, error) {
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INSTANCE-1N8fs", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
func (c *Commands) AddFontDefaultLabelPolicy(ctx context.Context, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
existingPolicy, err := c.defaultLabelPolicyWriteModelByID(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-1N8fE", "Errors.IAM.LabelPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-1N8fE", "Errors.Instance.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(nil, "INSTANCE-1N8fs", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
instanceAgg := InstanceAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyFontAddedEvent(ctx, instanceAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, instance.NewLabelPolicyFontAddedEvent(ctx, instanceAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -352,9 +357,9 @@ func (c *Commands) RemoveFontDefaultLabelPolicy(ctx context.Context) (*domain.Ob
|
||||
}
|
||||
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-Tk0gw", "Errors.IAM.LabelPolicy.NotFound")
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INSTANCE-Tk0gw", "Errors.Instance.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.FontKey)
|
||||
err = c.removeAsset(ctx, authz.GetInstance(ctx).InstanceID(), existingPolicy.FontKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,13 +1,14 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/golang/mock/gomock"
|
||||
"github.com/stretchr/testify/assert"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
@ -452,8 +453,8 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) {
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -465,20 +466,6 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) {
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "label policy not existing, not found error",
|
||||
fields: fields{
|
||||
@ -488,13 +475,61 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "logo",
|
||||
ContentType: "text/css",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: nil,
|
||||
Size: 0,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logo added, ok",
|
||||
fields: fields{
|
||||
@ -523,16 +558,24 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyLogoAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"key",
|
||||
"logo",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -545,8 +588,9 @@ func TestCommandSide_AddLogoDefaultLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddLogoDefaultLabelPolicy(tt.args.ctx, tt.args.storageKey)
|
||||
got, err := r.AddLogoDefaultLabelPolicy(tt.args.ctx, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -593,7 +637,6 @@ func TestCommandSide_RemoveLogoDefaultLabelPolicy(t *testing.T) {
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
|
||||
{
|
||||
name: "asset remove error, internal error",
|
||||
fields: fields{
|
||||
@ -708,10 +751,11 @@ func TestCommandSide_RemoveLogoDefaultLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -723,20 +767,6 @@ func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) {
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "label policy not existing, not found error",
|
||||
fields: fields{
|
||||
@ -746,13 +776,61 @@ func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "icon added, ok",
|
||||
fields: fields{
|
||||
@ -781,16 +859,24 @@ func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyIconAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"key",
|
||||
"icon",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -803,8 +889,9 @@ func TestCommandSide_AddIconDefaultLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddIconDefaultLabelPolicy(tt.args.ctx, tt.args.storageKey)
|
||||
got, err := r.AddIconDefaultLabelPolicy(tt.args.ctx, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -926,11 +1013,12 @@ func TestCommandSide_RemoveIconDefaultLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
instanceID string
|
||||
storageKey string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -942,21 +1030,6 @@ func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) {
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "label policy not existing, not found error",
|
||||
fields: fields{
|
||||
@ -968,12 +1041,61 @@ func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) {
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
storageKey: "key",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "INSTANCE",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logo dark added, ok",
|
||||
fields: fields{
|
||||
@ -1002,16 +1124,24 @@ func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyLogoDarkAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"key",
|
||||
"logo",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1024,8 +1154,9 @@ func TestCommandSide_AddLogoDarkDefaultLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddLogoDarkDefaultLabelPolicy(tt.args.ctx, tt.args.storageKey)
|
||||
got, err := r.AddLogoDarkDefaultLabelPolicy(tt.args.ctx, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1186,10 +1317,11 @@ func TestCommandSide_RemoveLogoDarkDefaultLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -1201,20 +1333,6 @@ func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) {
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "label policy not existing, not found error",
|
||||
fields: fields{
|
||||
@ -1224,13 +1342,61 @@ func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "icon dark added, ok",
|
||||
fields: fields{
|
||||
@ -1259,16 +1425,24 @@ func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyIconDarkAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"key",
|
||||
"icon",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1281,8 +1455,9 @@ func TestCommandSide_AddIconDarkDefaultLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddIconDarkDefaultLabelPolicy(tt.args.ctx, tt.args.storageKey)
|
||||
got, err := r.AddIconDarkDefaultLabelPolicy(tt.args.ctx, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1443,10 +1618,11 @@ func TestCommandSide_RemoveIconDarkDefaultLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -1458,20 +1634,6 @@ func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) {
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "label policy not existing, not found error",
|
||||
fields: fields{
|
||||
@ -1481,13 +1643,61 @@ func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "font",
|
||||
ContentType: "ttf",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "font",
|
||||
ContentType: "ttf",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "font added, ok",
|
||||
fields: fields{
|
||||
@ -1516,16 +1726,24 @@ func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
instance.NewLabelPolicyFontAddedEvent(context.Background(),
|
||||
&instance.NewAggregate("INSTANCE").Aggregate,
|
||||
"key",
|
||||
"font",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "IAM",
|
||||
ObjectName: "font",
|
||||
ContentType: "ttf",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1538,8 +1756,9 @@ func TestCommandSide_AddFontDefaultLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddFontDefaultLabelPolicy(tt.args.ctx, tt.args.storageKey)
|
||||
got, err := r.AddFontDefaultLabelPolicy(tt.args.ctx, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/repository/org"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
func (c *Commands) AddLabelPolicy(ctx context.Context, resourceOwner string, policy *domain.LabelPolicy) (*domain.LabelPolicy, error) {
|
||||
@ -150,13 +151,10 @@ func (c *Commands) ActivateLabelPolicy(ctx context.Context, orgID string) (*doma
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddLogoLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) AddLogoLabelPolicy(ctx context.Context, orgID string, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-KKd4X", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4N3nf", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -165,8 +163,12 @@ func (c *Commands) AddLogoLabelPolicy(ctx context.Context, orgID, storageKey str
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-23BMs", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "IAM-4N3nf", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyLogoAddedEvent(ctx, orgAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyLogoAddedEvent(ctx, orgAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -189,7 +191,7 @@ func (c *Commands) RemoveLogoLabelPolicy(ctx context.Context, orgID string) (*do
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-4MVsf", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, orgID, existingPolicy.LogoKey)
|
||||
err = c.removeAsset(ctx, orgID, existingPolicy.LogoKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -205,13 +207,10 @@ func (c *Commands) RemoveLogoLabelPolicy(ctx context.Context, orgID string) (*do
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddIconLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) AddIconLabelPolicy(ctx context.Context, orgID string, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-hMDs3", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4BS7f", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -220,8 +219,12 @@ func (c *Commands) AddIconLabelPolicy(ctx context.Context, orgID, storageKey str
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-4nq2f", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "IAM-4BS7f", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyIconAddedEvent(ctx, orgAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyIconAddedEvent(ctx, orgAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -245,7 +248,7 @@ func (c *Commands) RemoveIconLabelPolicy(ctx context.Context, orgID string) (*do
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-1nd9f", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
|
||||
err = c.RemoveAsset(ctx, orgID, existingPolicy.IconKey)
|
||||
err = c.removeAsset(ctx, orgID, existingPolicy.IconKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -261,13 +264,10 @@ func (c *Commands) RemoveIconLabelPolicy(ctx context.Context, orgID string) (*do
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddLogoDarkLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) AddLogoDarkLabelPolicy(ctx context.Context, orgID string, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-67Ms2", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-3S7fN", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -276,8 +276,12 @@ func (c *Commands) AddLogoDarkLabelPolicy(ctx context.Context, orgID, storageKey
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-QSqcd", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "IAM-3S7fN", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyLogoDarkAddedEvent(ctx, orgAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyLogoDarkAddedEvent(ctx, orgAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -300,7 +304,7 @@ func (c *Commands) RemoveLogoDarkLabelPolicy(ctx context.Context, orgID string)
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-0peQw", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, orgID, existingPolicy.LogoDarkKey)
|
||||
err = c.removeAsset(ctx, orgID, existingPolicy.LogoDarkKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -316,13 +320,10 @@ func (c *Commands) RemoveLogoDarkLabelPolicy(ctx context.Context, orgID string)
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddIconDarkLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) AddIconDarkLabelPolicy(ctx context.Context, orgID string, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-tzBfs", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "IAM-4B7cs", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -331,8 +332,12 @@ func (c *Commands) AddIconDarkLabelPolicy(ctx context.Context, orgID, storageKey
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-4Nf8s", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "IAM-4B7cs", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyIconDarkAddedEvent(ctx, orgAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyIconDarkAddedEvent(ctx, orgAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -367,13 +372,10 @@ func (c *Commands) RemoveIconDarkLabelPolicy(ctx context.Context, orgID string)
|
||||
return writeModelToObjectDetails(&existingPolicy.LabelPolicyWriteModel.WriteModel), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddFontLabelPolicy(ctx context.Context, orgID, storageKey string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) AddFontLabelPolicy(ctx context.Context, orgID string, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
if orgID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "Org-1Nf9s", "Errors.ResourceOwnerMissing")
|
||||
}
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-2f9fw", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingPolicy, err := c.orgLabelPolicyWriteModelByID(ctx, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -382,8 +384,12 @@ func (c *Commands) AddFontLabelPolicy(ctx context.Context, orgID, storageKey str
|
||||
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-2M9fs", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "ORG-2f9fw", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyFontAddedEvent(ctx, orgAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, org.NewLabelPolicyFontAddedEvent(ctx, orgAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -447,7 +453,7 @@ func (c *Commands) removeLabelPolicy(ctx context.Context, existingPolicy *OrgLab
|
||||
return nil, caos_errs.ThrowNotFound(nil, "Org-3M9df", "Errors.Org.LabelPolicy.NotFound")
|
||||
}
|
||||
|
||||
err = c.RemoveAssetsFolder(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix+"/", true)
|
||||
err = c.removeAssetsFolder(ctx, existingPolicy.AggregateID, static.ObjectTypeStyling)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -463,7 +469,7 @@ func (c *Commands) removeLabelPolicyIfExists(ctx context.Context, orgID string)
|
||||
if existingPolicy.State != domain.PolicyStateActive {
|
||||
return nil, nil
|
||||
}
|
||||
err = c.RemoveAssetsFolder(ctx, orgID, domain.LabelPolicyPrefix+"/", true)
|
||||
err = c.removeAssetsFolder(ctx, orgID, static.ObjectTypeStyling)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -472,7 +478,7 @@ func (c *Commands) removeLabelPolicyIfExists(ctx context.Context, orgID string)
|
||||
}
|
||||
|
||||
func (c *Commands) removeLabelPolicyAssets(ctx context.Context, existingPolicy *OrgLabelPolicyWriteModel) (*org.LabelPolicyAssetsRemovedEvent, error) {
|
||||
err := c.RemoveAssetsFolder(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix+"/", true)
|
||||
err := c.removeAssetsFolder(ctx, existingPolicy.AggregateID, static.ObjectTypeStyling)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
@ -782,9 +783,9 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) {
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -803,24 +804,17 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) {
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
orgID: "",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@ -835,14 +829,63 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logo added, ok",
|
||||
fields: fields{
|
||||
@ -871,17 +914,25 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyLogoAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"key",
|
||||
"logo",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -896,7 +947,7 @@ func TestCommandSide_AddLogoLabelPolicy(t *testing.T) {
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddLogoLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.storageKey)
|
||||
got, err := r.AddLogoLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1039,11 +1090,12 @@ func TestCommandSide_RemoveLogoLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddIconLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -1062,24 +1114,17 @@ func TestCommandSide_AddIconLabelPolicy(t *testing.T) {
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
orgID: "",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@ -1094,14 +1139,63 @@ func TestCommandSide_AddIconLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "icon added, ok",
|
||||
fields: fields{
|
||||
@ -1130,17 +1224,25 @@ func TestCommandSide_AddIconLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyIconAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"key",
|
||||
"icon",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1153,8 +1255,9 @@ func TestCommandSide_AddIconLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddIconLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.storageKey)
|
||||
got, err := r.AddIconLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1294,11 +1397,12 @@ func TestCommandSide_RemoveIconLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -1319,22 +1423,15 @@ func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) {
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
orgID: "",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@ -1349,14 +1446,63 @@ func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logo dark added, ok",
|
||||
fields: fields{
|
||||
@ -1385,17 +1531,25 @@ func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyLogoDarkAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"key",
|
||||
"logo",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "logo",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1408,8 +1562,9 @@ func TestCommandSide_AddLogoDarkLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddLogoDarkLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.storageKey)
|
||||
got, err := r.AddLogoDarkLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1555,9 +1710,9 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) {
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -1576,24 +1731,17 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) {
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
orgID: "",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@ -1608,14 +1756,63 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "icon dark added, ok",
|
||||
fields: fields{
|
||||
@ -1644,17 +1841,25 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyIconDarkAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"key",
|
||||
"icon",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "icon",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1667,8 +1872,9 @@ func TestCommandSide_AddIconDarkLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddIconDarkLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.storageKey)
|
||||
got, err := r.AddIconDarkLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
@ -1806,11 +2012,12 @@ func TestCommandSide_RemoveIconDarkLabelPolicy(t *testing.T) {
|
||||
func TestCommandSide_AddFontLabelPolicy(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -1829,24 +2036,17 @@ func TestCommandSide_AddFontLabelPolicy(t *testing.T) {
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
orgID: "",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "font",
|
||||
ContentType: "ttf",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@ -1861,14 +2061,55 @@ func TestCommandSide_AddFontLabelPolicy(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
"#ffffff",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "font",
|
||||
ContentType: "ttf",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "font added, ok",
|
||||
fields: fields{
|
||||
@ -1897,17 +2138,25 @@ func TestCommandSide_AddFontLabelPolicy(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
org.NewLabelPolicyFontAddedEvent(context.Background(),
|
||||
&org.NewAggregate("org1", "org1").Aggregate,
|
||||
"key",
|
||||
"font",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "font",
|
||||
ContentType: "ttf",
|
||||
ObjectType: static.ObjectTypeStyling,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -1920,8 +2169,9 @@ func TestCommandSide_AddFontLabelPolicy(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddFontLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.storageKey)
|
||||
got, err := r.AddFontLabelPolicy(tt.args.ctx, tt.args.orgID, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
@ -1,27 +1,70 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"io"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/superseriousbusiness/exifremove/pkg/exifremove"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
func (c *Commands) UploadAsset(ctx context.Context, bucketName, objectName, contentType string, file io.Reader, size int64) (*domain.AssetInfo, error) {
|
||||
type AssetUpload struct {
|
||||
ResourceOwner string
|
||||
ObjectName string
|
||||
ContentType string
|
||||
ObjectType static.ObjectType
|
||||
File io.Reader
|
||||
Size int64
|
||||
}
|
||||
|
||||
func (c *Commands) uploadAsset(ctx context.Context, upload *AssetUpload) (*static.Asset, error) {
|
||||
//TODO: handle location as soon as possible
|
||||
file, size, err := removeExif(upload.File, upload.Size, upload.ContentType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return c.static.PutObject(ctx,
|
||||
bucketName,
|
||||
objectName,
|
||||
contentType,
|
||||
authz.GetInstance(ctx).InstanceID(),
|
||||
"",
|
||||
upload.ResourceOwner,
|
||||
upload.ObjectName,
|
||||
upload.ContentType,
|
||||
upload.ObjectType,
|
||||
file,
|
||||
size,
|
||||
true,
|
||||
)
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveAsset(ctx context.Context, bucketName, storeKey string) error {
|
||||
return c.static.RemoveObject(ctx, bucketName, storeKey)
|
||||
func (c *Commands) removeAsset(ctx context.Context, resourceOwner, storeKey string) error {
|
||||
return c.static.RemoveObject(ctx, authz.GetInstance(ctx).InstanceID(), resourceOwner, storeKey)
|
||||
}
|
||||
|
||||
func (c *Commands) RemoveAssetsFolder(ctx context.Context, bucketName, path string, recursive bool) error {
|
||||
return c.static.RemoveObjects(ctx, bucketName, path, recursive)
|
||||
func (c *Commands) removeAssetsFolder(ctx context.Context, resourceOwner string, objectType static.ObjectType) error {
|
||||
return c.static.RemoveObjects(ctx, authz.GetInstance(ctx).InstanceID(), resourceOwner, objectType)
|
||||
}
|
||||
|
||||
func removeExif(file io.Reader, size int64, contentType string) (io.Reader, int64, error) {
|
||||
if !isAllowedContentType(contentType) {
|
||||
return file, size, nil
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
_, err := buf.ReadFrom(file)
|
||||
if err != nil {
|
||||
return file, 0, err
|
||||
}
|
||||
data, err := exifremove.Remove(buf.Bytes())
|
||||
if err != nil {
|
||||
return nil, 0, err
|
||||
}
|
||||
return bytes.NewReader(data), int64(len(data)), nil
|
||||
}
|
||||
|
||||
func isAllowedContentType(contentType string) bool {
|
||||
return strings.HasSuffix(contentType, "png") ||
|
||||
strings.HasSuffix(contentType, "jpg") ||
|
||||
strings.HasSuffix(contentType, "jpeg")
|
||||
}
|
||||
|
@ -8,13 +8,10 @@ import (
|
||||
"github.com/caos/zitadel/internal/repository/user"
|
||||
)
|
||||
|
||||
func (c *Commands) AddHumanAvatar(ctx context.Context, orgID, userID, storageKey string) (*domain.ObjectDetails, error) {
|
||||
func (c *Commands) AddHumanAvatar(ctx context.Context, orgID, userID string, upload *AssetUpload) (*domain.ObjectDetails, error) {
|
||||
if userID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "USER-Ba5Ds", "Errors.IDMissing")
|
||||
}
|
||||
if storageKey == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "USER-1Xyud", "Errors.Assets.EmptyKey")
|
||||
}
|
||||
existingUser, err := c.userWriteModelByID(ctx, userID, orgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@ -23,8 +20,12 @@ func (c *Commands) AddHumanAvatar(ctx context.Context, orgID, userID, storageKey
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "USER-vJ3fS", "Errors.Users.NotFound")
|
||||
}
|
||||
asset, err := c.uploadAsset(ctx, upload)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "USER-1Xyud", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
userAgg := UserAggregateFromWriteModel(&existingUser.WriteModel)
|
||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanAvatarAddedEvent(ctx, userAgg, storageKey))
|
||||
pushedEvents, err := c.eventstore.Push(ctx, user.NewHumanAvatarAddedEvent(ctx, userAgg, asset.Name))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -46,7 +47,7 @@ func (c *Commands) RemoveHumanAvatar(ctx context.Context, orgID, userID string)
|
||||
if existingUser.UserState == domain.UserStateUnspecified || existingUser.UserState == domain.UserStateDeleted {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "USER-35N8f", "Errors.Users.NotFound")
|
||||
}
|
||||
err = c.RemoveAsset(ctx, orgID, existingUser.Avatar)
|
||||
err = c.removeAsset(ctx, orgID, existingUser.Avatar)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
@ -20,12 +21,13 @@ import (
|
||||
func TestCommandSide_AddHumanAvatar(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
storage static.Storage
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
orgID string
|
||||
userID string
|
||||
storageKey string
|
||||
ctx context.Context
|
||||
orgID string
|
||||
userID string
|
||||
upload *AssetUpload
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
@ -44,25 +46,18 @@ func TestCommandSide_AddHumanAvatar(t *testing.T) {
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
storageKey: "key",
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "storage key empty, invalid argument error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
userID: "user1",
|
||||
orgID: "",
|
||||
userID: "",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "avatar",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeUserAvatar,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsErrorInvalidArgument,
|
||||
@ -77,17 +72,65 @@ func TestCommandSide_AddHumanAvatar(t *testing.T) {
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
userID: "user1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
userID: "user1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "avatar",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeUserAvatar,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "logo added, ok",
|
||||
name: "upload failed, error",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"username",
|
||||
"firstname",
|
||||
"lastname",
|
||||
"nickname",
|
||||
"displayname",
|
||||
language.Und,
|
||||
domain.GenderUnspecified,
|
||||
"email@test.ch",
|
||||
true,
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObjectError(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
userID: "user1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "avatar",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeUserAvatar,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errs.IsInternal,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "avatar added, ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(
|
||||
t,
|
||||
@ -112,18 +155,26 @@ func TestCommandSide_AddHumanAvatar(t *testing.T) {
|
||||
eventFromEventPusher(
|
||||
user.NewHumanAvatarAddedEvent(context.Background(),
|
||||
&user.NewAggregate("user1", "org1").Aggregate,
|
||||
"key",
|
||||
"avatar",
|
||||
),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
storage: mock.NewStorage(t).ExpectPutObject(),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
userID: "user1",
|
||||
storageKey: "key",
|
||||
ctx: context.Background(),
|
||||
orgID: "org1",
|
||||
userID: "user1",
|
||||
upload: &AssetUpload{
|
||||
ResourceOwner: "org1",
|
||||
ObjectName: "avatar",
|
||||
ContentType: "image",
|
||||
ObjectType: static.ObjectTypeUserAvatar,
|
||||
File: bytes.NewReader([]byte("test")),
|
||||
Size: 4,
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{
|
||||
@ -136,8 +187,9 @@ func TestCommandSide_AddHumanAvatar(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
r := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
static: tt.fields.storage,
|
||||
}
|
||||
got, err := r.AddHumanAvatar(tt.args.ctx, tt.args.orgID, tt.args.userID, tt.args.storageKey)
|
||||
got, err := r.AddHumanAvatar(tt.args.ctx, tt.args.orgID, tt.args.userID, tt.args.upload)
|
||||
if tt.res.err == nil {
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
@ -27,8 +27,32 @@ const (
|
||||
)
|
||||
|
||||
type InstanceSetup struct {
|
||||
Org OrgSetup
|
||||
Zitadel ZitadelConfig
|
||||
Org OrgSetup
|
||||
Zitadel ZitadelConfig
|
||||
Features struct {
|
||||
TierName string
|
||||
TierDescription string
|
||||
Retention time.Duration
|
||||
State domain.FeaturesState
|
||||
StateDescription string
|
||||
LoginPolicyFactors bool
|
||||
LoginPolicyIDP bool
|
||||
LoginPolicyPasswordless bool
|
||||
LoginPolicyRegistration bool
|
||||
LoginPolicyUsernameLogin bool
|
||||
LoginPolicyPasswordReset bool
|
||||
PasswordComplexityPolicy bool
|
||||
LabelPolicyPrivateLabel bool
|
||||
LabelPolicyWatermark bool
|
||||
CustomDomain bool
|
||||
PrivacyPolicy bool
|
||||
MetadataUser bool
|
||||
CustomTextMessage bool
|
||||
CustomTextLogin bool
|
||||
LockoutPolicy bool
|
||||
ActionsAllowed domain.ActionsAllowed
|
||||
MaxActions int
|
||||
}
|
||||
PasswordComplexityPolicy struct {
|
||||
MinLength uint64
|
||||
HasLowercase bool
|
||||
@ -170,6 +194,31 @@ func (command *Command) SetUpInstance(ctx context.Context, setup *InstanceSetup)
|
||||
projectAgg := project.NewAggregate(setup.Zitadel.projectID, orgID)
|
||||
|
||||
validations := []preparation.Validation{
|
||||
SetDefaultFeatures(
|
||||
instanceAgg,
|
||||
setup.Features.TierName,
|
||||
setup.Features.TierDescription,
|
||||
setup.Features.State,
|
||||
setup.Features.StateDescription,
|
||||
setup.Features.Retention,
|
||||
setup.Features.LoginPolicyFactors,
|
||||
setup.Features.LoginPolicyIDP,
|
||||
setup.Features.LoginPolicyPasswordless,
|
||||
setup.Features.LoginPolicyRegistration,
|
||||
setup.Features.LoginPolicyUsernameLogin,
|
||||
setup.Features.LoginPolicyPasswordReset,
|
||||
setup.Features.PasswordComplexityPolicy,
|
||||
setup.Features.LabelPolicyPrivateLabel,
|
||||
setup.Features.LabelPolicyWatermark,
|
||||
setup.Features.CustomDomain,
|
||||
setup.Features.PrivacyPolicy,
|
||||
setup.Features.MetadataUser,
|
||||
setup.Features.CustomTextMessage,
|
||||
setup.Features.CustomTextLogin,
|
||||
setup.Features.LockoutPolicy,
|
||||
setup.Features.ActionsAllowed,
|
||||
setup.Features.MaxActions,
|
||||
),
|
||||
AddPasswordComplexityPolicy(
|
||||
instanceAgg,
|
||||
setup.PasswordComplexityPolicy.MinLength,
|
||||
|
95
internal/command/v2/instance_features.go
Normal file
95
internal/command/v2/instance_features.go
Normal file
@ -0,0 +1,95 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/command"
|
||||
"github.com/caos/zitadel/internal/command/v2/preparation"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func SetDefaultFeatures(
|
||||
a *instance.Aggregate,
|
||||
tierName,
|
||||
tierDescription string,
|
||||
state domain.FeaturesState,
|
||||
stateDescription string,
|
||||
retention time.Duration,
|
||||
loginPolicyFactors,
|
||||
loginPolicyIDP,
|
||||
loginPolicyPasswordless,
|
||||
loginPolicyRegistration,
|
||||
loginPolicyUsernameLogin,
|
||||
loginPolicyPasswordReset,
|
||||
passwordComplexityPolicy,
|
||||
labelPolicyPrivateLabel,
|
||||
labelPolicyWatermark,
|
||||
customDomain,
|
||||
privacyPolicy,
|
||||
metadataUser,
|
||||
customTextMessage,
|
||||
customTextLogin,
|
||||
lockoutPolicy bool,
|
||||
actionsAllowed domain.ActionsAllowed,
|
||||
maxActions int,
|
||||
) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if !state.Valid() || state == domain.FeaturesStateUnspecified || state == domain.FeaturesStateRemoved {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "INSTA-d3r1s", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
writeModel, err := defaultFeatures(ctx, filter)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
event, hasChanged := writeModel.NewSetEvent(ctx, &a.Aggregate,
|
||||
tierName,
|
||||
tierDescription,
|
||||
state,
|
||||
stateDescription,
|
||||
retention,
|
||||
loginPolicyFactors,
|
||||
loginPolicyIDP,
|
||||
loginPolicyPasswordless,
|
||||
loginPolicyRegistration,
|
||||
loginPolicyUsernameLogin,
|
||||
loginPolicyPasswordReset,
|
||||
passwordComplexityPolicy,
|
||||
labelPolicyPrivateLabel,
|
||||
labelPolicyWatermark,
|
||||
customDomain,
|
||||
privacyPolicy,
|
||||
metadataUser,
|
||||
customTextMessage,
|
||||
customTextLogin,
|
||||
lockoutPolicy,
|
||||
actionsAllowed,
|
||||
maxActions,
|
||||
)
|
||||
if !hasChanged {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "INSTA-GE4h2", "Errors.Features.NotChanged")
|
||||
}
|
||||
return []eventstore.Command{
|
||||
event,
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func defaultFeatures(ctx context.Context, filter preparation.FilterToQueryReducer) (*command.InstanceFeaturesWriteModel, error) {
|
||||
features := command.NewInstanceFeaturesWriteModel(ctx)
|
||||
events, err := filter(ctx, features.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(events) == 0 {
|
||||
return features, nil
|
||||
}
|
||||
features.AppendEvents(events...)
|
||||
err = features.Reduce()
|
||||
return features, err
|
||||
}
|
152
internal/command/v2/instance_features_test.go
Normal file
152
internal/command/v2/instance_features_test.go
Normal file
@ -0,0 +1,152 @@
|
||||
package command
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore"
|
||||
"github.com/caos/zitadel/internal/repository/features"
|
||||
"github.com/caos/zitadel/internal/repository/instance"
|
||||
)
|
||||
|
||||
func TestSetDefaultFeatures(t *testing.T) {
|
||||
type args struct {
|
||||
a *instance.Aggregate
|
||||
tierName string
|
||||
tierDescription string
|
||||
state domain.FeaturesState
|
||||
stateDescription string
|
||||
retention time.Duration
|
||||
loginPolicyFactors bool
|
||||
loginPolicyIDP bool
|
||||
loginPolicyPasswordless bool
|
||||
loginPolicyRegistration bool
|
||||
loginPolicyUsernameLogin bool
|
||||
loginPolicyPasswordReset bool
|
||||
passwordComplexityPolicy bool
|
||||
labelPolicyPrivateLabel bool
|
||||
labelPolicyWatermark bool
|
||||
customDomain bool
|
||||
privacyPolicy bool
|
||||
metadataUser bool
|
||||
customTextMessage bool
|
||||
customTextLogin bool
|
||||
lockoutPolicy bool
|
||||
actionsAllowed domain.ActionsAllowed
|
||||
maxActions int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want Want
|
||||
}{
|
||||
{
|
||||
name: "invalid state",
|
||||
args: args{
|
||||
a: instance.NewAggregate("INSTANCE"),
|
||||
tierName: "",
|
||||
tierDescription: "",
|
||||
state: 0,
|
||||
stateDescription: "",
|
||||
retention: 0,
|
||||
loginPolicyFactors: false,
|
||||
loginPolicyIDP: false,
|
||||
loginPolicyPasswordless: false,
|
||||
loginPolicyRegistration: false,
|
||||
loginPolicyUsernameLogin: false,
|
||||
loginPolicyPasswordReset: false,
|
||||
passwordComplexityPolicy: false,
|
||||
labelPolicyPrivateLabel: false,
|
||||
labelPolicyWatermark: false,
|
||||
customDomain: false,
|
||||
privacyPolicy: false,
|
||||
metadataUser: false,
|
||||
customTextMessage: false,
|
||||
customTextLogin: false,
|
||||
lockoutPolicy: false,
|
||||
actionsAllowed: 0,
|
||||
maxActions: 0,
|
||||
},
|
||||
want: Want{
|
||||
ValidationErr: errors.ThrowInvalidArgument(nil, "INSTA-d3r1s", "Errors.Invalid.Argument"),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "correct",
|
||||
args: args{
|
||||
a: instance.NewAggregate("INSTANCE"),
|
||||
tierName: "",
|
||||
tierDescription: "",
|
||||
state: domain.FeaturesStateActive,
|
||||
stateDescription: "",
|
||||
retention: 0,
|
||||
loginPolicyFactors: false,
|
||||
loginPolicyIDP: false,
|
||||
loginPolicyPasswordless: false,
|
||||
loginPolicyRegistration: false,
|
||||
loginPolicyUsernameLogin: false,
|
||||
loginPolicyPasswordReset: false,
|
||||
passwordComplexityPolicy: false,
|
||||
labelPolicyPrivateLabel: false,
|
||||
labelPolicyWatermark: false,
|
||||
customDomain: false,
|
||||
privacyPolicy: false,
|
||||
metadataUser: false,
|
||||
customTextMessage: false,
|
||||
customTextLogin: false,
|
||||
lockoutPolicy: false,
|
||||
actionsAllowed: 0,
|
||||
maxActions: 0,
|
||||
},
|
||||
want: Want{
|
||||
Commands: []eventstore.Command{
|
||||
func() *instance.FeaturesSetEvent {
|
||||
event, _ := instance.NewFeaturesSetEvent(context.Background(), &instance.NewAggregate("INSTANCE").Aggregate,
|
||||
[]features.FeaturesChanges{
|
||||
features.ChangeState(domain.FeaturesStateActive),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}(),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
AssertValidation(t, SetDefaultFeatures(
|
||||
tt.args.a,
|
||||
tt.args.tierName,
|
||||
tt.args.tierDescription,
|
||||
tt.args.state,
|
||||
tt.args.stateDescription,
|
||||
tt.args.retention,
|
||||
tt.args.loginPolicyFactors,
|
||||
tt.args.loginPolicyIDP,
|
||||
tt.args.loginPolicyPasswordless,
|
||||
tt.args.loginPolicyRegistration,
|
||||
tt.args.loginPolicyUsernameLogin,
|
||||
tt.args.loginPolicyPasswordReset,
|
||||
tt.args.passwordComplexityPolicy,
|
||||
tt.args.labelPolicyPrivateLabel,
|
||||
tt.args.labelPolicyWatermark,
|
||||
tt.args.customDomain,
|
||||
tt.args.privacyPolicy,
|
||||
tt.args.metadataUser,
|
||||
tt.args.customTextMessage,
|
||||
tt.args.customTextLogin,
|
||||
tt.args.lockoutPolicy,
|
||||
tt.args.actionsAllowed,
|
||||
tt.args.maxActions,
|
||||
), NewMultiFilter().
|
||||
Append(func(ctx context.Context, queryFactory *eventstore.SearchQueryBuilder) ([]eventstore.Event, error) {
|
||||
return nil, nil
|
||||
}).
|
||||
Filter(),
|
||||
tt.want)
|
||||
})
|
||||
}
|
||||
}
|
@ -62,9 +62,9 @@ func NewFeatureProjection(ctx context.Context, config crdb.StatementHandlerConfi
|
||||
crdb.NewColumn(FeatureSequenceCol, crdb.ColumnTypeInt64),
|
||||
crdb.NewColumn(FeatureIsDefaultCol, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||
crdb.NewColumn(FeatureTierNameCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(FeatureTierDescriptionCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(FeatureTierDescriptionCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||
crdb.NewColumn(FeatureStateCol, crdb.ColumnTypeEnum, crdb.Default(0)),
|
||||
crdb.NewColumn(FeatureStateDescriptionCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(FeatureStateDescriptionCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||
crdb.NewColumn(FeatureAuditLogRetentionCol, crdb.ColumnTypeInt64, crdb.Default(0)),
|
||||
crdb.NewColumn(FeatureLoginPolicyFactorsCol, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||
crdb.NewColumn(FeatureLoginPolicyIDPCol, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||
|
@ -1,114 +1,30 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
"database/sql"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
"github.com/caos/zitadel/internal/static/database"
|
||||
"github.com/caos/zitadel/internal/static/s3"
|
||||
)
|
||||
|
||||
type AssetStorageConfig struct {
|
||||
Type string
|
||||
Config static.Config
|
||||
Config map[string]interface{} `mapstructure:",remain"`
|
||||
}
|
||||
|
||||
var storage = map[string]func() static.Config{
|
||||
"s3": func() static.Config { return &s3.Config{} },
|
||||
"none": func() static.Config { return &NoStorage{} },
|
||||
"": func() static.Config { return &NoStorage{} },
|
||||
}
|
||||
|
||||
func (c *AssetStorageConfig) UnmarshalJSON(data []byte) error {
|
||||
var rc struct {
|
||||
Type string
|
||||
Config json.RawMessage
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(data, &rc); err != nil {
|
||||
return errors.ThrowInternal(err, "STATIC-Bfn5r", "error parsing config")
|
||||
}
|
||||
|
||||
c.Type = rc.Type
|
||||
|
||||
var err error
|
||||
c.Config, err = newStorageConfig(c.Type, rc.Config)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func newStorageConfig(storageType string, configData []byte) (static.Config, error) {
|
||||
t, ok := storage[storageType]
|
||||
func (a *AssetStorageConfig) NewStorage(client *sql.DB) (static.Storage, error) {
|
||||
t, ok := storage[a.Type]
|
||||
if !ok {
|
||||
return nil, errors.ThrowInternalf(nil, "STATIC-dsbjh", "config type %s not supported", storageType)
|
||||
return nil, errors.ThrowInternalf(nil, "STATIC-dsbjh", "config type %s not supported", a.Type)
|
||||
}
|
||||
|
||||
staticConfig := t()
|
||||
if len(configData) == 0 {
|
||||
return staticConfig, nil
|
||||
}
|
||||
|
||||
if err := json.Unmarshal(configData, staticConfig); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "STATIC-GB4nw", "Could not read config: %v")
|
||||
}
|
||||
|
||||
return staticConfig, nil
|
||||
return t(client, a.Config)
|
||||
}
|
||||
|
||||
var (
|
||||
errNoStorage = errors.ThrowInternal(nil, "STATIC-ashg4", "Errors.Assets.Store.NotConfigured")
|
||||
)
|
||||
|
||||
type NoStorage struct{}
|
||||
|
||||
func (_ *NoStorage) NewStorage() (static.Storage, error) {
|
||||
return &NoStorage{}, nil
|
||||
}
|
||||
|
||||
func (_ *NoStorage) CreateBucket(ctx context.Context, name, location string) error {
|
||||
return errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) RemoveBucket(ctx context.Context, name string) error {
|
||||
return errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) ListBuckets(ctx context.Context) ([]*domain.BucketInfo, error) {
|
||||
return nil, errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) PutObject(ctx context.Context, bucketName, objectName, contentType string, object io.Reader, objectSize int64, createBucketIfNotExisting bool) (*domain.AssetInfo, error) {
|
||||
return nil, errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) GetObjectInfo(ctx context.Context, bucketName, objectName string) (*domain.AssetInfo, error) {
|
||||
return nil, errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) GetObject(ctx context.Context, bucketName, objectName string) (io.Reader, func() (*domain.AssetInfo, error), error) {
|
||||
return nil, nil, errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) ListObjectInfos(ctx context.Context, bucketName, prefix string, recursive bool) ([]*domain.AssetInfo, error) {
|
||||
return nil, errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) GetObjectPresignedURL(ctx context.Context, bucketName, objectName string, expiration time.Duration) (*url.URL, error) {
|
||||
return nil, errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) RemoveObject(ctx context.Context, bucketName, objectName string) error {
|
||||
return errNoStorage
|
||||
}
|
||||
|
||||
func (_ *NoStorage) RemoveObjects(ctx context.Context, bucketName, path string, recursive bool) error {
|
||||
return errNoStorage
|
||||
var storage = map[string]static.CreateStorage{
|
||||
"db": database.NewStorage,
|
||||
"": database.NewStorage,
|
||||
"s3": s3.NewStorage,
|
||||
}
|
||||
|
182
internal/static/database/crdb.go
Normal file
182
internal/static/database/crdb.go
Normal file
@ -0,0 +1,182 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"time"
|
||||
|
||||
"github.com/Masterminds/squirrel"
|
||||
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
var _ static.Storage = (*crdbStorage)(nil)
|
||||
|
||||
const (
|
||||
assetsTable = "system.assets"
|
||||
AssetColInstanceID = "instance_id"
|
||||
AssetColType = "asset_type"
|
||||
AssetColLocation = "location"
|
||||
AssetColResourceOwner = "resource_owner"
|
||||
AssetColName = "name"
|
||||
AssetColData = "data"
|
||||
AssetColContentType = "content_type"
|
||||
AssetColHash = "hash"
|
||||
AssetColUpdatedAt = "updated_at"
|
||||
)
|
||||
|
||||
type crdbStorage struct {
|
||||
client *sql.DB
|
||||
}
|
||||
|
||||
func NewStorage(client *sql.DB, _ map[string]interface{}) (static.Storage, error) {
|
||||
return &crdbStorage{client: client}, nil
|
||||
}
|
||||
|
||||
func (c *crdbStorage) PutObject(ctx context.Context, instanceID, location, resourceOwner, name, contentType string, objectType static.ObjectType, object io.Reader, objectSize int64) (*static.Asset, error) {
|
||||
data, err := io.ReadAll(object)
|
||||
if err != nil {
|
||||
return nil, caos_errors.ThrowInternal(err, "DATAB-Dfwvq", "Errors.Internal")
|
||||
}
|
||||
stmt, args, err := squirrel.Insert(assetsTable).
|
||||
Columns(AssetColInstanceID, AssetColResourceOwner, AssetColName, AssetColType, AssetColContentType, AssetColData, AssetColUpdatedAt).
|
||||
Values(instanceID, resourceOwner, name, objectType, contentType, data, "now()").
|
||||
Suffix(fmt.Sprintf(
|
||||
"ON CONFLICT (%s, %s, %s) DO UPDATE"+
|
||||
" SET %s = $5, %s = $6"+
|
||||
" RETURNING %s, %s", AssetColInstanceID, AssetColResourceOwner, AssetColName, AssetColContentType, AssetColData, AssetColHash, AssetColUpdatedAt)).
|
||||
PlaceholderFormat(squirrel.Dollar).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return nil, caos_errors.ThrowInternal(err, "DATAB-32DG1", "Errors.Internal")
|
||||
}
|
||||
var hash string
|
||||
var updatedAt time.Time
|
||||
err = c.client.QueryRowContext(ctx, stmt, args...).Scan(&hash, &updatedAt)
|
||||
if err != nil {
|
||||
return nil, caos_errors.ThrowInternal(err, "DATAB-D2g2q", "Errors.Internal")
|
||||
}
|
||||
return &static.Asset{
|
||||
InstanceID: instanceID,
|
||||
Name: name,
|
||||
Hash: hash,
|
||||
Size: objectSize,
|
||||
LastModified: updatedAt,
|
||||
Location: location,
|
||||
ContentType: contentType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (c *crdbStorage) GetObject(ctx context.Context, instanceID, resourceOwner, name string) ([]byte, func() (*static.Asset, error), error) {
|
||||
query, args, err := squirrel.Select(AssetColData, AssetColContentType, AssetColHash, AssetColUpdatedAt).
|
||||
From(assetsTable).
|
||||
Where(squirrel.Eq{
|
||||
AssetColInstanceID: instanceID,
|
||||
AssetColResourceOwner: resourceOwner,
|
||||
AssetColName: name,
|
||||
}).
|
||||
PlaceholderFormat(squirrel.Dollar).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return nil, nil, caos_errors.ThrowInternal(err, "DATAB-GE3hz", "Errors.Internal")
|
||||
}
|
||||
var data []byte
|
||||
asset := &static.Asset{
|
||||
InstanceID: instanceID,
|
||||
ResourceOwner: resourceOwner,
|
||||
Name: name,
|
||||
}
|
||||
err = c.client.QueryRowContext(ctx, query, args...).
|
||||
Scan(
|
||||
&data,
|
||||
&asset.ContentType,
|
||||
&asset.Hash,
|
||||
&asset.LastModified,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, nil, caos_errors.ThrowNotFound(err, "DATAB-pCP8P", "Errors.Assets.Object.NotFound")
|
||||
}
|
||||
return nil, nil, caos_errors.ThrowInternal(err, "DATAB-Sfgb3", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
asset.Size = int64(len(data))
|
||||
return data,
|
||||
func() (*static.Asset, error) {
|
||||
return asset, nil
|
||||
},
|
||||
nil
|
||||
}
|
||||
|
||||
func (c *crdbStorage) GetObjectInfo(ctx context.Context, instanceID, resourceOwner, name string) (*static.Asset, error) {
|
||||
query, args, err := squirrel.Select(AssetColContentType, AssetColLocation, "length("+AssetColData+")", AssetColHash, AssetColUpdatedAt).
|
||||
From(assetsTable).
|
||||
Where(squirrel.Eq{
|
||||
AssetColInstanceID: instanceID,
|
||||
AssetColResourceOwner: resourceOwner,
|
||||
AssetColName: name,
|
||||
}).
|
||||
PlaceholderFormat(squirrel.Dollar).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return nil, caos_errors.ThrowInternal(err, "DATAB-rggt2", "Errors.Internal")
|
||||
}
|
||||
asset := &static.Asset{
|
||||
InstanceID: instanceID,
|
||||
ResourceOwner: resourceOwner,
|
||||
Name: name,
|
||||
}
|
||||
err = c.client.QueryRowContext(ctx, query, args...).
|
||||
Scan(
|
||||
&asset.ContentType,
|
||||
&asset.Location,
|
||||
&asset.Size,
|
||||
&asset.Hash,
|
||||
&asset.LastModified,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, caos_errors.ThrowInternal(err, "DATAB-Dbh2s", "Errors.Internal")
|
||||
}
|
||||
return asset, nil
|
||||
}
|
||||
|
||||
func (c *crdbStorage) RemoveObject(ctx context.Context, instanceID, resourceOwner, name string) error {
|
||||
stmt, args, err := squirrel.Delete(assetsTable).
|
||||
Where(squirrel.Eq{
|
||||
AssetColInstanceID: instanceID,
|
||||
AssetColResourceOwner: resourceOwner,
|
||||
AssetColName: name,
|
||||
}).
|
||||
PlaceholderFormat(squirrel.Dollar).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return caos_errors.ThrowInternal(err, "DATAB-Sgvwq", "Errors.Internal")
|
||||
}
|
||||
_, err = c.client.ExecContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return caos_errors.ThrowInternal(err, "DATAB-RHNgf", "Errors.Assets.Object.RemoveFailed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *crdbStorage) RemoveObjects(ctx context.Context, instanceID, resourceOwner string, objectType static.ObjectType) error {
|
||||
stmt, args, err := squirrel.Delete(assetsTable).
|
||||
Where(squirrel.Eq{
|
||||
AssetColInstanceID: instanceID,
|
||||
AssetColResourceOwner: resourceOwner,
|
||||
AssetColType: objectType,
|
||||
}).
|
||||
PlaceholderFormat(squirrel.Dollar).
|
||||
ToSql()
|
||||
if err != nil {
|
||||
return caos_errors.ThrowInternal(err, "DATAB-Sfgeq", "Errors.Internal")
|
||||
}
|
||||
_, err = c.client.ExecContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return caos_errors.ThrowInternal(err, "DATAB-Efgt2", "Errors.Assets.Object.RemoveFailed")
|
||||
}
|
||||
return nil
|
||||
}
|
203
internal/static/database/crdb_test.go
Normal file
203
internal/static/database/crdb_test.go
Normal file
@ -0,0 +1,203 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"io"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/DATA-DOG/go-sqlmock"
|
||||
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
var (
|
||||
testNow = time.Now()
|
||||
)
|
||||
|
||||
const (
|
||||
objectStmt = "INSERT INTO system.assets" +
|
||||
" (instance_id,resource_owner,name,asset_type,content_type,data,updated_at)" +
|
||||
" VALUES ($1,$2,$3,$4,$5,$6,$7)" +
|
||||
" ON CONFLICT (instance_id, resource_owner, name) DO UPDATE SET" +
|
||||
" content_type = $5, data = $6" +
|
||||
" RETURNING hash"
|
||||
)
|
||||
|
||||
func Test_crdbStorage_CreateObject(t *testing.T) {
|
||||
type fields struct {
|
||||
client db
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
instanceID string
|
||||
location string
|
||||
resourceOwner string
|
||||
name string
|
||||
contentType string
|
||||
objectType static.ObjectType
|
||||
data io.Reader
|
||||
objectSize int64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
want *static.Asset
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
"create ok",
|
||||
fields{
|
||||
client: prepareDB(t,
|
||||
expectQuery(
|
||||
objectStmt,
|
||||
[]string{
|
||||
"hash",
|
||||
"updated_at",
|
||||
},
|
||||
[][]driver.Value{
|
||||
{
|
||||
"md5Hash",
|
||||
testNow,
|
||||
},
|
||||
},
|
||||
"instanceID",
|
||||
"resourceOwner",
|
||||
"name",
|
||||
static.ObjectTypeUserAvatar,
|
||||
"contentType",
|
||||
[]byte("test"),
|
||||
"now()",
|
||||
)),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
instanceID: "instanceID",
|
||||
location: "location",
|
||||
resourceOwner: "resourceOwner",
|
||||
name: "name",
|
||||
contentType: "contentType",
|
||||
data: bytes.NewReader([]byte("test")),
|
||||
objectSize: 4,
|
||||
},
|
||||
&static.Asset{
|
||||
InstanceID: "instanceID",
|
||||
Name: "name",
|
||||
Hash: "md5Hash",
|
||||
Size: 4,
|
||||
LastModified: testNow,
|
||||
Location: "location",
|
||||
ContentType: "contentType",
|
||||
},
|
||||
false,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &crdbStorage{
|
||||
client: tt.fields.client.db,
|
||||
}
|
||||
got, err := c.PutObject(tt.args.ctx, tt.args.instanceID, tt.args.location, tt.args.resourceOwner, tt.args.name, tt.args.contentType, tt.args.objectType, tt.args.data, tt.args.objectSize)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("CreateObject() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("CreateObject() got = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
type db struct {
|
||||
mock sqlmock.Sqlmock
|
||||
db *sql.DB
|
||||
}
|
||||
|
||||
func prepareDB(t *testing.T, expectations ...expectation) db {
|
||||
t.Helper()
|
||||
client, mock, err := sqlmock.New()
|
||||
if err != nil {
|
||||
t.Fatalf("unable to create sql mock: %v", err)
|
||||
}
|
||||
for _, expectation := range expectations {
|
||||
expectation(mock)
|
||||
}
|
||||
return db{
|
||||
mock: mock,
|
||||
db: client,
|
||||
}
|
||||
}
|
||||
|
||||
type expectation func(m sqlmock.Sqlmock)
|
||||
|
||||
func expectExists(query string, value bool, args ...driver.Value) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnRows(sqlmock.NewRows([]string{"exists"}).AddRow(value))
|
||||
}
|
||||
}
|
||||
|
||||
func expectQueryErr(query string, err error, args ...driver.Value) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
m.ExpectQuery(regexp.QuoteMeta(query)).WithArgs(args...).WillReturnError(err)
|
||||
}
|
||||
}
|
||||
func expectQuery(stmt string, cols []string, rows [][]driver.Value, args ...driver.Value) func(m sqlmock.Sqlmock) {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
q := m.ExpectQuery(regexp.QuoteMeta(stmt)).WithArgs(args...)
|
||||
result := sqlmock.NewRows(cols)
|
||||
count := uint64(len(rows))
|
||||
for _, row := range rows {
|
||||
if cols[len(cols)-1] == "count" {
|
||||
row = append(row, count)
|
||||
}
|
||||
result.AddRow(row...)
|
||||
}
|
||||
q.WillReturnRows(result)
|
||||
q.RowsWillBeClosed()
|
||||
}
|
||||
}
|
||||
|
||||
func expectExec(stmt string, err error, args ...driver.Value) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
query := m.ExpectExec(regexp.QuoteMeta(stmt)).WithArgs(args...)
|
||||
if err != nil {
|
||||
query.WillReturnError(err)
|
||||
return
|
||||
}
|
||||
query.WillReturnResult(sqlmock.NewResult(1, 1))
|
||||
}
|
||||
}
|
||||
|
||||
func expectBegin(err error) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
query := m.ExpectBegin()
|
||||
if err != nil {
|
||||
query.WillReturnError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expectCommit(err error) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
query := m.ExpectCommit()
|
||||
if err != nil {
|
||||
query.WillReturnError(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func expectRollback(err error) expectation {
|
||||
return func(m sqlmock.Sqlmock) {
|
||||
query := m.ExpectRollback()
|
||||
if err != nil {
|
||||
query.WillReturnError(err)
|
||||
}
|
||||
}
|
||||
}
|
@ -22,6 +22,7 @@ Errors:
|
||||
Object:
|
||||
PutFailed: Objekt konnte nicht erstellt werden
|
||||
GetFailed: Objekt konnte nicht gelesen werden
|
||||
NotFound: Objekt konnte nicht gefunden werden
|
||||
PresignedTokenFailed: Signiertes Token konnte nicht erstellt werden
|
||||
ListFailed: Objektliste konnte nicht gelesen werden
|
||||
RemoveFailed: Objekt konnte nicht gelöscht werden
|
||||
|
@ -22,6 +22,7 @@ Errors:
|
||||
Object:
|
||||
PutFailed: Object not created
|
||||
GetFailed: Object could not be read
|
||||
NotFound: Object could not be found
|
||||
PresignedTokenFailed: Signed token could not be created
|
||||
ListFailed: Objectlist could not be read
|
||||
RemoveFailed: Object could not be removed
|
||||
|
@ -22,6 +22,7 @@ Errors:
|
||||
Object:
|
||||
PutFailed: Oggetto non creato
|
||||
GetFailed: Oggetto non può essere letto
|
||||
NotFound: Oggetto non trovato
|
||||
PresignedTokenFailed: Il token non può essere creato
|
||||
ListFailed: La lista degli oggetti non può essere letta
|
||||
RemoveFailed: L'oggetto non può essere rimosso
|
||||
|
@ -7,11 +7,8 @@ package mock
|
||||
import (
|
||||
context "context"
|
||||
io "io"
|
||||
url "net/url"
|
||||
reflect "reflect"
|
||||
time "time"
|
||||
|
||||
domain "github.com/caos/zitadel/internal/domain"
|
||||
static "github.com/caos/zitadel/internal/static"
|
||||
gomock "github.com/golang/mock/gomock"
|
||||
)
|
||||
@ -39,187 +36,76 @@ func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// CreateBucket mocks base method.
|
||||
func (m *MockStorage) CreateBucket(ctx context.Context, name, location string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "CreateBucket", ctx, name, location)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// CreateBucket indicates an expected call of CreateBucket.
|
||||
func (mr *MockStorageMockRecorder) CreateBucket(ctx, name, location interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "CreateBucket", reflect.TypeOf((*MockStorage)(nil).CreateBucket), ctx, name, location)
|
||||
}
|
||||
|
||||
// GetObject mocks base method.
|
||||
func (m *MockStorage) GetObject(ctx context.Context, bucketName, objectName string) (io.Reader, func() (*domain.AssetInfo, error), error) {
|
||||
func (m *MockStorage) GetObject(ctx context.Context, instanceID, resourceOwner, name string) ([]byte, func() (*static.Asset, error), error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetObject", ctx, bucketName, objectName)
|
||||
ret0, _ := ret[0].(io.Reader)
|
||||
ret1, _ := ret[1].(func() (*domain.AssetInfo, error))
|
||||
ret := m.ctrl.Call(m, "GetObject", ctx, instanceID, resourceOwner, name)
|
||||
ret0, _ := ret[0].([]byte)
|
||||
ret1, _ := ret[1].(func() (*static.Asset, error))
|
||||
ret2, _ := ret[2].(error)
|
||||
return ret0, ret1, ret2
|
||||
}
|
||||
|
||||
// GetObject indicates an expected call of GetObject.
|
||||
func (mr *MockStorageMockRecorder) GetObject(ctx, bucketName, objectName interface{}) *gomock.Call {
|
||||
func (mr *MockStorageMockRecorder) GetObject(ctx, instanceID, resourceOwner, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObject", reflect.TypeOf((*MockStorage)(nil).GetObject), ctx, bucketName, objectName)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObject", reflect.TypeOf((*MockStorage)(nil).GetObject), ctx, instanceID, resourceOwner, name)
|
||||
}
|
||||
|
||||
// GetObjectInfo mocks base method.
|
||||
func (m *MockStorage) GetObjectInfo(ctx context.Context, bucketName, objectName string) (*domain.AssetInfo, error) {
|
||||
func (m *MockStorage) GetObjectInfo(ctx context.Context, instanceID, resourceOwner, name string) (*static.Asset, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetObjectInfo", ctx, bucketName, objectName)
|
||||
ret0, _ := ret[0].(*domain.AssetInfo)
|
||||
ret := m.ctrl.Call(m, "GetObjectInfo", ctx, instanceID, resourceOwner, name)
|
||||
ret0, _ := ret[0].(*static.Asset)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetObjectInfo indicates an expected call of GetObjectInfo.
|
||||
func (mr *MockStorageMockRecorder) GetObjectInfo(ctx, bucketName, objectName interface{}) *gomock.Call {
|
||||
func (mr *MockStorageMockRecorder) GetObjectInfo(ctx, instanceID, resourceOwner, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfo", reflect.TypeOf((*MockStorage)(nil).GetObjectInfo), ctx, bucketName, objectName)
|
||||
}
|
||||
|
||||
// GetObjectPresignedURL mocks base method.
|
||||
func (m *MockStorage) GetObjectPresignedURL(ctx context.Context, bucketName, objectName string, expiration time.Duration) (*url.URL, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "GetObjectPresignedURL", ctx, bucketName, objectName, expiration)
|
||||
ret0, _ := ret[0].(*url.URL)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// GetObjectPresignedURL indicates an expected call of GetObjectPresignedURL.
|
||||
func (mr *MockStorageMockRecorder) GetObjectPresignedURL(ctx, bucketName, objectName, expiration interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectPresignedURL", reflect.TypeOf((*MockStorage)(nil).GetObjectPresignedURL), ctx, bucketName, objectName, expiration)
|
||||
}
|
||||
|
||||
// ListBuckets mocks base method.
|
||||
func (m *MockStorage) ListBuckets(ctx context.Context) ([]*domain.BucketInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListBuckets", ctx)
|
||||
ret0, _ := ret[0].([]*domain.BucketInfo)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListBuckets indicates an expected call of ListBuckets.
|
||||
func (mr *MockStorageMockRecorder) ListBuckets(ctx interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListBuckets", reflect.TypeOf((*MockStorage)(nil).ListBuckets), ctx)
|
||||
}
|
||||
|
||||
// ListObjectInfos mocks base method.
|
||||
func (m *MockStorage) ListObjectInfos(ctx context.Context, bucketName, prefix string, recursive bool) ([]*domain.AssetInfo, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "ListObjectInfos", ctx, bucketName, prefix, recursive)
|
||||
ret0, _ := ret[0].([]*domain.AssetInfo)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// ListObjectInfos indicates an expected call of ListObjectInfos.
|
||||
func (mr *MockStorageMockRecorder) ListObjectInfos(ctx, bucketName, prefix, recursive interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ListObjectInfos", reflect.TypeOf((*MockStorage)(nil).ListObjectInfos), ctx, bucketName, prefix, recursive)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfo", reflect.TypeOf((*MockStorage)(nil).GetObjectInfo), ctx, instanceID, resourceOwner, name)
|
||||
}
|
||||
|
||||
// PutObject mocks base method.
|
||||
func (m *MockStorage) PutObject(ctx context.Context, bucketName, objectName, contentType string, object io.Reader, objectSize int64, createBucketIfNotExisting bool) (*domain.AssetInfo, error) {
|
||||
func (m *MockStorage) PutObject(ctx context.Context, instanceID, location, resourceOwner, name, contentType string, objectType static.ObjectType, object io.Reader, objectSize int64) (*static.Asset, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "PutObject", ctx, bucketName, objectName, contentType, object, objectSize, createBucketIfNotExisting)
|
||||
ret0, _ := ret[0].(*domain.AssetInfo)
|
||||
ret := m.ctrl.Call(m, "PutObject", ctx, instanceID, location, resourceOwner, name, contentType, objectType, object, objectSize)
|
||||
ret0, _ := ret[0].(*static.Asset)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// PutObject indicates an expected call of PutObject.
|
||||
func (mr *MockStorageMockRecorder) PutObject(ctx, bucketName, objectName, contentType, object, objectSize, createBucketIfNotExisting interface{}) *gomock.Call {
|
||||
func (mr *MockStorageMockRecorder) PutObject(ctx, instanceID, location, resourceOwner, name, contentType, objectType, object, objectSize interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutObject", reflect.TypeOf((*MockStorage)(nil).PutObject), ctx, bucketName, objectName, contentType, object, objectSize, createBucketIfNotExisting)
|
||||
}
|
||||
|
||||
// RemoveBucket mocks base method.
|
||||
func (m *MockStorage) RemoveBucket(ctx context.Context, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveBucket", ctx, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveBucket indicates an expected call of RemoveBucket.
|
||||
func (mr *MockStorageMockRecorder) RemoveBucket(ctx, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveBucket", reflect.TypeOf((*MockStorage)(nil).RemoveBucket), ctx, name)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "PutObject", reflect.TypeOf((*MockStorage)(nil).PutObject), ctx, instanceID, location, resourceOwner, name, contentType, objectType, object, objectSize)
|
||||
}
|
||||
|
||||
// RemoveObject mocks base method.
|
||||
func (m *MockStorage) RemoveObject(ctx context.Context, bucketName, objectName string) error {
|
||||
func (m *MockStorage) RemoveObject(ctx context.Context, instanceID, resourceOwner, name string) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveObject", ctx, bucketName, objectName)
|
||||
ret := m.ctrl.Call(m, "RemoveObject", ctx, instanceID, resourceOwner, name)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveObject indicates an expected call of RemoveObject.
|
||||
func (mr *MockStorageMockRecorder) RemoveObject(ctx, bucketName, objectName interface{}) *gomock.Call {
|
||||
func (mr *MockStorageMockRecorder) RemoveObject(ctx, instanceID, resourceOwner, name interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObject", reflect.TypeOf((*MockStorage)(nil).RemoveObject), ctx, bucketName, objectName)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObject", reflect.TypeOf((*MockStorage)(nil).RemoveObject), ctx, instanceID, resourceOwner, name)
|
||||
}
|
||||
|
||||
// RemoveObjects mocks base method.
|
||||
func (m *MockStorage) RemoveObjects(ctx context.Context, bucketName, path string, recursive bool) error {
|
||||
func (m *MockStorage) RemoveObjects(ctx context.Context, instanceID, resourceOwner string, objectType static.ObjectType) error {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "RemoveObjects", ctx, bucketName, path, recursive)
|
||||
ret := m.ctrl.Call(m, "RemoveObjects", ctx, instanceID, resourceOwner, objectType)
|
||||
ret0, _ := ret[0].(error)
|
||||
return ret0
|
||||
}
|
||||
|
||||
// RemoveObjects indicates an expected call of RemoveObjects.
|
||||
func (mr *MockStorageMockRecorder) RemoveObjects(ctx, bucketName, path, recursive interface{}) *gomock.Call {
|
||||
func (mr *MockStorageMockRecorder) RemoveObjects(ctx, instanceID, resourceOwner, objectType interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObjects", reflect.TypeOf((*MockStorage)(nil).RemoveObjects), ctx, bucketName, path, recursive)
|
||||
}
|
||||
|
||||
// MockConfig is a mock of Config interface.
|
||||
type MockConfig struct {
|
||||
ctrl *gomock.Controller
|
||||
recorder *MockConfigMockRecorder
|
||||
}
|
||||
|
||||
// MockConfigMockRecorder is the mock recorder for MockConfig.
|
||||
type MockConfigMockRecorder struct {
|
||||
mock *MockConfig
|
||||
}
|
||||
|
||||
// NewMockConfig creates a new mock instance.
|
||||
func NewMockConfig(ctrl *gomock.Controller) *MockConfig {
|
||||
mock := &MockConfig{ctrl: ctrl}
|
||||
mock.recorder = &MockConfigMockRecorder{mock}
|
||||
return mock
|
||||
}
|
||||
|
||||
// EXPECT returns an object that allows the caller to indicate expected use.
|
||||
func (m *MockConfig) EXPECT() *MockConfigMockRecorder {
|
||||
return m.recorder
|
||||
}
|
||||
|
||||
// NewStorage mocks base method.
|
||||
func (m *MockConfig) NewStorage() (static.Storage, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "NewStorage")
|
||||
ret0, _ := ret[0].(static.Storage)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// NewStorage indicates an expected call of NewStorage.
|
||||
func (mr *MockConfigMockRecorder) NewStorage() *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "NewStorage", reflect.TypeOf((*MockConfig)(nil).NewStorage))
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObjects", reflect.TypeOf((*MockStorage)(nil).RemoveObjects), ctx, instanceID, resourceOwner, objectType)
|
||||
}
|
||||
|
@ -1,34 +1,49 @@
|
||||
package mock
|
||||
|
||||
import (
|
||||
"context"
|
||||
"io"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/golang/mock/gomock"
|
||||
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
func NewStorage(t *testing.T) *MockStorage {
|
||||
return NewMockStorage(gomock.NewController(t))
|
||||
}
|
||||
|
||||
func (m *MockStorage) ExpectAddObjectNoError() *MockStorage {
|
||||
func (m *MockStorage) ExpectPutObject() *MockStorage {
|
||||
m.EXPECT().
|
||||
PutObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
Return(nil, nil)
|
||||
PutObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
DoAndReturn(func(ctx context.Context, instanceID, location, resourceOwner, name, contentType string, objectType static.ObjectType, object io.Reader, objectSize int64) (*static.Asset, error) {
|
||||
hash, _ := io.ReadAll(object)
|
||||
return &static.Asset{
|
||||
InstanceID: instanceID,
|
||||
Name: name,
|
||||
Hash: string(hash),
|
||||
Size: objectSize,
|
||||
LastModified: time.Now(),
|
||||
Location: location,
|
||||
ContentType: contentType,
|
||||
}, nil
|
||||
})
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockStorage) ExpectAddObjectError() *MockStorage {
|
||||
func (m *MockStorage) ExpectPutObjectError() *MockStorage {
|
||||
m.EXPECT().
|
||||
PutObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
PutObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
Return(nil, caos_errors.ThrowInternal(nil, "", ""))
|
||||
return m
|
||||
}
|
||||
|
||||
func (m *MockStorage) ExpectRemoveObjectNoError() *MockStorage {
|
||||
m.EXPECT().
|
||||
RemoveObject(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
RemoveObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
Return(nil)
|
||||
return m
|
||||
}
|
||||
@ -42,7 +57,7 @@ func (m *MockStorage) ExpectRemoveObjectsNoError() *MockStorage {
|
||||
|
||||
func (m *MockStorage) ExpectRemoveObjectError() *MockStorage {
|
||||
m.EXPECT().
|
||||
RemoveObject(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
RemoveObject(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
Return(caos_errors.ThrowInternal(nil, "", ""))
|
||||
return m
|
||||
}
|
||||
|
@ -1,9 +1,13 @@
|
||||
package s3
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"encoding/json"
|
||||
|
||||
"github.com/minio/minio-go/v7"
|
||||
"github.com/minio/minio-go/v7/pkg/credentials"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
@ -34,3 +38,15 @@ func (c *Config) NewStorage() (static.Storage, error) {
|
||||
MultiDelete: c.MultiDelete,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func NewStorage(_ *sql.DB, rawConfig map[string]interface{}) (static.Storage, error) {
|
||||
configData, err := json.Marshal(rawConfig)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "MINIO-Ef2f2", "could not map config")
|
||||
}
|
||||
c := new(Config)
|
||||
if err := json.Unmarshal(configData, c); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "MINIO-GB4nw", "could not map config")
|
||||
}
|
||||
return c.NewStorage()
|
||||
}
|
||||
|
@ -2,11 +2,10 @@ package s3
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/minio/minio-go/v7"
|
||||
@ -14,8 +13,11 @@ import (
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
var _ static.Storage = (*Minio)(nil)
|
||||
|
||||
type Minio struct {
|
||||
Client *minio.Client
|
||||
Location string
|
||||
@ -23,129 +25,66 @@ type Minio struct {
|
||||
MultiDelete bool
|
||||
}
|
||||
|
||||
func (m *Minio) CreateBucket(ctx context.Context, name, location string) error {
|
||||
if location == "" {
|
||||
location = m.Location
|
||||
func (m *Minio) PutObject(ctx context.Context, instanceID, location, resourceOwner, name, contentType string, objectType static.ObjectType, object io.Reader, objectSize int64) (*static.Asset, error) {
|
||||
err := m.createBucket(ctx, instanceID, location)
|
||||
if err != nil && !caos_errs.IsErrorAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
name = m.prefixBucketName(name)
|
||||
exists, err := m.Client.BucketExists(ctx, name)
|
||||
if err != nil {
|
||||
logging.LogWithFields("MINIO-ADvf3", "bucketname", name).WithError(err).Error("cannot check if bucket exists")
|
||||
return caos_errs.ThrowInternal(err, "MINIO-1b8fs", "Errors.Assets.Bucket.Internal")
|
||||
}
|
||||
if exists {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "MINIO-9n3MK", "Errors.Assets.Bucket.AlreadyExists")
|
||||
}
|
||||
err = m.Client.MakeBucket(ctx, name, minio.MakeBucketOptions{Region: location})
|
||||
if err != nil {
|
||||
return caos_errs.ThrowInternal(err, "MINIO-4m90d", "Errors.Assets.Bucket.CreateFailed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Minio) ListBuckets(ctx context.Context) ([]*domain.BucketInfo, error) {
|
||||
infos, err := m.Client.ListBuckets(ctx)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "MINIO-390OP", "Errors.Assets.Bucket.ListFailed")
|
||||
}
|
||||
buckets := make([]*domain.BucketInfo, len(infos))
|
||||
for i, info := range infos {
|
||||
buckets[i] = &domain.BucketInfo{
|
||||
Name: info.Name,
|
||||
CreationDate: info.CreationDate,
|
||||
}
|
||||
}
|
||||
return buckets, nil
|
||||
}
|
||||
|
||||
func (m *Minio) RemoveBucket(ctx context.Context, name string) error {
|
||||
name = m.prefixBucketName(name)
|
||||
err := m.Client.RemoveBucket(ctx, name)
|
||||
if err != nil {
|
||||
return caos_errs.ThrowInternal(err, "MINIO-338Hs", "Errors.Assets.Bucket.RemoveFailed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Minio) PutObject(ctx context.Context, bucketName, objectName, contentType string, object io.Reader, objectSize int64, createBucketIfNotExisting bool) (*domain.AssetInfo, error) {
|
||||
if createBucketIfNotExisting {
|
||||
err := m.CreateBucket(ctx, bucketName, "")
|
||||
if err != nil && !caos_errs.IsErrorAlreadyExists(err) {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
bucketName := m.prefixBucketName(instanceID)
|
||||
objectName := fmt.Sprintf("%s/%s", resourceOwner, name)
|
||||
info, err := m.Client.PutObject(ctx, bucketName, objectName, object, objectSize, minio.PutObjectOptions{ContentType: contentType})
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "MINIO-590sw", "Errors.Assets.Object.PutFailed")
|
||||
}
|
||||
return &domain.AssetInfo{
|
||||
Bucket: info.Bucket,
|
||||
Key: info.Key,
|
||||
ETag: info.ETag,
|
||||
Size: info.Size,
|
||||
LastModified: info.LastModified,
|
||||
Location: info.Location,
|
||||
VersionID: info.VersionID,
|
||||
return &static.Asset{
|
||||
InstanceID: info.Bucket,
|
||||
ResourceOwner: resourceOwner,
|
||||
Name: info.Key,
|
||||
Hash: info.ETag,
|
||||
Size: info.Size,
|
||||
LastModified: info.LastModified,
|
||||
Location: info.Location,
|
||||
ContentType: contentType,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (m *Minio) GetObjectInfo(ctx context.Context, bucketName, objectName string) (*domain.AssetInfo, error) {
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
objectinfo, err := m.Client.StatObject(ctx, bucketName, objectName, minio.StatObjectOptions{})
|
||||
func (m *Minio) GetObject(ctx context.Context, instanceID, resourceOwner, name string) ([]byte, func() (*static.Asset, error), error) {
|
||||
bucketName := m.prefixBucketName(instanceID)
|
||||
objectName := fmt.Sprintf("%s/%s", resourceOwner, name)
|
||||
object, err := m.Client.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, caos_errs.ThrowInternal(err, "MINIO-VGDgv", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
info := func() (*static.Asset, error) {
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "MINIO-F96xF", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
return m.objectToAssetInfo(instanceID, resourceOwner, info), nil
|
||||
}
|
||||
asset, err := io.ReadAll(object)
|
||||
if err != nil {
|
||||
return nil, nil, caos_errs.ThrowInternal(err, "MINIO-SFef1", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
return asset, info, nil
|
||||
}
|
||||
|
||||
func (m *Minio) GetObjectInfo(ctx context.Context, instanceID, resourceOwner, name string) (*static.Asset, error) {
|
||||
bucketName := m.prefixBucketName(instanceID)
|
||||
objectName := fmt.Sprintf("%s/%s", resourceOwner, name)
|
||||
objectInfo, err := m.Client.StatObject(ctx, bucketName, objectName, minio.StatObjectOptions{})
|
||||
if err != nil {
|
||||
if errResp := minio.ToErrorResponse(err); errResp.StatusCode == http.StatusNotFound {
|
||||
return nil, caos_errs.ThrowNotFound(err, "MINIO-Gdfh4", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
return nil, caos_errs.ThrowInternal(err, "MINIO-1vySX", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
return m.objectToAssetInfo(bucketName, objectinfo), nil
|
||||
return m.objectToAssetInfo(instanceID, resourceOwner, objectInfo), nil
|
||||
}
|
||||
|
||||
func (m *Minio) GetObject(ctx context.Context, bucketName, objectName string) (io.Reader, func() (*domain.AssetInfo, error), error) {
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
object, err := m.Client.GetObject(ctx, bucketName, objectName, minio.GetObjectOptions{})
|
||||
if err != nil {
|
||||
return nil, nil, caos_errs.ThrowInternal(err, "MINIO-VGDgv", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
info := func() (*domain.AssetInfo, error) {
|
||||
info, err := object.Stat()
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "MINIO-F96xF", "Errors.Assets.Object.GetFailed")
|
||||
}
|
||||
return m.objectToAssetInfo(bucketName, info), nil
|
||||
}
|
||||
return object, info, nil
|
||||
}
|
||||
|
||||
func (m *Minio) GetObjectPresignedURL(ctx context.Context, bucketName, objectName string, expiration time.Duration) (*url.URL, error) {
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
reqParams := make(url.Values)
|
||||
presignedURL, err := m.Client.PresignedGetObject(ctx, bucketName, objectName, expiration, reqParams)
|
||||
if err != nil {
|
||||
return nil, caos_errs.ThrowInternal(err, "MINIO-19Mp0", "Errors.Assets.Object.PresignedTokenFailed")
|
||||
}
|
||||
return presignedURL, nil
|
||||
}
|
||||
|
||||
func (m *Minio) ListObjectInfos(ctx context.Context, bucketName, prefix string, recursive bool) ([]*domain.AssetInfo, error) {
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
assetInfos := make([]*domain.AssetInfo, 0)
|
||||
|
||||
objects, cancel := m.listObjects(ctx, bucketName, prefix, recursive)
|
||||
defer cancel()
|
||||
for object := range objects {
|
||||
if object.Err != nil {
|
||||
logging.LogWithFields("MINIO-wC8sd", "bucket-name", bucketName, "prefix", prefix).WithError(object.Err).Debug("unable to get object")
|
||||
return nil, caos_errs.ThrowInternal(object.Err, "MINIO-1m09S", "Errors.Assets.Object.ListFailed")
|
||||
}
|
||||
assetInfos = append(assetInfos, m.objectToAssetInfo(bucketName, object))
|
||||
}
|
||||
return assetInfos, nil
|
||||
}
|
||||
|
||||
func (m *Minio) RemoveObject(ctx context.Context, bucketName, objectName string) error {
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
func (m *Minio) RemoveObject(ctx context.Context, instanceID, resourceOwner, name string) error {
|
||||
bucketName := m.prefixBucketName(instanceID)
|
||||
objectName := fmt.Sprintf("%s/%s", resourceOwner, name)
|
||||
err := m.Client.RemoveObject(ctx, bucketName, objectName, minio.RemoveObjectOptions{})
|
||||
if err != nil {
|
||||
return caos_errs.ThrowInternal(err, "MINIO-x85RT", "Errors.Assets.Object.RemoveFailed")
|
||||
@ -153,19 +92,27 @@ func (m *Minio) RemoveObject(ctx context.Context, bucketName, objectName string)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Minio) RemoveObjects(ctx context.Context, bucketName, path string, recursive bool) error {
|
||||
bucketName = m.prefixBucketName(bucketName)
|
||||
func (m *Minio) RemoveObjects(ctx context.Context, instanceID, resourceOwner string, objectType static.ObjectType) error {
|
||||
bucketName := m.prefixBucketName(instanceID)
|
||||
objectsCh := make(chan minio.ObjectInfo)
|
||||
g := new(errgroup.Group)
|
||||
|
||||
var path string
|
||||
switch objectType {
|
||||
case static.ObjectTypeStyling:
|
||||
path = domain.LabelPolicyPrefix + "/"
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
g.Go(func() error {
|
||||
defer close(objectsCh)
|
||||
objects, cancel := m.listObjects(ctx, bucketName, path, recursive)
|
||||
objects, cancel := m.listObjects(ctx, bucketName, resourceOwner, true)
|
||||
for object := range objects {
|
||||
if err := object.Err; err != nil {
|
||||
cancel()
|
||||
if errResp := minio.ToErrorResponse(err); errResp.StatusCode == http.StatusNotFound {
|
||||
logging.LogWithFields("MINIO-ss8va", "bucketName", bucketName, "path", path).Warn("list objects for remove failed with not found")
|
||||
logging.WithFields("bucketName", bucketName, "path", path).Warn("list objects for remove failed with not found")
|
||||
continue
|
||||
}
|
||||
return caos_errs.ThrowInternal(object.Err, "MINIO-WQF32", "Errors.Assets.Object.ListFailed")
|
||||
@ -189,6 +136,26 @@ func (m *Minio) RemoveObjects(ctx context.Context, bucketName, path string, recu
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
func (m *Minio) createBucket(ctx context.Context, name, location string) error {
|
||||
if location == "" {
|
||||
location = m.Location
|
||||
}
|
||||
name = m.prefixBucketName(name)
|
||||
exists, err := m.Client.BucketExists(ctx, name)
|
||||
if err != nil {
|
||||
logging.WithFields("bucketname", name).WithError(err).Error("cannot check if bucket exists")
|
||||
return caos_errs.ThrowInternal(err, "MINIO-1b8fs", "Errors.Assets.Bucket.Internal")
|
||||
}
|
||||
if exists {
|
||||
return caos_errs.ThrowAlreadyExists(nil, "MINIO-9n3MK", "Errors.Assets.Bucket.AlreadyExists")
|
||||
}
|
||||
err = m.Client.MakeBucket(ctx, name, minio.MakeBucketOptions{Region: location})
|
||||
if err != nil {
|
||||
return caos_errs.ThrowInternal(err, "MINIO-4m90d", "Errors.Assets.Bucket.CreateFailed")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Minio) listObjects(ctx context.Context, bucketName, prefix string, recursive bool) (<-chan minio.ObjectInfo, context.CancelFunc) {
|
||||
ctxCancel, cancel := context.WithCancel(ctx)
|
||||
|
||||
@ -198,17 +165,15 @@ func (m *Minio) listObjects(ctx context.Context, bucketName, prefix string, recu
|
||||
}), cancel
|
||||
}
|
||||
|
||||
func (m *Minio) objectToAssetInfo(bucketName string, object minio.ObjectInfo) *domain.AssetInfo {
|
||||
return &domain.AssetInfo{
|
||||
Bucket: bucketName,
|
||||
Key: object.Key,
|
||||
ETag: object.ETag,
|
||||
Size: object.Size,
|
||||
LastModified: object.LastModified,
|
||||
VersionID: object.VersionID,
|
||||
Expiration: object.Expires,
|
||||
ContentType: object.ContentType,
|
||||
AutheticatedURL: m.Client.EndpointURL().String() + "/" + bucketName + "/" + object.Key,
|
||||
func (m *Minio) objectToAssetInfo(bucketName string, resourceOwner string, object minio.ObjectInfo) *static.Asset {
|
||||
return &static.Asset{
|
||||
InstanceID: bucketName,
|
||||
ResourceOwner: resourceOwner,
|
||||
Name: object.Key,
|
||||
Hash: object.ETag,
|
||||
Size: object.Size,
|
||||
LastModified: object.LastModified,
|
||||
ContentType: object.ContentType,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,25 +2,36 @@ package static
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
"io"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
)
|
||||
|
||||
type CreateStorage func(client *sql.DB, rawConfig map[string]interface{}) (Storage, error)
|
||||
|
||||
type Storage interface {
|
||||
CreateBucket(ctx context.Context, name, location string) error
|
||||
RemoveBucket(ctx context.Context, name string) error
|
||||
ListBuckets(ctx context.Context) ([]*domain.BucketInfo, error)
|
||||
PutObject(ctx context.Context, bucketName, objectName, contentType string, object io.Reader, objectSize int64, createBucketIfNotExisting bool) (*domain.AssetInfo, error)
|
||||
GetObjectInfo(ctx context.Context, bucketName, objectName string) (*domain.AssetInfo, error)
|
||||
GetObject(ctx context.Context, bucketName, objectName string) (io.Reader, func() (*domain.AssetInfo, error), error)
|
||||
ListObjectInfos(ctx context.Context, bucketName, prefix string, recursive bool) ([]*domain.AssetInfo, error)
|
||||
GetObjectPresignedURL(ctx context.Context, bucketName, objectName string, expiration time.Duration) (*url.URL, error)
|
||||
RemoveObject(ctx context.Context, bucketName, objectName string) error
|
||||
RemoveObjects(ctx context.Context, bucketName, path string, recursive bool) error
|
||||
PutObject(ctx context.Context, instanceID, location, resourceOwner, name, contentType string, objectType ObjectType, object io.Reader, objectSize int64) (*Asset, error)
|
||||
GetObject(ctx context.Context, instanceID, resourceOwner, name string) ([]byte, func() (*Asset, error), error)
|
||||
GetObjectInfo(ctx context.Context, instanceID, resourceOwner, name string) (*Asset, error)
|
||||
RemoveObject(ctx context.Context, instanceID, resourceOwner, name string) error
|
||||
RemoveObjects(ctx context.Context, instanceID, resourceOwner string, objectType ObjectType) error
|
||||
//TODO: add functionality to move asset location
|
||||
}
|
||||
type Config interface {
|
||||
NewStorage() (Storage, error)
|
||||
|
||||
type ObjectType int32
|
||||
|
||||
const (
|
||||
ObjectTypeUserAvatar = iota
|
||||
ObjectTypeStyling
|
||||
)
|
||||
|
||||
type Asset struct {
|
||||
InstanceID string
|
||||
ResourceOwner string
|
||||
Name string
|
||||
Hash string
|
||||
Size int64
|
||||
LastModified time.Time
|
||||
Location string
|
||||
ContentType string
|
||||
}
|
||||
|
@ -1,8 +1,6 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/url"
|
||||
"time"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
@ -11,7 +9,6 @@ import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
"github.com/caos/zitadel/internal/static"
|
||||
)
|
||||
|
||||
type UserView struct {
|
||||
@ -41,7 +38,6 @@ type HumanView struct {
|
||||
DisplayName string
|
||||
AvatarKey string
|
||||
AvatarURL string
|
||||
PreSignedAvatar *url.URL
|
||||
PreferredLanguage string
|
||||
Gender Gender
|
||||
Email string
|
||||
@ -257,23 +253,6 @@ func (u *UserView) GetProfile() (*Profile, error) {
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (u *UserView) FillUserAvatar(ctx context.Context, static static.Storage, expiration time.Duration) error {
|
||||
if u.HumanView == nil {
|
||||
return errors.ThrowPreconditionFailed(nil, "MODEL-2k8da", "Errors.User.NotHuman")
|
||||
}
|
||||
if static != nil {
|
||||
if ctx == nil {
|
||||
ctx = context.Background()
|
||||
}
|
||||
presignesAvatarURL, err := static.GetObjectPresignedURL(ctx, u.ResourceOwner, u.AvatarKey, expiration)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.PreSignedAvatar = presignesAvatarURL
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserView) GetPhone() (*Phone, error) {
|
||||
if u.HumanView == nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "MODEL-him4a", "Errors.User.NotHuman")
|
||||
|
Loading…
x
Reference in New Issue
Block a user