feat: enable iframe use (#4766)

* feat: enable iframe use

* cleanup

* fix mocks

* fix linting

* docs: add iframe usage to solution scenarios configurations

* improve api

* feat(console): security policy

* description

* remove unnecessary line

* disable input button and urls when not enabled

* add image to docs

Co-authored-by: Max Peintner <max@caos.ch>
Co-authored-by: Fabi <38692350+hifabienne@users.noreply.github.com>
This commit is contained in:
Livio Spring
2022-12-14 07:17:36 +01:00
committed by GitHub
parent 33e973f015
commit 632639ae7f
40 changed files with 1151 additions and 45 deletions

View File

@@ -19,6 +19,7 @@ type Instance interface {
RequestedHost() string
DefaultLanguage() language.Tag
DefaultOrganisationID() string
SecurityPolicyAllowedOrigins() []string
}
type InstanceVerifier interface {
@@ -66,6 +67,10 @@ func (i *instance) DefaultOrganisationID() string {
return i.orgID
}
func (i *instance) SecurityPolicyAllowedOrigins() []string {
return nil
}
func GetInstance(ctx context.Context) Instance {
instance, ok := ctx.Value(instanceKey).(Instance)
if !ok {

View File

@@ -99,3 +99,7 @@ func (m *mockInstance) RequestedDomain() string {
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}
func (m *mockInstance) SecurityPolicyAllowedOrigins() []string {
return nil
}

View File

@@ -106,3 +106,23 @@ func (s *Server) UpdateSMTPConfigPassword(ctx context.Context, req *admin_pb.Upd
details.ResourceOwner),
}, nil
}
func (s *Server) GetSecurityPolicy(ctx context.Context, req *admin_pb.GetSecurityPolicyRequest) (*admin_pb.GetSecurityPolicyResponse, error) {
policy, err := s.query.SecurityPolicy(ctx)
if err != nil {
return nil, err
}
return &admin_pb.GetSecurityPolicyResponse{
Policy: SecurityPolicyToPb(policy),
}, nil
}
func (s *Server) SetSecurityPolicy(ctx context.Context, req *admin_pb.SetSecurityPolicyRequest) (*admin_pb.SetSecurityPolicyResponse, error) {
details, err := s.command.SetSecurityPolicy(ctx, req.EnableIframeEmbedding, req.AllowedOrigins)
if err != nil {
return nil, err
}
return &admin_pb.SetSecurityPolicyResponse{
Details: object.DomainToChangeDetailsPb(details),
}, nil
}

View File

@@ -149,3 +149,11 @@ func SMTPConfigToPb(smtp *query.SMTPConfig) *settings_pb.SMTPConfig {
}
return mapped
}
func SecurityPolicyToPb(policy *query.SecurityPolicy) *settings_pb.SecurityPolicy {
return &settings_pb.SecurityPolicy{
Details: obj_grpc.ToViewDetailsPb(policy.Sequence, policy.CreationDate, policy.ChangeDate, policy.AggregateID),
EnableIframeEmbedding: policy.Enabled,
AllowedOrigins: policy.AllowedOrigins,
}
}

View File

@@ -193,3 +193,7 @@ func (m *mockInstance) RequestedDomain() string {
func (m *mockInstance) RequestedHost() string {
return "localhost:8080"
}
func (m *mockInstance) SecurityPolicyAllowedOrigins() []string {
return nil
}

View File

@@ -6,36 +6,38 @@ import (
)
type CSP struct {
DefaultSrc CSPSourceOptions
ScriptSrc CSPSourceOptions
ObjectSrc CSPSourceOptions
StyleSrc CSPSourceOptions
ImgSrc CSPSourceOptions
MediaSrc CSPSourceOptions
FrameSrc CSPSourceOptions
FontSrc CSPSourceOptions
ManifestSrc CSPSourceOptions
ConnectSrc CSPSourceOptions
FormAction CSPSourceOptions
DefaultSrc CSPSourceOptions
ScriptSrc CSPSourceOptions
ObjectSrc CSPSourceOptions
StyleSrc CSPSourceOptions
ImgSrc CSPSourceOptions
MediaSrc CSPSourceOptions
FrameSrc CSPSourceOptions
FrameAncestors CSPSourceOptions
FontSrc CSPSourceOptions
ManifestSrc CSPSourceOptions
ConnectSrc CSPSourceOptions
FormAction CSPSourceOptions
}
var (
DefaultSCP = CSP{
DefaultSrc: CSPSourceOptsNone(),
ScriptSrc: CSPSourceOptsSelf(),
ObjectSrc: CSPSourceOptsNone(),
StyleSrc: CSPSourceOptsSelf(),
ImgSrc: CSPSourceOptsSelf(),
MediaSrc: CSPSourceOptsNone(),
FrameSrc: CSPSourceOptsNone(),
FontSrc: CSPSourceOptsSelf(),
ManifestSrc: CSPSourceOptsSelf(),
ConnectSrc: CSPSourceOptsSelf(),
DefaultSrc: CSPSourceOptsNone(),
ScriptSrc: CSPSourceOptsSelf(),
ObjectSrc: CSPSourceOptsNone(),
StyleSrc: CSPSourceOptsSelf(),
ImgSrc: CSPSourceOptsSelf(),
MediaSrc: CSPSourceOptsNone(),
FrameSrc: CSPSourceOptsNone(),
FrameAncestors: CSPSourceOptsNone(),
FontSrc: CSPSourceOptsSelf(),
ManifestSrc: CSPSourceOptsSelf(),
ConnectSrc: CSPSourceOptsSelf(),
}
)
func (csp *CSP) Value(nonce string, host string) string {
valuesMap := csp.asMap()
func (csp *CSP) Value(nonce, host string, iframe []string) string {
valuesMap := csp.asMap(iframe)
values := make([]string, 0, len(valuesMap))
for k, v := range valuesMap {
@@ -49,19 +51,24 @@ func (csp *CSP) Value(nonce string, host string) string {
return strings.Join(values, ";")
}
func (csp *CSP) asMap() map[string]CSPSourceOptions {
func (csp *CSP) asMap(iframe []string) map[string]CSPSourceOptions {
frameAncestors := csp.FrameAncestors
if len(iframe) > 0 {
frameAncestors = CSPSourceOpts().AddHost(iframe...)
}
return map[string]CSPSourceOptions{
"default-src": csp.DefaultSrc,
"script-src": csp.ScriptSrc,
"object-src": csp.ObjectSrc,
"style-src": csp.StyleSrc,
"img-src": csp.ImgSrc,
"media-src": csp.MediaSrc,
"frame-src": csp.FrameSrc,
"font-src": csp.FontSrc,
"manifest-src": csp.ManifestSrc,
"connect-src": csp.ConnectSrc,
"form-action": csp.FormAction,
"default-src": csp.DefaultSrc,
"script-src": csp.ScriptSrc,
"object-src": csp.ObjectSrc,
"style-src": csp.StyleSrc,
"img-src": csp.ImgSrc,
"media-src": csp.MediaSrc,
"frame-src": csp.FrameSrc,
"frame-ancestors": frameAncestors,
"font-src": csp.FontSrc,
"manifest-src": csp.ManifestSrc,
"connect-src": csp.ConnectSrc,
"form-action": csp.FormAction,
}
}

View File

@@ -277,3 +277,7 @@ func (m *mockInstance) RequestedDomain() string {
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}
func (m *mockInstance) SecurityPolicyAllowedOrigins() []string {
return nil
}

View File

@@ -6,6 +6,7 @@ import (
"encoding/base64"
"net/http"
"github.com/zitadel/zitadel/internal/api/authz"
http_utils "github.com/zitadel/zitadel/internal/api/http"
)
@@ -62,11 +63,14 @@ func (h *headers) ServeHTTP(w http.ResponseWriter, r *http.Request) {
}
r = saveContext(r, nonceKey, nonce)
}
allowedHosts := authz.GetInstance(r.Context()).SecurityPolicyAllowedOrigins()
headers := w.Header()
headers.Set(http_utils.ContentSecurityPolicy, h.csp.Value(nonce, r.Host))
headers.Set(http_utils.ContentSecurityPolicy, h.csp.Value(nonce, r.Host, allowedHosts))
headers.Set(http_utils.XXSSProtection, "1; mode=block")
headers.Set(http_utils.StrictTransportSecurity, "max-age=31536000; includeSubDomains")
headers.Set(http_utils.XFrameOptions, "DENY")
if len(allowedHosts) == 0 {
headers.Set(http_utils.XFrameOptions, "DENY")
}
headers.Set(http_utils.XContentTypeOptions, "nosniff")
headers.Set(http_utils.ReferrerPolicy, "same-origin")
headers.Set(http_utils.FeaturePolicy, "payment 'none'")

View File

@@ -99,7 +99,7 @@ func Start(config Config, externalSecure bool, issuer op.IssuerFromRequest, inst
handler := mux.NewRouter()
handler.Use(security, instanceHandler)
handler.Use(instanceHandler, security)
handler.Handle(envRequestPath, middleware.TelemetryHandler()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
url := http_util.BuildOrigin(r.Host, externalSecure)
environmentJSON, err := createEnvironmentJSON(url, issuer(r), authz.GetInstance(r.Context()).ConsoleClientID(), customerPortal)

View File

@@ -0,0 +1,59 @@
package command
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/command/preparation"
"github.com/zitadel/zitadel/internal/domain"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
)
func (c *Commands) SetSecurityPolicy(ctx context.Context, enabled bool, allowedOrigins []string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.prepareSetSecurityPolicy(instanceAgg, enabled, allowedOrigins)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil {
return nil, err
}
events, err := c.eventstore.Push(ctx, cmds...)
if err != nil {
return nil, err
}
return &domain.ObjectDetails{
Sequence: events[len(events)-1].Sequence(),
EventDate: events[len(events)-1].CreationDate(),
ResourceOwner: events[len(events)-1].Aggregate().InstanceID,
}, nil
}
func (c *Commands) prepareSetSecurityPolicy(a *instance.Aggregate, enabled bool, allowedOrigins []string) preparation.Validation {
return func() (preparation.CreateCommands, error) {
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
writeModel, err := c.getSecurityPolicyWriteModel(ctx, filter)
if err != nil {
return nil, err
}
cmd, err := writeModel.NewSetEvent(ctx, &a.Aggregate, enabled, allowedOrigins)
if err != nil {
return nil, err
}
return []eventstore.Command{cmd}, nil
}, nil
}
}
func (c *Commands) getSecurityPolicyWriteModel(ctx context.Context, filter preparation.FilterToQueryReducer) (_ *InstanceSecurityPolicyWriteModel, err error) {
writeModel := NewInstanceSecurityPolicyWriteModel(ctx)
events, err := filter(ctx, writeModel.Query())
if err != nil {
return nil, err
}
if len(events) == 0 {
return writeModel, nil
}
writeModel.AppendEvents(events...)
err = writeModel.Reduce()
return writeModel, err
}

View File

@@ -0,0 +1,73 @@
package command
import (
"context"
"reflect"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/repository/instance"
)
type InstanceSecurityPolicyWriteModel struct {
eventstore.WriteModel
Enabled bool
AllowedOrigins []string
}
func NewInstanceSecurityPolicyWriteModel(ctx context.Context) *InstanceSecurityPolicyWriteModel {
return &InstanceSecurityPolicyWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: authz.GetInstance(ctx).InstanceID(),
ResourceOwner: authz.GetInstance(ctx).InstanceID(),
},
}
}
func (wm *InstanceSecurityPolicyWriteModel) Reduce() error {
for _, event := range wm.Events {
if e, ok := event.(*instance.SecurityPolicySetEvent); ok {
if e.Enabled != nil {
wm.Enabled = *e.Enabled
}
if e.AllowedOrigins != nil {
wm.AllowedOrigins = *e.AllowedOrigins
}
}
}
return wm.WriteModel.Reduce()
}
func (wm *InstanceSecurityPolicyWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(instance.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(
instance.SecurityPolicySetEventType).
Builder()
}
func (wm *InstanceSecurityPolicyWriteModel) NewSetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
enabled bool,
allowedOrigins []string,
) (*instance.SecurityPolicySetEvent, error) {
changes := make([]instance.SecurityPolicyChanges, 0, 2)
var err error
if wm.Enabled != enabled {
changes = append(changes, instance.ChangeSecurityPolicyEnabled(enabled))
}
if enabled && !reflect.DeepEqual(wm.AllowedOrigins, allowedOrigins) {
changes = append(changes, instance.ChangeSecurityPolicyAllowedOrigins(allowedOrigins))
}
changeEvent, err := instance.NewSecurityPolicySetEvent(ctx, aggregate, changes)
if err != nil {
return nil, err
}
return changeEvent, nil
}

View File

@@ -244,3 +244,7 @@ func (m *mockInstance) RequestedDomain() string {
func (m *mockInstance) RequestedHost() string {
return "zitadel.cloud:443"
}
func (m *mockInstance) SecurityPolicyAllowedOrigins() []string {
return nil
}

View File

@@ -11,6 +11,7 @@ import (
"golang.org/x/text/language"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/query/projection"
"github.com/zitadel/zitadel/internal/telemetry/tracing"
@@ -81,6 +82,12 @@ type Instance struct {
DefaultLang language.Tag
Domains []*InstanceDomain
host string
csp csp
}
type csp struct {
enabled bool
allowedOrigins database.StringArray
}
type Instances struct {
@@ -120,6 +127,13 @@ func (i *Instance) DefaultOrganisationID() string {
return i.DefaultOrgID
}
func (i *Instance) SecurityPolicyAllowedOrigins() []string {
if !i.csp.enabled {
return nil
}
return i.csp.allowedOrigins
}
type InstanceSearchQueries struct {
SearchRequest
Queries []SearchQuery
@@ -189,7 +203,7 @@ func (q *Queries) InstanceByHost(ctx context.Context, host string) (_ authz.Inst
ctx, span := tracing.NewSpan(ctx)
defer func() { span.EndWithError(err) }()
stmt, scan := prepareInstanceDomainQuery(host)
stmt, scan := prepareAuthzInstanceQuery(host)
host = strings.Split(host, ":")[0] //remove possible port
query, args, err := stmt.Where(sq.Eq{
InstanceDomainDomainCol.identifier(): host,
@@ -436,3 +450,92 @@ func prepareInstanceDomainQuery(host string) (sq.SelectBuilder, func(*sql.Rows)
return instance, nil
}
}
func prepareAuthzInstanceQuery(host string) (sq.SelectBuilder, func(*sql.Rows) (*Instance, error)) {
return sq.Select(
InstanceColumnID.identifier(),
InstanceColumnCreationDate.identifier(),
InstanceColumnChangeDate.identifier(),
InstanceColumnSequence.identifier(),
InstanceColumnName.identifier(),
InstanceColumnDefaultOrgID.identifier(),
InstanceColumnProjectID.identifier(),
InstanceColumnConsoleID.identifier(),
InstanceColumnConsoleAppID.identifier(),
InstanceColumnDefaultLanguage.identifier(),
InstanceDomainDomainCol.identifier(),
InstanceDomainIsPrimaryCol.identifier(),
InstanceDomainIsGeneratedCol.identifier(),
InstanceDomainCreationDateCol.identifier(),
InstanceDomainChangeDateCol.identifier(),
InstanceDomainSequenceCol.identifier(),
SecurityPolicyColumnEnabled.identifier(),
SecurityPolicyColumnAllowedOrigins.identifier(),
).
From(instanceTable.identifier()).
LeftJoin(join(InstanceDomainInstanceIDCol, InstanceColumnID)).
LeftJoin(join(SecurityPolicyColumnInstanceID, InstanceColumnID)).
PlaceholderFormat(sq.Dollar),
func(rows *sql.Rows) (*Instance, error) {
instance := &Instance{
host: host,
Domains: make([]*InstanceDomain, 0),
}
lang := ""
for rows.Next() {
var (
domain sql.NullString
isPrimary sql.NullBool
isGenerated sql.NullBool
changeDate sql.NullTime
creationDate sql.NullTime
sequence sql.NullInt64
securityPolicyEnabled sql.NullBool
)
err := rows.Scan(
&instance.ID,
&instance.CreationDate,
&instance.ChangeDate,
&instance.Sequence,
&instance.Name,
&instance.DefaultOrgID,
&instance.IAMProjectID,
&instance.ConsoleID,
&instance.ConsoleAppID,
&lang,
&domain,
&isPrimary,
&isGenerated,
&changeDate,
&creationDate,
&sequence,
&securityPolicyEnabled,
&instance.csp.allowedOrigins,
)
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-d3fas", "Errors.Internal")
}
if !domain.Valid {
continue
}
instance.Domains = append(instance.Domains, &InstanceDomain{
CreationDate: creationDate.Time,
ChangeDate: changeDate.Time,
Sequence: uint64(sequence.Int64),
Domain: domain.String,
IsPrimary: isPrimary.Bool,
IsGenerated: isGenerated.Bool,
InstanceID: instance.ID,
})
instance.csp.enabled = securityPolicyEnabled.Bool
}
if instance.ID == "" {
return nil, errors.ThrowNotFound(nil, "QUERY-n0wng", "Errors.IAM.NotFound")
}
instance.DefaultLang = language.Make(lang)
if err := rows.Close(); err != nil {
return nil, errors.ThrowInternal(err, "QUERY-Dfbe2", "Errors.Query.CloseRows")
}
return instance, nil
}
}

View File

@@ -59,6 +59,7 @@ var (
OIDCSettingsProjection *oidcSettingsProjection
DebugNotificationProviderProjection *debugNotificationProviderProjection
KeyProjection *keyProjection
SecurityPolicyProjection *securityPolicyProjection
NotificationsProjection interface{}
)
@@ -131,6 +132,7 @@ func Create(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, c
OIDCSettingsProjection = newOIDCSettingsProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["oidc_settings"]))
DebugNotificationProviderProjection = newDebugNotificationProviderProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["debug_notification_provider"]))
KeyProjection = newKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyEncryptionAlgorithm, certEncryptionAlgorithm)
SecurityPolicyProjection = newSecurityPolicyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["security_policies"]))
newProjectionsList()
return nil
}
@@ -221,5 +223,6 @@ func newProjectionsList() {
OIDCSettingsProjection,
DebugNotificationProviderProjection,
KeyProjection,
SecurityPolicyProjection,
}
}

