fix: label policy (#1828)

* fix: font color

* fix: assets from iam when policy is default

* fix: remove multiple files

* fix mock storage

* doc: add asset api

* build assets docs

* fix operator test

* docs

* fix remove assets from org label policy and not default

* fix remove label policy assets and feature downgrade correctly

* fix storage mock

* Update docs/docs/apis/apis.md

Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
Co-authored-by: Fabi <38692350+fgerschwiler@users.noreply.github.com>
This commit is contained in:
Livio Amstutz 2021-06-08 09:48:44 +02:00 committed by GitHub
parent 0ed722395e
commit 81974b977d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 641 additions and 162 deletions

View File

@ -140,7 +140,8 @@ COPY internal/errors internal/errors
RUN build/zitadel/generate-grpc.sh \
&& go generate openapi/statik/generate.go \
&& go run internal/api/assets/generator/asset_generator.go -directory=internal/api/assets/generator/
&& mkdir -p docs/apis/assets/ \
&& go run internal/api/assets/generator/asset_generator.go -directory=internal/api/assets/generator/ -assets=docs/apis/assets/assets.md
#######################
@ -166,6 +167,7 @@ COPY --from=go-stub /go/src/github.com/caos/zitadel/openapi/statik/statik.go ope
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption/templates.gen.go internal/protoc/protoc-gen-authoption/templates.gen.go
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/protoc/protoc-gen-authoption/authoption/options.pb.go internal/protoc/protoc-gen-authoption/authoption/options.pb.go
COPY --from=go-stub /go/src/github.com/caos/zitadel/docs/apis/proto docs/docs/apis/proto
COPY --from=go-stub /go/src/github.com/caos/zitadel/docs/apis/assets docs/docs/apis/assets
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/api/assets/authz.go ./internal/api/assets/authz.go
COPY --from=go-stub /go/src/github.com/caos/zitadel/internal/api/assets/router.go ./internal/api/assets/router.go

View File

@ -20,6 +20,7 @@ AssetStorage:
SSL: $ZITADEL_ASSET_STORAGE_SSL
Location: $ZITADEL_ASSET_STORAGE_LOCATION
BucketPrefix: $ZITADEL_ASSET_STORAGE_BUCKET_PREFIX
MultiDelete: $ZITADEL_ASSET_STORAGE_MULTI_DELETE
Metrics:
Type: 'otel'

View File

@ -1,3 +0,0 @@
---
title: Administration
---

View File

@ -50,3 +50,13 @@ This API is intended to configure and manage the IAM itself.
| GRPC | [https://api.zitadel.ch/caos.zitadel.admin.api.v1.AdminService/](https://api.zitadel.ch/caos.zitadel.admin.api.v1.AdminService) |
[Latest API Version](https://github.com/caos/zitadel/blob/main/proto/zitadel/admin.proto)
## Assets API
The Assets API allows you to up- and download all kinds of assets. This can be files such as logos, fonts or user avatar.
| Service | URI |
|:--------|:--------------------------------------------------------------------------------------------------------------------------------|
| REST | [https://api.zitadel.ch/assets/v1/](https://api.zitadel.ch/assets/v1/) |

259
docs/docs/apis/assets/assets.md Executable file
View File

@ -0,0 +1,259 @@
---
title: zitadel/assets
---
## AssetsService
### UploadDefaultLabelPolicyFont()
> UploadDefaultLabelPolicyFont()
POST: /iam/policy/label/font
### GetDefaultLabelPolicyFont()
> GetDefaultLabelPolicyFont()
GET: /iam/policy/label/font
### GetPreviewDefaultLabelPolicyFont()
> GetPreviewDefaultLabelPolicyFont()
GET: /iam/policy/label/font/_preview
### UploadDefaultLabelPolicyIcon()
> UploadDefaultLabelPolicyIcon()
POST: /iam/policy/label/icon
### UploadDefaultLabelPolicyIcon()
> UploadDefaultLabelPolicyIconDark()
POST: /iam/policy/label/icon/dark
### GetDefaultLabelPolicyIcon()
> GetDefaultLabelPolicyIcon()
GET: /iam/policy/label/icon
### GetDefaultLabelPolicyIcon()
> GetDefaultLabelPolicyIconDark()
GET: /iam/policy/label/icon/dark
### GetPreviewDefaultLabelPolicyIcon()
> GetPreviewDefaultLabelPolicyIcon()
GET: /iam/policy/label/icon/_preview
### GetPreviewDefaultLabelPolicyIcon()
> GetPreviewDefaultLabelPolicyIconDark()
GET: /iam/policy/label/icon/dark/_preview
### UploadDefaultLabelPolicyLogo()
> UploadDefaultLabelPolicyLogo()
POST: /iam/policy/label/logo
### UploadDefaultLabelPolicyLogo()
> UploadDefaultLabelPolicyLogoDark()
POST: /iam/policy/label/logo/dark
### GetDefaultLabelPolicyLogo()
> GetDefaultLabelPolicyLogo()
GET: /iam/policy/label/logo
### GetDefaultLabelPolicyLogo()
> GetDefaultLabelPolicyLogoDark()
GET: /iam/policy/label/logo/dark
### GetPreviewDefaultLabelPolicyLogo()
> GetPreviewDefaultLabelPolicyLogo()
GET: /iam/policy/label/logo/_preview
### GetPreviewDefaultLabelPolicyLogo()
> GetPreviewDefaultLabelPolicyLogoDark()
GET: /iam/policy/label/logo/dark/_preview
### UploadOrgLabelPolicyFont()
> UploadOrgLabelPolicyFont()
POST: /org/policy/label/font
### GetOrgLabelPolicyFont()
> GetOrgLabelPolicyFont()
GET: /org/policy/label/font
### GetPreviewOrgLabelPolicyFont()
> GetPreviewOrgLabelPolicyFont()
GET: /org/policy/label/font/_preview
### UploadOrgLabelPolicyIcon()
> UploadOrgLabelPolicyIcon()
POST: /org/policy/label/icon
### UploadOrgLabelPolicyIcon()
> UploadOrgLabelPolicyIconDark()
POST: /org/policy/label/icon/dark
### GetOrgLabelPolicyIcon()
> GetOrgLabelPolicyIcon()
GET: /org/policy/label/icon
### GetOrgLabelPolicyIcon()
> GetOrgLabelPolicyIconDark()
GET: /org/policy/label/icon/dark
### GetPreviewOrgLabelPolicyIcon()
> GetPreviewOrgLabelPolicyIcon()
GET: /org/policy/label/icon/_preview
### GetPreviewOrgLabelPolicyIcon()
> GetPreviewOrgLabelPolicyIconDark()
GET: /org/policy/label/icon/dark/_preview
### UploadOrgLabelPolicyLogo()
> UploadOrgLabelPolicyLogo()
POST: /org/policy/label/logo
### UploadOrgLabelPolicyLogo()
> UploadOrgLabelPolicyLogoDark()
POST: /org/policy/label/logo/dark
### GetOrgLabelPolicyLogo()
> GetOrgLabelPolicyLogo()
GET: /org/policy/label/logo
### GetOrgLabelPolicyLogo()
> GetOrgLabelPolicyLogoDark()
GET: /org/policy/label/logo/dark
### GetPreviewOrgLabelPolicyLogo()
> GetPreviewOrgLabelPolicyLogo()
GET: /org/policy/label/logo/_preview
### GetPreviewOrgLabelPolicyLogo()
> GetPreviewOrgLabelPolicyLogoDark()
GET: /org/policy/label/logo/dark/_preview
### UploadMyUserAvatar()
> UploadMyUserAvatar()
POST: /users/me/avatar
### GetMyUserAvatar()
> GetMyUserAvatar()
GET: /users/me/avatar

View File

@ -1,3 +0,0 @@
---
title: Authentication
---

View File

@ -8,10 +8,11 @@ title: Introduction
---
ZITADEL provides three API's for different use cases. These API's are built with GRPC and then generate a REST service.
ZITADEL provides four API's for different use cases. Three API's are built with GRPC and then generate a REST service.
Each service's proto definition is located in the source control on GitHub.
As we generate the REST services and Swagger file out of the proto definition we recommend that you rely on the proto file.
We annotate the corresponding REST methods on each possible call as well as the AuthN and AuthZ requirements.
The last API (assets) is only a REST API because we use multipart form data.
See below for an example with the call **GetMyUser**.

View File

@ -1,3 +0,0 @@
---
title: Management
---

View File

@ -109,6 +109,14 @@ module.exports = {
'apis/proto/options',
],
},
{
type: 'category',
label: 'Assets API Definition',
collapsed: false,
items: [
'apis/assets/assets',
],
},
{
type: 'category',
label: 'OpenID Connect & OAuth',

1
go.mod
View File

@ -69,6 +69,7 @@ require (
go.opentelemetry.io/otel/exporters/stdout v0.13.0
go.opentelemetry.io/otel/sdk v0.13.0
golang.org/x/crypto v0.0.0-20210322153248-0c34fe9e7dc2
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
golang.org/x/text v0.3.6
golang.org/x/tools v0.1.0
google.golang.org/api v0.34.0

2
go.sum
View File

@ -1118,6 +1118,8 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9 h1:SQFwaSi55rU7vdNs9Yr0Z324VNlrF+0wMqRXT4St8ck=
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=

View File

@ -174,6 +174,12 @@ func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64
cssContent += fmt.Sprintf("--zitadel-color-warn-%v: %s;", i, color)
}
}
if policy.FontColor != "" {
palette := m.generateColorPaletteRGBA255(policy.FontColor)
for i, color := range palette {
cssContent += fmt.Sprintf("--zitadel-color-text-%v: %s;", i, color)
}
}
var fontname string
if policy.FontURL != "" {
split := strings.Split(policy.FontURL, "/")
@ -206,7 +212,7 @@ func (m *Styling) writeFile(policy *iam_model.LabelPolicyView) (io.Reader, int64
if policy.FontColorDark != "" {
palette := m.generateColorPaletteRGBA255(policy.FontColorDark)
for i, color := range palette {
cssContent += fmt.Sprintf("--zitadel-color-font-%v: %s;", i, color)
cssContent += fmt.Sprintf("--zitadel-color-text-%v: %s;", i, color)
}
}
cssContent += fmt.Sprint("}")

View File

@ -123,7 +123,7 @@ func UploadHandleFunc(s AssetsService, uploader Uploader) func(http.ResponseWrit
contentType := handler.Header.Get("content-type")
size := handler.Size
if !uploader.ContentTypeAllowed(contentType) {
s.ErrorHandler()(w, r, caos_errs.ThrowInvalidArgument(nil, "UPLOAD-Dbvfs", "invalid content-type"))
s.ErrorHandler()(w, r, caos_errs.ThrowInvalidArgumentf(nil, "UPLOAD-Dbvfs", "invalid content-type: %s", contentType))
return
}
if size > uploader.MaxFileSize() {

View File

@ -13,6 +13,7 @@ import (
var (
directory = flag.String("directory", "./", "working directory: asset.yaml must be in this directory, files will be generated into parent directory")
assets = flag.String("assets", "../../../../docs/docs/apis/assets/assets.md", "path where the assets.md will be generated")
)
func main() {
@ -22,7 +23,9 @@ func main() {
logging.Log("ASSETS-Gn31f").OnError(err).Fatal("cannot open authz file")
router, err := os.OpenFile(*directory+"../router.go", os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0755)
logging.Log("ASSETS-ABen3").OnError(err).Fatal("cannot open router file")
GenerateAssetHandler(configFile, authz, router)
docs, err := os.OpenFile(*assets, os.O_TRUNC|os.O_WRONLY|os.O_CREATE, 0755)
logging.Log("ASSETS-Dfvsd").OnError(err).Fatal("cannot open docs file")
GenerateAssetHandler(configFile, authz, router, docs)
}
type Method struct {
@ -94,7 +97,7 @@ type Service struct {
Methods map[string]Method
}
func GenerateAssetHandler(configFilePath string, output io.Writer, output2 io.Writer) {
func GenerateAssetHandler(configFilePath string, authz, router, docs io.Writer) {
conf := new(struct {
Services Services
})
@ -104,6 +107,8 @@ func GenerateAssetHandler(configFilePath string, output io.Writer, output2 io.Wr
logging.Log("ASSETS-BGbbg").OnError(err).Fatal("cannot parse authz template")
tmplRouter, err := template.New("").Parse(routerTmpl)
logging.Log("ASSETS-gh4rq").OnError(err).Fatal("cannot parse router template")
tmplDocs, err := template.New("").Parse(docsTmpl)
logging.Log("ASSETS-FGdgs").OnError(err).Fatal("cannot parse docs template")
data := &struct {
GoPkgName string
Name string
@ -115,10 +120,12 @@ func GenerateAssetHandler(configFilePath string, output io.Writer, output2 io.Wr
Prefix: "/assets/v1",
Services: conf.Services,
}
err = tmplAuthz.Execute(output, data)
err = tmplAuthz.Execute(authz, data)
logging.Log("ASSETS-BHngj").OnError(err).Fatal("cannot generate authz")
err = tmplRouter.Execute(output2, data)
err = tmplRouter.Execute(router, data)
logging.Log("ASSETS-Bfd41").OnError(err).Fatal("cannot generate router")
err = tmplDocs.Execute(docs, data)
logging.Log("ASSETS-Bfd41").OnError(err).Fatal("cannot generate docs")
}
const authzTmpl = `package {{.GoPkgName}}
@ -198,3 +205,30 @@ func RegisterRoutes(router *mux.Router, s {{.Name}}) {
{{ end }}
}
`
const docsTmpl = `---
title: zitadel/admin.proto
---
## {{.Name}}
{{ range $service := .Services}}
{{ range $methodName, $method := .Methods}}
{{ range $handler := .Handlers}}
### {{$handler.Name}}{{$methodName}}()
> {{$handler.Name}}{{$methodName}}()
{{$handler.Method}}: {{$service.Prefix}}{{$method.Path}}{{$handler.PathSuffix}}
{{ if $method.HasDarkMode }}
### {{$handler.Name}}{{$methodName}}()
> {{$handler.Name}}{{$methodName}}Dark()
{{$handler.Method}}: {{$service.Prefix}}{{$method.Path}}/dark{{$handler.PathSuffix}}
{{ end }}
{{ end }}
{{ end }}
{{ end }}
`

View File

@ -136,10 +136,7 @@ func (l *labelPolicyLogoDownloader) ObjectName(ctx context.Context, path string)
}
func (l *labelPolicyLogoDownloader) BucketName(ctx context.Context, id string) string {
if l.defaultPolicy {
return domain.IAMID
}
return authz.GetCtxData(ctx).OrgID
return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.org)
}
func (h *Handler) UploadDefaultLabelPolicyIcon() Uploader {
@ -267,10 +264,7 @@ func (l *labelPolicyIconDownloader) ObjectName(ctx context.Context, path string)
}
func (l *labelPolicyIconDownloader) BucketName(ctx context.Context, id string) string {
if l.defaultPolicy {
return domain.IAMID
}
return authz.GetCtxData(ctx).OrgID
return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.org)
}
func (h *Handler) UploadDefaultLabelPolicyFont() Uploader {
@ -357,10 +351,7 @@ func (l *labelPolicyFontDownloader) ObjectName(ctx context.Context, path string)
}
func (l *labelPolicyFontDownloader) BucketName(ctx context.Context, id string) string {
if l.defaultPolicy {
return domain.IAMID
}
return authz.GetCtxData(ctx).OrgID
return getLabelPolicyBucketName(ctx, l.defaultPolicy, l.preview, l.org)
}
func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, orgRepo repository.OrgRepository) (*model.LabelPolicyView, error) {
@ -375,3 +366,17 @@ func getLabelPolicy(ctx context.Context, defaultPolicy, preview bool, orgRepo re
}
return orgRepo.GetLabelPolicy(ctx)
}
func getLabelPolicyBucketName(ctx context.Context, defaultPolicy, preview bool, org repository.OrgRepository) string {
if defaultPolicy {
return domain.IAMID
}
policy, err := getLabelPolicy(ctx, defaultPolicy, preview, org)
if err != nil {
return ""
}
if policy.Default {
return domain.IAMID
}
return authz.GetCtxData(ctx).OrgID
}

View File

@ -86,7 +86,7 @@ func (s *Server) ResetLabelPolicyToDefault(ctx context.Context, req *mgmt_pb.Res
}
func (s *Server) RemoveCustomLabelPolicyLogo(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyLogoRequest) (*mgmt_pb.RemoveCustomLabelPolicyLogoResponse, error) {
policy, err := s.command.RemoveLogoDefaultLabelPolicy(ctx)
policy, err := s.command.RemoveLogoLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
@ -100,7 +100,7 @@ func (s *Server) RemoveCustomLabelPolicyLogo(ctx context.Context, req *mgmt_pb.R
}
func (s *Server) RemoveCustomLabelPolicyLogoDark(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyLogoDarkRequest) (*mgmt_pb.RemoveCustomLabelPolicyLogoDarkResponse, error) {
policy, err := s.command.RemoveLogoDarkDefaultLabelPolicy(ctx)
policy, err := s.command.RemoveLogoDarkLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
@ -114,7 +114,7 @@ func (s *Server) RemoveCustomLabelPolicyLogoDark(ctx context.Context, req *mgmt_
}
func (s *Server) RemoveCustomLabelPolicyIcon(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyIconRequest) (*mgmt_pb.RemoveCustomLabelPolicyIconResponse, error) {
policy, err := s.command.RemoveIconDefaultLabelPolicy(ctx)
policy, err := s.command.RemoveIconLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
@ -128,7 +128,7 @@ func (s *Server) RemoveCustomLabelPolicyIcon(ctx context.Context, req *mgmt_pb.R
}
func (s *Server) RemoveCustomLabelPolicyIconDark(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyIconDarkRequest) (*mgmt_pb.RemoveCustomLabelPolicyIconDarkResponse, error) {
policy, err := s.command.RemoveIconDarkDefaultLabelPolicy(ctx)
policy, err := s.command.RemoveIconDarkLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}
@ -142,7 +142,7 @@ func (s *Server) RemoveCustomLabelPolicyIconDark(ctx context.Context, req *mgmt_
}
func (s *Server) RemoveCustomLabelPolicyFont(ctx context.Context, req *mgmt_pb.RemoveCustomLabelPolicyFontRequest) (*mgmt_pb.RemoveCustomLabelPolicyFontResponse, error) {
policy, err := s.command.RemoveFontDefaultLabelPolicy(ctx)
policy, err := s.command.RemoveFontLabelPolicy(ctx, authz.GetCtxData(ctx).OrgID)
if err != nil {
return nil, err
}

View File

@ -98,6 +98,8 @@ func (m *LabelPolicy) processLabelPolicy(event *models.Event) (err error) {
return err
}
err = policy.AppendEvent(event)
case model.LabelPolicyRemoved:
return m.view.DeleteLabelPolicy(event.AggregateID, event)
default:
return m.view.ProcessedLabelPolicySequence(event)
}

View File

@ -279,12 +279,15 @@ func (c *Commands) setAllowedLabelPolicy(ctx context.Context, orgID string, feat
}
events = append(events, assetsEvent)
}
changedEvent, hasChanged := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel),
changedEvent, hasChangedEvent := existingPolicy.NewChangedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel),
policy.PrimaryColor, policy.BackgroundColor, policy.WarnColor, policy.FontColor,
policy.PrimaryColorDark, policy.BackgroundColorDark, policy.WarnColorDark, policy.FontColorDark,
policy.HideLoginNameSuffix, policy.ErrorMsgPopup, policy.HideLoginNameSuffix)
if hasChanged {
if hasChangedEvent {
events = append(events, changedEvent)
}
if len(events) > 0 {
events = append(events, org.NewLabelPolicyActivatedEvent(ctx, OrgAggregateFromWriteModel(&existingPolicy.WriteModel)))
}
return events, nil
}

View File

@ -958,7 +958,7 @@ func TestCommandSide_SetOrgFeatures(t *testing.T) {
),
),
iamDomain: "iam-domain",
static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectNoError(),
static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectsNoError(),
},
args: args{
ctx: context.Background(),

View File

@ -324,10 +324,6 @@ func (c *Commands) RemoveIconDarkLabelPolicy(ctx context.Context, orgID string)
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-3NFos", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.IconDarkKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyIconDarkRemovedEvent(ctx, orgAgg, existingPolicy.IconDarkKey))
if err != nil {
@ -379,10 +375,6 @@ func (c *Commands) RemoveFontLabelPolicy(ctx context.Context, orgID string) (*do
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "ORG-4n9SD", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, orgID, existingPolicy.FontKey)
if err != nil {
return nil, err
}
orgAgg := OrgAggregateFromWriteModel(&existingPolicy.LabelPolicyWriteModel.WriteModel)
pushedEvents, err := c.eventstore.PushEvents(ctx, org.NewLabelPolicyFontRemovedEvent(ctx, orgAgg, existingPolicy.FontKey))
if err != nil {
@ -423,7 +415,8 @@ func (c *Commands) removeLabelPolicy(ctx context.Context, existingPolicy *OrgLab
if existingPolicy.State == domain.PolicyStateUnspecified || existingPolicy.State == domain.PolicyStateRemoved {
return nil, caos_errs.ThrowNotFound(nil, "Org-3M9df", "Errors.Org.LabelPolicy.NotFound")
}
err = c.RemoveAsset(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix)
err = c.RemoveAssetsFolder(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix+"/", true)
if err != nil {
return nil, err
}
@ -439,7 +432,7 @@ func (c *Commands) removeLabelPolicyIfExists(ctx context.Context, orgID string)
if existingPolicy.State != domain.PolicyStateActive {
return nil, nil
}
err = c.RemoveAsset(ctx, orgID, domain.LabelPolicyPrefix)
err = c.RemoveAssetsFolder(ctx, orgID, domain.LabelPolicyPrefix+"/", true)
if err != nil {
return nil, err
}
@ -448,7 +441,7 @@ func (c *Commands) removeLabelPolicyIfExists(ctx context.Context, orgID string)
}
func (c *Commands) removeLabelPolicyAssets(ctx context.Context, existingPolicy *OrgLabelPolicyWriteModel) (*org.LabelPolicyAssetsRemovedEvent, error) {
err := c.RemoveAsset(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix)
err := c.RemoveAssetsFolder(ctx, existingPolicy.AggregateID, domain.LabelPolicyPrefix+"/", true)
if err != nil {
return nil, err
}

View File

@ -575,7 +575,7 @@ func TestCommandSide_RemoveLabelPolicy(t *testing.T) {
},
),
),
static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectNoError(),
static: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectsNoError(),
},
args: args{
ctx: context.Background(),
@ -1559,7 +1559,6 @@ func TestCommandSide_RemoveIconDarkLabelPolicy(t *testing.T) {
{
name: "icon dark added, ok",
fields: fields{
storage: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectNoError(),
eventstore: eventstoreExpect(
t,
expectFilter(
@ -1813,7 +1812,6 @@ func TestCommandSide_RemoveFontLabelPolicy(t *testing.T) {
{
name: "font added, ok",
fields: fields{
storage: mock.NewMockStorage(gomock.NewController(t)).ExpectRemoveObjectNoError(),
eventstore: eventstoreExpect(
t,
expectFilter(

View File

@ -25,3 +25,7 @@ func (c *Commands) UploadAsset(ctx context.Context, bucketName, objectName, cont
func (c *Commands) RemoveAsset(ctx context.Context, bucketName, storeKey string) error {
return c.static.RemoveObject(ctx, bucketName, storeKey)
}
func (c *Commands) RemoveAssetsFolder(ctx context.Context, bucketName, path string, recursive bool) error {
return c.static.RemoveObjects(ctx, bucketName, path, recursive)
}

View File

@ -4,6 +4,7 @@ import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore/v1"
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
@ -100,6 +101,8 @@ func (m *LabelPolicy) processLabelPolicy(event *es_models.Event) (err error) {
return err
}
err = policy.AppendEvent(event)
case model.LabelPolicyRemoved:
return m.view.DeleteLabelPolicy(event.AggregateID, event)
case iam_es_model.LabelPolicyActivated, model.LabelPolicyActivated:
policy, err = m.view.LabelPolicyByAggregateIDAndState(event.AggregateID, int32(domain.LabelPolicyStatePreview))
if err != nil {
@ -135,15 +138,21 @@ func (p *LabelPolicy) CleanUpBucket(policy *iam_model.LabelPolicyView) {
return
}
for _, object := range objects {
if !deleteableObject(object, policy) {
if !deletableObject(object, policy) {
continue
}
p.static.RemoveObject(ctx, policy.AggregateID, object.Key)
err = p.static.RemoveObject(ctx, policy.AggregateID, object.Key)
logging.LogWithFields("SPOOL-ASd3g", "aggregate", policy.AggregateID, "key", object.Key).OnError(err).Warn("could not delete asset")
}
}
func deleteableObject(object *domain.AssetInfo, policy *iam_model.LabelPolicyView) bool {
if object.Key == policy.LogoURL || object.Key == policy.LogoDarkURL || object.Key == policy.IconURL || object.Key == policy.IconDarkURL || object.Key == policy.FontURL {
func deletableObject(object *domain.AssetInfo, policy *iam_model.LabelPolicyView) bool {
if object.Key == policy.LogoURL ||
object.Key == policy.LogoDarkURL ||
object.Key == policy.IconURL ||
object.Key == policy.IconDarkURL ||
object.Key == policy.FontURL ||
object.Key == domain.LabelPolicyPrefix+"/css/" {
return false
}
return true

View File

@ -6,39 +6,40 @@ package mock
import (
context "context"
domain "github.com/caos/zitadel/internal/domain"
static "github.com/caos/zitadel/internal/static"
gomock "github.com/golang/mock/gomock"
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"
)
// MockStorage is a mock of Storage interface
// MockStorage is a mock of Storage interface.
type MockStorage struct {
ctrl *gomock.Controller
recorder *MockStorageMockRecorder
}
// MockStorageMockRecorder is the mock recorder for MockStorage
// MockStorageMockRecorder is the mock recorder for MockStorage.
type MockStorageMockRecorder struct {
mock *MockStorage
}
// NewMockStorage creates a new mock instance
// NewMockStorage creates a new mock instance.
func NewMockStorage(ctrl *gomock.Controller) *MockStorage {
mock := &MockStorage{ctrl: ctrl}
mock.recorder = &MockStorageMockRecorder{mock}
return mock
}
// EXPECT returns an object that allows the caller to indicate expected use
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockStorage) EXPECT() *MockStorageMockRecorder {
return m.recorder
}
// CreateBucket mocks base method
// 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)
@ -46,72 +47,13 @@ func (m *MockStorage) CreateBucket(ctx context.Context, name, location string) e
return ret0
}
// CreateBucket indicates an expected call of CreateBucket
// 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)
}
// 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)
}
// 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)
}
// 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) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PutObject", ctx, bucketName, objectName, contentType, object, objectSize, createBucketIfNotExisting)
ret0, _ := ret[0].(*domain.AssetInfo)
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 {
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)
}
// GetObjectInfo mocks base method
func (m *MockStorage) GetObjectInfo(ctx context.Context, bucketName, objectName string) (*domain.AssetInfo, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetObjectInfo", ctx, bucketName, objectName)
ret0, _ := ret[0].(*domain.AssetInfo)
ret1, _ := ret[1].(error)
return ret0, ret1
}
// GetObjectInfo indicates an expected call of GetObjectInfo
func (mr *MockStorageMockRecorder) GetObjectInfo(ctx, bucketName, objectName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObjectInfo", reflect.TypeOf((*MockStorage)(nil).GetObjectInfo), ctx, bucketName, objectName)
}
// GetObject mocks base method
// GetObject mocks base method.
func (m *MockStorage) GetObject(ctx context.Context, bucketName, objectName string) (io.Reader, func() (*domain.AssetInfo, error), error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "GetObject", ctx, bucketName, objectName)
@ -121,28 +63,28 @@ func (m *MockStorage) GetObject(ctx context.Context, bucketName, objectName stri
return ret0, ret1, ret2
}
// GetObject indicates an expected call of GetObject
// GetObject indicates an expected call of GetObject.
func (mr *MockStorageMockRecorder) GetObject(ctx, bucketName, objectName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "GetObject", reflect.TypeOf((*MockStorage)(nil).GetObject), ctx, bucketName, objectName)
}
// ListObjectInfos mocks base method
func (m *MockStorage) ListObjectInfos(ctx context.Context, bucketName, prefix string, recursive bool) ([]*domain.AssetInfo, error) {
// GetObjectInfo mocks base method.
func (m *MockStorage) GetObjectInfo(ctx context.Context, bucketName, objectName string) (*domain.AssetInfo, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "ListObjectInfos", ctx, bucketName, prefix, recursive)
ret0, _ := ret[0].([]*domain.AssetInfo)
ret := m.ctrl.Call(m, "GetObjectInfo", ctx, bucketName, objectName)
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 {
// GetObjectInfo indicates an expected call of GetObjectInfo.
func (mr *MockStorageMockRecorder) GetObjectInfo(ctx, bucketName, objectName 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, bucketName, objectName)
}
// GetObjectPresignedURL mocks base method
// 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)
@ -151,13 +93,72 @@ func (m *MockStorage) GetObjectPresignedURL(ctx context.Context, bucketName, obj
return ret0, ret1
}
// GetObjectPresignedURL indicates an expected call of GetObjectPresignedURL
// 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)
}
// RemoveObject mocks base method
// 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)
}
// 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) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "PutObject", ctx, bucketName, objectName, contentType, object, objectSize, createBucketIfNotExisting)
ret0, _ := ret[0].(*domain.AssetInfo)
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 {
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)
}
// RemoveObject mocks base method.
func (m *MockStorage) RemoveObject(ctx context.Context, bucketName, objectName string) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RemoveObject", ctx, bucketName, objectName)
@ -165,36 +166,50 @@ func (m *MockStorage) RemoveObject(ctx context.Context, bucketName, objectName s
return ret0
}
// RemoveObject indicates an expected call of RemoveObject
// RemoveObject indicates an expected call of RemoveObject.
func (mr *MockStorageMockRecorder) RemoveObject(ctx, bucketName, objectName interface{}) *gomock.Call {
mr.mock.ctrl.T.Helper()
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "RemoveObject", reflect.TypeOf((*MockStorage)(nil).RemoveObject), ctx, bucketName, objectName)
}
// MockConfig is a mock of Config interface
// RemoveObjects mocks base method.
func (m *MockStorage) RemoveObjects(ctx context.Context, bucketName, path string, recursive bool) error {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "RemoveObjects", ctx, bucketName, path, recursive)
ret0, _ := ret[0].(error)
return ret0
}
// RemoveObjects indicates an expected call of RemoveObjects.
func (mr *MockStorageMockRecorder) RemoveObjects(ctx, bucketName, path, recursive 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
// MockConfigMockRecorder is the mock recorder for MockConfig.
type MockConfigMockRecorder struct {
mock *MockConfig
}
// NewMockConfig creates a new mock instance
// 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
// EXPECT returns an object that allows the caller to indicate expected use.
func (m *MockConfig) EXPECT() *MockConfigMockRecorder {
return m.recorder
}
// NewStorage mocks base method
// NewStorage mocks base method.
func (m *MockConfig) NewStorage() (static.Storage, error) {
m.ctrl.T.Helper()
ret := m.ctrl.Call(m, "NewStorage")
@ -203,7 +218,7 @@ func (m *MockConfig) NewStorage() (static.Storage, error) {
return ret0, ret1
}
// NewStorage indicates an expected call of NewStorage
// 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))

View File

@ -33,6 +33,13 @@ func (m *MockStorage) ExpectRemoveObjectNoError() *MockStorage {
return m
}
func (m *MockStorage) ExpectRemoveObjectsNoError() *MockStorage {
m.EXPECT().
RemoveObjects(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).
Return(nil)
return m
}
func (m *MockStorage) ExpectRemoveObjectError() *MockStorage {
m.EXPECT().
RemoveObject(gomock.Any(), gomock.Any(), gomock.Any()).

View File

@ -15,6 +15,7 @@ type Config struct {
SSL bool
Location string
BucketPrefix string
MultiDelete bool
}
func (c *Config) NewStorage() (static.Storage, error) {
@ -30,5 +31,6 @@ func (c *Config) NewStorage() (static.Storage, error) {
Client: minioClient,
Location: c.Location,
BucketPrefix: c.BucketPrefix,
MultiDelete: c.MultiDelete,
}, nil
}

View File

@ -10,6 +10,7 @@ import (
"github.com/caos/logging"
"github.com/minio/minio-go/v7"
"golang.org/x/sync/errgroup"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
@ -19,6 +20,7 @@ type Minio struct {
Client *minio.Client
Location string
BucketPrefix string
MultiDelete bool
}
func (m *Minio) CreateBucket(ctx context.Context, name, location string) error {
@ -128,15 +130,11 @@ func (m *Minio) GetObjectPresignedURL(ctx context.Context, bucketName, objectNam
func (m *Minio) ListObjectInfos(ctx context.Context, bucketName, prefix string, recursive bool) ([]*domain.AssetInfo, error) {
bucketName = m.prefixBucketName(bucketName)
ctx, cancel := context.WithCancel(ctx)
defer cancel()
objectCh := m.Client.ListObjects(ctx, bucketName, minio.ListObjectsOptions{
Prefix: prefix,
Recursive: recursive,
})
assetInfos := make([]*domain.AssetInfo, 0)
for object := range objectCh {
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")
@ -155,6 +153,47 @@ 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)
objectsCh := make(chan minio.ObjectInfo)
g := new(errgroup.Group)
g.Go(func() error {
defer close(objectsCh)
objects, cancel := m.listObjects(ctx, bucketName, path, recursive)
for object := range objects {
if object.Err != nil {
cancel()
return caos_errs.ThrowInternal(object.Err, "MINIO-WQF32", "Errors.Assets.Object.ListFailed")
}
objectsCh <- object
}
return nil
})
if m.MultiDelete {
for objError := range m.Client.RemoveObjects(ctx, bucketName, objectsCh, minio.RemoveObjectsOptions{GovernanceBypass: true}) {
return caos_errs.ThrowInternal(objError.Err, "MINIO-Sfdgr", "Errors.Assets.Object.RemoveFailed")
}
return g.Wait()
}
for objectInfo := range objectsCh {
if err := m.Client.RemoveObject(ctx, bucketName, objectInfo.Key, minio.RemoveObjectOptions{GovernanceBypass: true}); err != nil {
return caos_errs.ThrowInternal(err, "MINIO-GVgew", "Errors.Assets.Object.RemoveFailed")
}
}
return g.Wait()
}
func (m *Minio) listObjects(ctx context.Context, bucketName, prefix string, recursive bool) (<-chan minio.ObjectInfo, context.CancelFunc) {
ctxCancel, cancel := context.WithCancel(ctx)
return m.Client.ListObjects(ctxCancel, bucketName, minio.ListObjectsOptions{
Prefix: prefix,
Recursive: recursive,
}), cancel
}
func (m *Minio) objectToAssetInfo(bucketName string, object minio.ObjectInfo) *domain.AssetInfo {
return &domain.AssetInfo{
Bucket: bucketName,

View File

@ -19,6 +19,7 @@ type Storage interface {
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
}
type Config interface {
NewStorage() (Storage, error)

View File

@ -3,6 +3,7 @@
--zitadel-color-background: var(--zitadel-color-background-500);
--zitadel-color-secondary: var(--zitadel-color-secondary-500);
--zitadel-color-warn: var(--zitadel-color-warn-500);
--zitadel-color-text: var(--zitadel-color-text-500);
--zitadel-color-primary-50: #eaedfa;
--zitadel-color-primary-100: #ccd2f2;
@ -42,7 +43,18 @@
--zitadel-font-family: 'Lato';
--zitadel-color-background-500: var(--zitadel-color-grey-50);
--zitadel-color-background-50: rgb(255, 255, 255);
--zitadel-color-background-100: rgb(255, 255, 255);
--zitadel-color-background-200: rgb(255, 255, 255);
--zitadel-color-background-300: rgb(255, 255, 255);
--zitadel-color-background-400: rgb(255, 255, 255);
--zitadel-color-background-500: rgb(250, 250, 250);
--zitadel-color-background-600: rgb(222, 222, 222);
--zitadel-color-background-700: rgb(195, 195, 194);
--zitadel-color-background-800: rgb(168, 168, 168);
--zitadel-color-background-900: rgb(142, 142, 142);
--zitadel-color-background-contrast: rgb(0, 0, 0);
--zitadel-color-footer-line: #e3e8ee;
--zitadel-color-input-background: #fafafa50;
@ -50,7 +62,17 @@
--zitadel-color-input-border-hover: #1a1b1b;
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
--zitadel-color-text: rgba(0, 0, 0, 0.87);
--zitadel-color-font-50: rgb(0, 0, 0);
--zitadel-color-font-100: rgb(0, 0, 0);
--zitadel-color-font-200: rgb(0, 0, 0);
--zitadel-color-font-300: rgb(0, 0, 0);
--zitadel-color-font-400: rgb(0, 0, 0);
--zitadel-color-font-500: rgb(0, 0, 0);
--zitadel-color-font-600: rgb(0, 0, 0);
--zitadel-color-font-700: rgb(0, 0, 0);
--zitadel-color-font-800: rgb(0, 0, 0);
--zitadel-color-font-900: rgb(0, 0, 0);
--zitadel-color-font-contrast: rgb(255, 255, 255);
--zitadel-color-label: var(--zitadel-color-grey-600);
@ -69,7 +91,6 @@
--zitadel-color-raised-button-background: var(--zitadel-color-white);
--zitadel-color-white: #ffffff;
--zitadel-color-black: #000000;
--zitadel-color-grey-50: #fafafa;
--zitadel-color-grey-100: #f5f5f5;
@ -128,7 +149,18 @@
--zitadel-font-family: 'Lato';
--zitadel-color-background-500: var(--zitadel-color-grey-900);
--zitadel-color-background-50: rgb(60, 60, 60);
--zitadel-color-background-100: rgb(55, 55, 55);
--zitadel-color-background-200: rgb(49, 49, 49);
--zitadel-color-background-300: rgb(44, 44, 44);
--zitadel-color-background-400: rgb(36, 36, 36);
--zitadel-color-background-500: rgb(33, 33, 33);
--zitadel-color-background-600: rgb(30, 30, 30);
--zitadel-color-background-700: rgb(28, 28, 28);
--zitadel-color-background-800: rgb(25, 25, 25);
--zitadel-color-background-900: rgb(23, 23, 23);
--zitadel-color-background-contrast: rgb(255, 255, 255);
--zitadel-color-footer-line: #303131;
--zitadel-color-input-background: rgba(0, 0, 0, 0.2);
@ -136,7 +168,17 @@
--zitadel-color-input-border-hover: #aeafb1;
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
--zitadel-color-text: var(--zitadel-color-white);
--zitadel-color-text-50: rgb(255, 255, 255);
--zitadel-color-text-100: rgb(255, 255, 255);
--zitadel-color-text-200: rgb(255, 255, 255);
--zitadel-color-text-300: rgb(255, 255, 255);
--zitadel-color-text-400: rgb(255, 255, 255);
--zitadel-color-text-500: rgb(255, 255, 255);
--zitadel-color-text-600: rgb(221, 222, 223);
--zitadel-color-text-700: rgb(194, 195, 195);
--zitadel-color-text-800: rgb(167, 168, 169);
--zitadel-color-text-900: rgb(141, 142, 143);
--zitadel-color-text-contrast: rgb(0, 0, 0);
--zitadel-color-label: var(--zitadel-color-grey-600);
--zitadel-color-account-selector-hover: rgba(255, 255, 255, 0.02);
@ -162,4 +204,4 @@
--zitadel-color-google-text: #8b8d8d;
--zitadel-color-google-background: #ffffff;
}
}

View File

@ -4,6 +4,7 @@
--zitadel-color-background: var(--zitadel-color-background-500);
--zitadel-color-secondary: var(--zitadel-color-secondary-500);
--zitadel-color-warn: var(--zitadel-color-warn-500);
--zitadel-color-text: var(--zitadel-color-text-500);
--zitadel-color-primary-50: #eaedfa;
--zitadel-color-primary-100: #ccd2f2;
--zitadel-color-primary-200: #aab4ea;
@ -38,13 +39,33 @@
--zitadel-color-warn-800: #c62828;
--zitadel-color-warn-900: #b71c1c;
--zitadel-font-family: "Lato";
--zitadel-color-background-500: var(--zitadel-color-grey-50);
--zitadel-color-background-50: rgb(255, 255, 255);
--zitadel-color-background-100: rgb(255, 255, 255);
--zitadel-color-background-200: rgb(255, 255, 255);
--zitadel-color-background-300: rgb(255, 255, 255);
--zitadel-color-background-400: rgb(255, 255, 255);
--zitadel-color-background-500: rgb(250, 250, 250);
--zitadel-color-background-600: rgb(222, 222, 222);
--zitadel-color-background-700: rgb(195, 195, 194);
--zitadel-color-background-800: rgb(168, 168, 168);
--zitadel-color-background-900: rgb(142, 142, 142);
--zitadel-color-background-contrast: rgb(0, 0, 0);
--zitadel-color-footer-line: #e3e8ee;
--zitadel-color-input-background: #fafafa50;
--zitadel-color-input-border: #00000040;
--zitadel-color-input-border-hover: #1a1b1b;
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
--zitadel-color-text: rgba(0, 0, 0, 0.87);
--zitadel-color-font-50: rgb(0, 0, 0);
--zitadel-color-font-100: rgb(0, 0, 0);
--zitadel-color-font-200: rgb(0, 0, 0);
--zitadel-color-font-300: rgb(0, 0, 0);
--zitadel-color-font-400: rgb(0, 0, 0);
--zitadel-color-font-500: rgb(0, 0, 0);
--zitadel-color-font-600: rgb(0, 0, 0);
--zitadel-color-font-700: rgb(0, 0, 0);
--zitadel-color-font-800: rgb(0, 0, 0);
--zitadel-color-font-900: rgb(0, 0, 0);
--zitadel-color-font-contrast: rgb(255, 255, 255);
--zitadel-color-label: var(--zitadel-color-grey-600);
--zitadel-color-account-selector-hover: rgba(0, 0, 0, 0.02);
--zitadel-color-account-selector-active: rgba(0, 0, 0, 0.05);
@ -56,7 +77,6 @@
--zitadel-color-button-selected-background: var(--zitadel-color-grey-900);
--zitadel-color-button-disabled-selected-background: var(--zitadel-color-grey-800);
--zitadel-color-raised-button-background: var(--zitadel-color-white);
--zitadel-color-white: #ffffff;
--zitadel-color-black: #000000;
--zitadel-color-grey-50: #fafafa;
--zitadel-color-grey-100: #f5f5f5;
@ -108,13 +128,33 @@
--zitadel-color-warn-800: #c62828;
--zitadel-color-warn-900: #b71c1c;
--zitadel-font-family: "Lato";
--zitadel-color-background-500: var(--zitadel-color-grey-900);
--zitadel-color-background-50: rgb(60, 60, 60);
--zitadel-color-background-100: rgb(55, 55, 55);
--zitadel-color-background-200: rgb(49, 49, 49);
--zitadel-color-background-300: rgb(44, 44, 44);
--zitadel-color-background-400: rgb(36, 36, 36);
--zitadel-color-background-500: rgb(33, 33, 33);
--zitadel-color-background-600: rgb(30, 30, 30);
--zitadel-color-background-700: rgb(28, 28, 28);
--zitadel-color-background-800: rgb(25, 25, 25);
--zitadel-color-background-900: rgb(23, 23, 23);
--zitadel-color-background-contrast: rgb(255, 255, 255);
--zitadel-color-footer-line: #303131;
--zitadel-color-input-background: rgba(0, 0, 0, 0.2);
--zitadel-color-input-border: #403e3e;
--zitadel-color-input-border-hover: #aeafb1;
--zitadel-color-input-placeholder: var(--zitadel-color-grey-600);
--zitadel-color-text: var(--zitadel-color-white);
--zitadel-color-text-50: rgb(255, 255, 255);
--zitadel-color-text-100: rgb(255, 255, 255);
--zitadel-color-text-200: rgb(255, 255, 255);
--zitadel-color-text-300: rgb(255, 255, 255);
--zitadel-color-text-400: rgb(255, 255, 255);
--zitadel-color-text-500: rgb(255, 255, 255);
--zitadel-color-text-600: rgb(221, 222, 223);
--zitadel-color-text-700: rgb(194, 195, 195);
--zitadel-color-text-800: rgb(167, 168, 169);
--zitadel-color-text-900: rgb(141, 142, 143);
--zitadel-color-text-contrast: rgb(0, 0, 0);
--zitadel-color-label: var(--zitadel-color-grey-600);
--zitadel-color-account-selector-hover: rgba(255, 255, 255, 0.02);
--zitadel-color-account-selector-active: rgba(255, 255, 255, 0.05);

File diff suppressed because one or more lines are too long

View File

@ -41,6 +41,7 @@ type AssetStorage struct {
SSL bool `yaml:"ssl,omitempty"`
Location string `yaml:"location,omitempty"`
BucketPrefix string `yaml:"bucketPrefix,omitempty"`
MultiDelete bool `yaml:"multiDelete,omitempty"`
}
type DNS struct {

View File

@ -107,6 +107,7 @@ func literalsConfigMap(
literalsConfigMap["ZITADEL_ASSET_STORAGE_SSL"] = strconv.FormatBool(desired.AssetStorage.SSL)
literalsConfigMap["ZITADEL_ASSET_STORAGE_LOCATION"] = desired.AssetStorage.Location
literalsConfigMap["ZITADEL_ASSET_STORAGE_BUCKET_PREFIX"] = desired.AssetStorage.BucketPrefix
literalsConfigMap["ZITADEL_ASSET_STORAGE_MULTI_DELETE"] = strconv.FormatBool(desired.AssetStorage.MultiDelete)
}
}

View File

@ -294,6 +294,7 @@ func TestConfiguration_LiteralsConfigMap(t *testing.T) {
"ZITADEL_ASSET_STORAGE_SSL": "false",
"ZITADEL_ASSET_STORAGE_LOCATION": "",
"ZITADEL_ASSET_STORAGE_BUCKET_PREFIX": "",
"ZITADEL_ASSET_STORAGE_MULTI_DELETE": "false",
}
literals := literalsConfigMap(desiredEmpty, users, certPath, secretPath, googleSA, zitadelKeyPath, queried)
@ -378,6 +379,7 @@ func TestConfiguration_LiteralsConfigMapFull(t *testing.T) {
"ZITADEL_ASSET_STORAGE_SSL": "true",
"ZITADEL_ASSET_STORAGE_LOCATION": "location",
"ZITADEL_ASSET_STORAGE_BUCKET_PREFIX": "bucketprefix",
"ZITADEL_ASSET_STORAGE_MULTI_DELETE": "false",
}
literals := literalsConfigMap(desiredFull, users, certPath, secretPath, googleSA, zitadelKeyPath, queried)