View File

@@ -0,0 +1,89 @@
package projection
import (
"context"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/handler"
"github.com/zitadel/zitadel/internal/eventstore/handler/crdb"
"github.com/zitadel/zitadel/internal/repository/instance"
)
const (
SecurityPolicyProjectionTable = "projections.security_policies"
SecurityPolicyColumnInstanceID = "instance_id"
SecurityPolicyColumnCreationDate = "creation_date"
SecurityPolicyColumnChangeDate = "change_date"
SecurityPolicyColumnSequence = "sequence"
SecurityPolicyColumnEnabled = "enabled"
SecurityPolicyColumnAllowedOrigins = "origins"
)
type securityPolicyProjection struct {
crdb.StatementHandler
}
func newSecurityPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *securityPolicyProjection {
p := new(securityPolicyProjection)
config.ProjectionName = SecurityPolicyProjectionTable
config.Reducers = p.reducers()
config.InitCheck = crdb.NewTableCheck(
crdb.NewTable([]*crdb.Column{
crdb.NewColumn(SecurityPolicyColumnCreationDate, crdb.ColumnTypeTimestamp),
crdb.NewColumn(SecurityPolicyColumnChangeDate, crdb.ColumnTypeTimestamp),
crdb.NewColumn(SecurityPolicyColumnInstanceID, crdb.ColumnTypeText),
crdb.NewColumn(SecurityPolicyColumnSequence, crdb.ColumnTypeInt64),
crdb.NewColumn(SecurityPolicyColumnEnabled, crdb.ColumnTypeBool, crdb.Default(false)),
crdb.NewColumn(SecurityPolicyColumnAllowedOrigins, crdb.ColumnTypeTextArray, crdb.Nullable()),
},
crdb.NewPrimaryKey(SecurityPolicyColumnInstanceID),
),
)
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p
}
func (p *securityPolicyProjection) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{
{
Aggregate: instance.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: instance.SecurityPolicySetEventType,
Reduce: p.reduceSecurityPolicySet,
},
{
Event: instance.InstanceRemovedEventType,
Reduce: reduceInstanceRemovedHelper(SecurityPolicyColumnInstanceID),
},
},
},
}
}
func (p *securityPolicyProjection) reduceSecurityPolicySet(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*instance.SecurityPolicySetEvent)
if !ok {
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-D3g87", "reduce.wrong.event.type %s", instance.SecurityPolicySetEventType)
}
changes := []handler.Column{
handler.NewCol(SecurityPolicyColumnCreationDate, e.CreationDate()),
handler.NewCol(SecurityPolicyColumnChangeDate, e.CreationDate()),
handler.NewCol(SecurityPolicyColumnInstanceID, e.Aggregate().InstanceID),
handler.NewCol(SecurityPolicyColumnSequence, e.Sequence()),
}
if e.Enabled != nil {
changes = append(changes, handler.NewCol(SecurityPolicyColumnEnabled, *e.Enabled))
}
if e.AllowedOrigins != nil {
changes = append(changes, handler.NewCol(SecurityPolicyColumnAllowedOrigins, e.AllowedOrigins))
}
return crdb.NewUpsertStatement(
e,
[]handler.Column{
handler.NewCol(SecurityPolicyColumnInstanceID, ""),
},
changes,
), nil
}

View File

@@ -0,0 +1,98 @@
package query
import (
"context"
"database/sql"
errs "errors"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/database"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/query/projection"
)
var (
securityPolicyTable = table{
name: projection.SecurityPolicyProjectionTable,
instanceIDCol: projection.SecurityPolicyColumnInstanceID,
}
SecurityPolicyColumnCreationDate = Column{
name: projection.SecurityPolicyColumnCreationDate,
table: securityPolicyTable,
}
SecurityPolicyColumnChangeDate = Column{
name: projection.SecurityPolicyColumnChangeDate,
table: securityPolicyTable,
}
SecurityPolicyColumnInstanceID = Column{
name: projection.SecurityPolicyColumnInstanceID,
table: securityPolicyTable,
}
SecurityPolicyColumnSequence = Column{
name: projection.SecurityPolicyColumnSequence,
table: securityPolicyTable,
}
SecurityPolicyColumnEnabled = Column{
name: projection.SecurityPolicyColumnEnabled,
table: securityPolicyTable,
}
SecurityPolicyColumnAllowedOrigins = Column{
name: projection.SecurityPolicyColumnAllowedOrigins,
table: securityPolicyTable,
}
)
type SecurityPolicy struct {
AggregateID string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
Enabled bool
AllowedOrigins database.StringArray
}
func (q *Queries) SecurityPolicy(ctx context.Context) (*SecurityPolicy, error) {
stmt, scan := prepareSecurityPolicyQuery()
query, args, err := stmt.Where(sq.Eq{
SecurityPolicyColumnInstanceID.identifier(): authz.GetInstance(ctx).InstanceID(),
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-Sf6d1", "Errors.Query.SQLStatment")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func prepareSecurityPolicyQuery() (sq.SelectBuilder, func(*sql.Row) (*SecurityPolicy, error)) {
return sq.Select(
SecurityPolicyColumnInstanceID.identifier(),
SecurityPolicyColumnCreationDate.identifier(),
SecurityPolicyColumnChangeDate.identifier(),
SecurityPolicyColumnInstanceID.identifier(),
SecurityPolicyColumnSequence.identifier(),
SecurityPolicyColumnEnabled.identifier(),
SecurityPolicyColumnAllowedOrigins.identifier()).
From(securityPolicyTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*SecurityPolicy, error) {
securityPolicy := new(SecurityPolicy)
err := row.Scan(
&securityPolicy.AggregateID,
&securityPolicy.CreationDate,
&securityPolicy.ChangeDate,
&securityPolicy.ResourceOwner,
&securityPolicy.Sequence,
&securityPolicy.Enabled,
&securityPolicy.AllowedOrigins,
)
if err != nil && !errs.Is(err, sql.ErrNoRows) { // ignore not found errors
return nil, errors.ThrowInternal(err, "QUERY-Dfrt2", "Errors.Internal")
}
return securityPolicy, nil
}
}

View File

@@ -30,6 +30,7 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(DebugNotificationProviderLogRemovedEventType, DebugNotificationProviderLogRemovedEventMapper).
RegisterFilterEventMapper(OIDCSettingsAddedEventType, OIDCSettingsAddedEventMapper).
RegisterFilterEventMapper(OIDCSettingsChangedEventType, OIDCSettingsChangedEventMapper).
RegisterFilterEventMapper(SecurityPolicySetEventType, SecurityPolicySetEventMapper).
RegisterFilterEventMapper(LabelPolicyAddedEventType, LabelPolicyAddedEventMapper).
RegisterFilterEventMapper(LabelPolicyChangedEventType, LabelPolicyChangedEventMapper).
RegisterFilterEventMapper(LabelPolicyActivatedEventType, LabelPolicyActivatedEventMapper).

View File

@@ -0,0 +1,80 @@
package instance
import (
"context"
"encoding/json"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/internal/eventstore"
"github.com/zitadel/zitadel/internal/eventstore/repository"
)
const (
securityPolicyPrefix = "policy.security."
SecurityPolicySetEventType = instanceEventTypePrefix + securityPolicyPrefix + "set"
)
type SecurityPolicySetEvent struct {
eventstore.BaseEvent `json:"-"`
Enabled *bool `json:"enabled,omitempty"`
AllowedOrigins *[]string `json:"allowedOrigins,omitempty"`
}
func NewSecurityPolicySetEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []SecurityPolicyChanges,
) (*SecurityPolicySetEvent, error) {
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "POLICY-EWsf3", "Errors.NoChangesFound")
}
event := &SecurityPolicySetEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
SecurityPolicySetEventType,
),
}
for _, change := range changes {
change(event)
}
return event, nil
}
type SecurityPolicyChanges func(event *SecurityPolicySetEvent)
func ChangeSecurityPolicyEnabled(enabled bool) func(event *SecurityPolicySetEvent) {
return func(e *SecurityPolicySetEvent) {
e.Enabled = &enabled
}
}
func ChangeSecurityPolicyAllowedOrigins(allowedOrigins []string) func(event *SecurityPolicySetEvent) {
return func(e *SecurityPolicySetEvent) {
if len(allowedOrigins) == 0 {
allowedOrigins = []string{}
}
e.AllowedOrigins = &allowedOrigins
}
}
func (e *SecurityPolicySetEvent) Data() interface{} {
return e
}
func (e *SecurityPolicySetEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func SecurityPolicySetEventMapper(event *repository.Event) (eventstore.Event, error) {
securityPolicyAdded := &SecurityPolicySetEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, securityPolicyAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "IAM-soiwj", "unable to unmarshal oidc config added")
}
return securityPolicyAdded, nil
}