feat: OIDC setting (#3245)

* feat: add oidc config struct

* feat: oidc config command side

* feat: oidc configuration query side

* feat: add translations

* feat: add tests

* feat: add translations

* feat: rename oidc config to oidc settings

* feat: rename oidc config to oidc settings
This commit is contained in:
Fabi 2022-02-25 16:05:06 +01:00 committed by GitHub
parent f05d4063bf
commit 7d6c933485
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
57 changed files with 1440 additions and 40 deletions

View File

@ -188,6 +188,30 @@ Update twilio sms provider token
PUT: /sms/twilio/{id}/token
### GetOIDCSettings
> **rpc** GetOIDCSettings([GetOIDCSettingsRequest](#getoidcsettingsrequest))
[GetOIDCSettingsResponse](#getoidcsettingsresponse)
Get OIDC settings (e.g token lifetimes, etc.)
GET: /settings/oidc
### UpdateOIDCSettings
> **rpc** UpdateOIDCSettings([UpdateOIDCSettingsRequest](#updateoidcsettingsrequest))
[UpdateOIDCSettingsResponse](#updateoidcsettingsresponse)
Update oidc settings (e.g token lifetimes, etc)
PUT: /settings/oidc
### GetOrgByID
> **rpc** GetOrgByID([GetOrgByIDRequest](#getorgbyidrequest))
@ -2046,6 +2070,23 @@ This is an empty request
### GetOIDCSettingsRequest
This is an empty request
### GetOIDCSettingsResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| settings | zitadel.settings.v1.OIDCSettings | - | |
### GetOrgByIDRequest
@ -3613,6 +3654,31 @@ This is an empty request
### UpdateOIDCSettingsRequest
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| access_token_lifetime | google.protobuf.Duration | - | |
| id_token_lifetime | google.protobuf.Duration | - | |
| refresh_token_idle_expiration | google.protobuf.Duration | - | |
| refresh_token_expiration | google.protobuf.Duration | - | |
### UpdateOIDCSettingsResponse
| Field | Type | Description | Validation |
| ----- | ---- | ----------- | ----------- |
| details | zitadel.v1.ObjectDetails | - | |
### UpdateOrgIAMPolicyRequest

View File

@ -0,0 +1,29 @@
package admin
import (
"context"
"github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/domain"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
)
func (s *Server) GetOIDCSettings(ctx context.Context, _ *admin_pb.GetOIDCSettingsRequest) (*admin_pb.GetOIDCSettingsResponse, error) {
result, err := s.query.OIDCSettingsByAggID(ctx, domain.IAMID)
if err != nil {
return nil, err
}
return &admin_pb.GetOIDCSettingsResponse{
Settings: OIDCSettingsToPb(result),
}, nil
}
func (s *Server) UpdateOIDCSettings(ctx context.Context, req *admin_pb.UpdateOIDCSettingsRequest) (*admin_pb.UpdateOIDCSettingsResponse, error) {
result, err := s.command.ChangeOIDCSettings(ctx, UpdateOIDCConfigToConfig(req))
if err != nil {
return nil, err
}
return &admin_pb.UpdateOIDCSettingsResponse{
Details: object.DomainToChangeDetailsPb(result),
}, nil
}

View File

@ -0,0 +1,29 @@
package admin
import (
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/query"
admin_pb "github.com/caos/zitadel/pkg/grpc/admin"
settings_pb "github.com/caos/zitadel/pkg/grpc/settings"
"google.golang.org/protobuf/types/known/durationpb"
)
func OIDCSettingsToPb(config *query.OIDCSettings) *settings_pb.OIDCSettings {
return &settings_pb.OIDCSettings{
Details: obj_grpc.ToViewDetailsPb(config.Sequence, config.CreationDate, config.ChangeDate, config.AggregateID),
AccessTokenLifetime: durationpb.New(config.AccessTokenLifetime),
IdTokenLifetime: durationpb.New(config.IdTokenLifetime),
RefreshTokenIdleExpiration: durationpb.New(config.RefreshTokenIdleExpiration),
RefreshTokenExpiration: durationpb.New(config.RefreshTokenExpiration),
}
}
func UpdateOIDCConfigToConfig(req *admin_pb.UpdateOIDCSettingsRequest) *domain.OIDCSettings {
return &domain.OIDCSettings{
AccessTokenLifetime: req.AccessTokenLifetime.AsDuration(),
IdTokenLifetime: req.IdTokenLifetime.AsDuration(),
RefreshTokenIdleExpiration: req.RefreshTokenIdleExpiration.AsDuration(),
RefreshTokenExpiration: req.RefreshTokenExpiration.AsDuration(),
}
}

View File

@ -0,0 +1,79 @@
package command
import (
"context"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/repository/iam"
)
func (c *Commands) AddOIDCSettings(ctx context.Context, settings *domain.OIDCSettings) (*domain.ObjectDetails, error) {
oidcSettingWriteModel, err := c.getOIDCSettings(ctx)
if err != nil {
return nil, err
}
if oidcSettingWriteModel.State.Exists() {
return nil, caos_errs.ThrowAlreadyExists(nil, "COMMAND-d9nlw", "Errors.OIDCSettings.AlreadyExists")
}
iamAgg := IAMAggregateFromWriteModel(&oidcSettingWriteModel.WriteModel)
pushedEvents, err := c.eventstore.Push(ctx, iam.NewOIDCSettingsAddedEvent(
ctx,
iamAgg,
settings.AccessTokenLifetime,
settings.IdTokenLifetime,
settings.RefreshTokenIdleExpiration,
settings.RefreshTokenExpiration))
if err != nil {
return nil, err
}
err = AppendAndReduce(oidcSettingWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&oidcSettingWriteModel.WriteModel), nil
}
func (c *Commands) ChangeOIDCSettings(ctx context.Context, settings *domain.OIDCSettings) (*domain.ObjectDetails, error) {
oidcSettingWriteModel, err := c.getOIDCSettings(ctx)
if err != nil {
return nil, err
}
if !oidcSettingWriteModel.State.Exists() {
return nil, caos_errs.ThrowNotFound(nil, "COMMAND-8snEr", "Errors.OIDCSettings.NotFound")
}
iamAgg := IAMAggregateFromWriteModel(&oidcSettingWriteModel.WriteModel)
changedEvent, hasChanged, err := oidcSettingWriteModel.NewChangedEvent(
ctx,
iamAgg,
settings.AccessTokenLifetime,
settings.IdTokenLifetime,
settings.RefreshTokenIdleExpiration,
settings.RefreshTokenExpiration)
if err != nil {
return nil, err
}
if !hasChanged {
return nil, caos_errs.ThrowPreconditionFailed(nil, "COMMAND-398uF", "Errors.NoChangesFound")
}
pushedEvents, err := c.eventstore.Push(ctx, changedEvent)
if err != nil {
return nil, err
}
err = AppendAndReduce(oidcSettingWriteModel, pushedEvents...)
if err != nil {
return nil, err
}
return writeModelToObjectDetails(&oidcSettingWriteModel.WriteModel), nil
}
func (c *Commands) getOIDCSettings(ctx context.Context) (_ *IAMOIDCSettingsWriteModel, err error) {
writeModel := NewIAMOIDCSettingsWriteModel()
err = c.eventstore.FilterToQueryReducer(ctx, writeModel)
if err != nil {
return nil, err
}
return writeModel, nil
}

View File

@ -0,0 +1,101 @@
package command
import (
"context"
"time"
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/repository/iam"
)
type IAMOIDCSettingsWriteModel struct {
eventstore.WriteModel
AccessTokenLifetime time.Duration
IdTokenLifetime time.Duration
RefreshTokenIdleExpiration time.Duration
RefreshTokenExpiration time.Duration
State domain.OIDCSettingsState
}
func NewIAMOIDCSettingsWriteModel() *IAMOIDCSettingsWriteModel {
return &IAMOIDCSettingsWriteModel{
WriteModel: eventstore.WriteModel{
AggregateID: domain.IAMID,
ResourceOwner: domain.IAMID,
},
}
}
func (wm *IAMOIDCSettingsWriteModel) Reduce() error {
for _, event := range wm.Events {
switch e := event.(type) {
case *iam.OIDCSettingsAddedEvent:
wm.AccessTokenLifetime = e.AccessTokenLifetime
wm.IdTokenLifetime = e.IdTokenLifetime
wm.RefreshTokenIdleExpiration = e.RefreshTokenIdleExpiration
wm.RefreshTokenExpiration = e.RefreshTokenExpiration
wm.State = domain.OIDCSettingsStateActive
case *iam.OIDCSettingsChangedEvent:
if e.AccessTokenLifetime != nil {
wm.AccessTokenLifetime = *e.AccessTokenLifetime
}
if e.IdTokenLifetime != nil {
wm.IdTokenLifetime = *e.IdTokenLifetime
}
if e.RefreshTokenIdleExpiration != nil {
wm.RefreshTokenIdleExpiration = *e.RefreshTokenIdleExpiration
}
if e.RefreshTokenExpiration != nil {
wm.RefreshTokenExpiration = *e.RefreshTokenExpiration
}
}
}
return wm.WriteModel.Reduce()
}
func (wm *IAMOIDCSettingsWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(iam.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(
iam.OIDCSettingsAddedEventType,
iam.OIDCSettingsChangedEventType).
Builder()
}
func (wm *IAMOIDCSettingsWriteModel) NewChangedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
accessTokenLifetime,
idTokenLifetime,
refreshTokenIdleExpiration,
refreshTokenExpiration time.Duration,
) (*iam.OIDCSettingsChangedEvent, bool, error) {
changes := make([]iam.OIDCSettingsChanges, 0, 4)
var err error
if wm.AccessTokenLifetime != accessTokenLifetime {
changes = append(changes, iam.ChangeOIDCSettingsAccessTokenLifetime(accessTokenLifetime))
}
if wm.IdTokenLifetime != idTokenLifetime {
changes = append(changes, iam.ChangeOIDCSettingsIdTokenLifetime(idTokenLifetime))
}
if wm.RefreshTokenIdleExpiration != refreshTokenIdleExpiration {
changes = append(changes, iam.ChangeOIDCSettingsRefreshTokenIdleExpiration(refreshTokenIdleExpiration))
}
if wm.RefreshTokenExpiration != refreshTokenExpiration {
changes = append(changes, iam.ChangeOIDCSettingsRefreshTokenExpiration(refreshTokenExpiration))
}
if len(changes) == 0 {
return nil, false, nil
}
changeEvent, err := iam.NewOIDCSettingsChangeEvent(ctx, aggregate, changes)
if err != nil {
return nil, false, err
}
return changeEvent, true, nil
}

View File

@ -0,0 +1,264 @@
package command
import (
"context"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/caos/zitadel/internal/domain"
caos_errs "github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/repository/iam"
)
func TestCommandSide_AddOIDCConfig(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
oidcConfig *domain.OIDCSettings
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "oidc config, error already exists",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewOIDCSettingsAddedEvent(context.Background(),
&iam.NewAggregate().Aggregate,
time.Hour*1,
time.Hour*1,
time.Hour*1,
time.Hour*1,
),
),
),
),
},
args: args{
ctx: context.Background(),
oidcConfig: &domain.OIDCSettings{
AccessTokenLifetime: 1 * time.Hour,
IdTokenLifetime: 1 * time.Hour,
RefreshTokenIdleExpiration: 1 * time.Hour,
RefreshTokenExpiration: 1 * time.Hour,
},
},
res: res{
err: caos_errs.IsErrorAlreadyExists,
},
},
{
name: "add secret generator, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
expectPush(
[]*repository.Event{
eventFromEventPusher(iam.NewOIDCSettingsAddedEvent(
context.Background(),
&iam.NewAggregate().Aggregate,
time.Hour*1,
time.Hour*1,
time.Hour*1,
time.Hour*1,
),
),
},
),
),
},
args: args{
ctx: context.Background(),
oidcConfig: &domain.OIDCSettings{
AccessTokenLifetime: 1 * time.Hour,
IdTokenLifetime: 1 * time.Hour,
RefreshTokenIdleExpiration: 1 * time.Hour,
RefreshTokenExpiration: 1 * time.Hour,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.AddOIDCSettings(tt.args.ctx, tt.args.oidcConfig)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func TestCommandSide_ChangeOIDCConfig(t *testing.T) {
type fields struct {
eventstore *eventstore.Eventstore
}
type args struct {
ctx context.Context
oidcConfig *domain.OIDCSettings
}
type res struct {
want *domain.ObjectDetails
err func(error) bool
}
tests := []struct {
name string
fields fields
args args
res res
}{
{
name: "oidc config not existing, not found error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(),
),
},
args: args{
ctx: context.Background(),
},
res: res{
err: caos_errs.IsNotFound,
},
},
{
name: "no changes, precondition error",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewOIDCSettingsAddedEvent(
context.Background(),
&iam.NewAggregate().Aggregate,
time.Hour*1,
time.Hour*1,
time.Hour*1,
time.Hour*1,
),
),
),
),
},
args: args{
ctx: context.Background(),
oidcConfig: &domain.OIDCSettings{
AccessTokenLifetime: 1 * time.Hour,
IdTokenLifetime: 1 * time.Hour,
RefreshTokenIdleExpiration: 1 * time.Hour,
RefreshTokenExpiration: 1 * time.Hour,
},
},
res: res{
err: caos_errs.IsPreconditionFailed,
},
},
{
name: "secret generator change, ok",
fields: fields{
eventstore: eventstoreExpect(
t,
expectFilter(
eventFromEventPusher(
iam.NewOIDCSettingsAddedEvent(
context.Background(),
&iam.NewAggregate().Aggregate,
time.Hour*1,
time.Hour*1,
time.Hour*1,
time.Hour*1,
),
),
),
expectPush(
[]*repository.Event{
eventFromEventPusher(
newOIDCConfigChangedEvent(context.Background(),
time.Hour*2,
time.Hour*2,
time.Hour*2,
time.Hour*2),
),
},
),
),
},
args: args{
ctx: context.Background(),
oidcConfig: &domain.OIDCSettings{
AccessTokenLifetime: 2 * time.Hour,
IdTokenLifetime: 2 * time.Hour,
RefreshTokenIdleExpiration: 2 * time.Hour,
RefreshTokenExpiration: 2 * time.Hour,
},
},
res: res{
want: &domain.ObjectDetails{
ResourceOwner: "IAM",
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := &Commands{
eventstore: tt.fields.eventstore,
}
got, err := r.ChangeOIDCSettings(tt.args.ctx, tt.args.oidcConfig)
if tt.res.err == nil {
assert.NoError(t, err)
}
if tt.res.err != nil && !tt.res.err(err) {
t.Errorf("got wrong err: %v ", err)
}
if tt.res.err == nil {
assert.Equal(t, tt.res.want, got)
}
})
}
}
func newOIDCConfigChangedEvent(ctx context.Context, accessTokenLifetime, idTokenLifetime, refreshTokenIdleExpiration, refreshTokenExpiration time.Duration) *iam.OIDCSettingsChangedEvent {
changes := []iam.OIDCSettingsChanges{
iam.ChangeOIDCSettingsAccessTokenLifetime(accessTokenLifetime),
iam.ChangeOIDCSettingsIdTokenLifetime(idTokenLifetime),
iam.ChangeOIDCSettingsRefreshTokenIdleExpiration(refreshTokenIdleExpiration),
iam.ChangeOIDCSettingsRefreshTokenExpiration(refreshTokenExpiration),
}
event, _ := iam.NewOIDCSettingsChangeEvent(ctx,
&iam.NewAggregate().Aggregate,
changes,
)
return event
}

View File

@ -0,0 +1,37 @@
package domain
import (
"time"
"github.com/caos/zitadel/internal/eventstore/v1/models"
)
type OIDCSettings struct {
models.ObjectRoot
State OIDCSettingsState
Default bool
AccessTokenLifetime time.Duration
IdTokenLifetime time.Duration
RefreshTokenIdleExpiration time.Duration
RefreshTokenExpiration time.Duration
}
type OIDCSettingsState int32
const (
OIDCSettingsStateUnspecified OIDCSettingsState = iota
OIDCSettingsStateActive
OIDCSettingsStateRemoved
oidcSettingsStateCount
)
func (c OIDCSettingsState) Valid() bool {
return c >= 0 && c < oidcSettingsStateCount
}
func (s OIDCSettingsState) Exists() bool {
return s != OIDCSettingsStateUnspecified && s != OIDCSettingsStateRemoved
}

View File

@ -0,0 +1,115 @@
package query
import (
"context"
"database/sql"
errs "errors"
"time"
sq "github.com/Masterminds/squirrel"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/query/projection"
)
var (
oidcSettingsTable = table{
name: projection.OIDCSettingsProjectionTable,
}
OIDCSettingsColumnAggregateID = Column{
name: projection.OIDCSettingsColumnAggregateID,
table: oidcSettingsTable,
}
OIDCSettingsColumnCreationDate = Column{
name: projection.OIDCSettingsColumnCreationDate,
table: oidcSettingsTable,
}
OIDCSettingsColumnChangeDate = Column{
name: projection.OIDCSettingsColumnChangeDate,
table: oidcSettingsTable,
}
OIDCSettingsColumnResourceOwner = Column{
name: projection.OIDCSettingsColumnResourceOwner,
table: oidcSettingsTable,
}
OIDCSettingsColumnSequence = Column{
name: projection.OIDCSettingsColumnSequence,
table: oidcSettingsTable,
}
OIDCSettingsColumnAccessTokenLifetime = Column{
name: projection.OIDCSettingsColumnAccessTokenLifetime,
table: oidcSettingsTable,
}
OIDCSettingsColumnIdTokenLifetime = Column{
name: projection.OIDCSettingsColumnIdTokenLifetime,
table: oidcSettingsTable,
}
OIDCSettingsColumnRefreshTokenIdleExpiration = Column{
name: projection.OIDCSettingsColumnRefreshTokenIdleExpiration,
table: oidcSettingsTable,
}
OIDCSettingsColumnRefreshTokenExpiration = Column{
name: projection.OIDCSettingsColumnRefreshTokenExpiration,
table: oidcSettingsTable,
}
)
type OIDCSettings struct {
AggregateID string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
AccessTokenLifetime time.Duration
IdTokenLifetime time.Duration
RefreshTokenIdleExpiration time.Duration
RefreshTokenExpiration time.Duration
}
func (q *Queries) OIDCSettingsByAggID(ctx context.Context, aggregateID string) (*OIDCSettings, error) {
stmt, scan := prepareOIDCSettingsQuery()
query, args, err := stmt.Where(sq.Eq{
OIDCSettingsColumnAggregateID.identifier(): aggregateID,
}).ToSql()
if err != nil {
return nil, errors.ThrowInternal(err, "QUERY-s9nle", "Errors.Query.SQLStatment")
}
row := q.client.QueryRowContext(ctx, query, args...)
return scan(row)
}
func prepareOIDCSettingsQuery() (sq.SelectBuilder, func(*sql.Row) (*OIDCSettings, error)) {
return sq.Select(
OIDCSettingsColumnAggregateID.identifier(),
OIDCSettingsColumnCreationDate.identifier(),
OIDCSettingsColumnChangeDate.identifier(),
OIDCSettingsColumnResourceOwner.identifier(),
OIDCSettingsColumnSequence.identifier(),
OIDCSettingsColumnAccessTokenLifetime.identifier(),
OIDCSettingsColumnIdTokenLifetime.identifier(),
OIDCSettingsColumnRefreshTokenIdleExpiration.identifier(),
OIDCSettingsColumnRefreshTokenExpiration.identifier()).
From(oidcSettingsTable.identifier()).PlaceholderFormat(sq.Dollar),
func(row *sql.Row) (*OIDCSettings, error) {
oidcSettings := new(OIDCSettings)
err := row.Scan(
&oidcSettings.AggregateID,
&oidcSettings.CreationDate,
&oidcSettings.ChangeDate,
&oidcSettings.ResourceOwner,
&oidcSettings.Sequence,
&oidcSettings.AccessTokenLifetime,
&oidcSettings.IdTokenLifetime,
&oidcSettings.RefreshTokenIdleExpiration,
&oidcSettings.RefreshTokenExpiration,
)
if err != nil {
if errs.Is(err, sql.ErrNoRows) {
return nil, errors.ThrowNotFound(err, "QUERY-s9nlw", "Errors.OIDCSettings.NotFound")
}
return nil, errors.ThrowInternal(err, "QUERY-9bf8s", "Errors.Internal")
}
return oidcSettings, nil
}
}

View File

@ -0,0 +1,136 @@
package query
import (
"database/sql"
"database/sql/driver"
"errors"
"fmt"
"regexp"
"testing"
"time"
errs "github.com/caos/zitadel/internal/errors"
)
func Test_OIDCConfigsPrepares(t *testing.T) {
type want struct {
sqlExpectations sqlExpectation
err checkErr
}
tests := []struct {
name string
prepare interface{}
want want
object interface{}
}{
{
name: "prepareOIDCSettingsQuery no result",
prepare: prepareOIDCSettingsQuery,
want: want{
sqlExpectations: mockQueries(
`SELECT zitadel.projections.oidc_settings.aggregate_id,`+
` zitadel.projections.oidc_settings.creation_date,`+
` zitadel.projections.oidc_settings.change_date,`+
` zitadel.projections.oidc_settings.resource_owner,`+
` zitadel.projections.oidc_settings.sequence,`+
` zitadel.projections.oidc_settings.access_token_lifetime,`+
` zitadel.projections.oidc_settings.id_token_lifetime,`+
` zitadel.projections.oidc_settings.refresh_token_idle_expiration,`+
` zitadel.projections.oidc_settings.refresh_token_expiration`+
` FROM zitadel.projections.oidc_settings`,
nil,
nil,
),
err: func(err error) (error, bool) {
if !errs.IsNotFound(err) {
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
}
return nil, true
},
},
object: (*OIDCSettings)(nil),
},
{
name: "prepareOIDCSettingsQuery found",
prepare: prepareOIDCSettingsQuery,
want: want{
sqlExpectations: mockQuery(
regexp.QuoteMeta(`SELECT zitadel.projections.oidc_settings.aggregate_id,`+
` zitadel.projections.oidc_settings.creation_date,`+
` zitadel.projections.oidc_settings.change_date,`+
` zitadel.projections.oidc_settings.resource_owner,`+
` zitadel.projections.oidc_settings.sequence,`+
` zitadel.projections.oidc_settings.access_token_lifetime,`+
` zitadel.projections.oidc_settings.id_token_lifetime,`+
` zitadel.projections.oidc_settings.refresh_token_idle_expiration,`+
` zitadel.projections.oidc_settings.refresh_token_expiration`+
` FROM zitadel.projections.oidc_settings`),
[]string{
"aggregate_id",
"creation_date",
"change_date",
"resource_owner",
"sequence",
"access_token_lifetime",
"id_token_lifetime",
"refresh_token_idle_expiration",
"refresh_token_expiration",
},
[]driver.Value{
"agg-id",
testNow,
testNow,
"ro",
uint64(20211108),
time.Minute * 1,
time.Minute * 2,
time.Minute * 3,
time.Minute * 4,
},
),
},
object: &OIDCSettings{
AggregateID: "agg-id",
CreationDate: testNow,
ChangeDate: testNow,
ResourceOwner: "ro",
Sequence: 20211108,
AccessTokenLifetime: time.Minute * 1,
IdTokenLifetime: time.Minute * 2,
RefreshTokenIdleExpiration: time.Minute * 3,
RefreshTokenExpiration: time.Minute * 4,
},
},
{
name: "prepareOIDCSettingsQuery sql err",
prepare: prepareOIDCSettingsQuery,
want: want{
sqlExpectations: mockQueryErr(
regexp.QuoteMeta(`SELECT zitadel.projections.oidc_settings.aggregate_id,`+
` zitadel.projections.oidc_settings.creation_date,`+
` zitadel.projections.oidc_settings.change_date,`+
` zitadel.projections.oidc_settings.resource_owner,`+
` zitadel.projections.oidc_settings.sequence,`+
` zitadel.projections.oidc_settings.access_token_lifetime,`+
` zitadel.projections.oidc_settings.id_token_lifetime,`+
` zitadel.projections.oidc_settings.refresh_token_idle_expiration,`+
` zitadel.projections.oidc_settings.refresh_token_expiration`+
` FROM zitadel.projections.oidc_settings`),
sql.ErrConnDone,
),
err: func(err error) (error, bool) {
if !errors.Is(err, sql.ErrConnDone) {
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
}
return nil, true
},
},
object: nil,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
})
}
}

View File

@ -32,7 +32,7 @@ type ActionProjection struct {
}
func NewActionProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ActionProjection {
p := &ActionProjection{}
p := new(ActionProjection)
config.ProjectionName = ActionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -25,7 +25,7 @@ const (
)
func NewAppProjection(ctx context.Context, config crdb.StatementHandlerConfig) *AppProjection {
p := &AppProjection{}
p := new(AppProjection)
config.ProjectionName = AppProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -35,7 +35,7 @@ type AuthNKeyProjection struct {
}
func NewAuthNKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *AuthNKeyProjection {
p := &AuthNKeyProjection{}
p := new(AuthNKeyProjection)
config.ProjectionName = AuthNKeyTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -33,7 +33,7 @@ const (
)
func NewCustomTextProjection(ctx context.Context, config crdb.StatementHandlerConfig) *CustomTextProjection {
p := &CustomTextProjection{}
p := new(CustomTextProjection)
config.ProjectionName = CustomTextTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -25,7 +25,7 @@ const (
)
func NewFeatureProjection(ctx context.Context, config crdb.StatementHandlerConfig) *FeatureProjection {
p := &FeatureProjection{}
p := new(FeatureProjection)
config.ProjectionName = FeatureTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -26,7 +26,7 @@ type FlowProjection struct {
}
func NewFlowProjection(ctx context.Context, config crdb.StatementHandlerConfig) *FlowProjection {
p := &FlowProjection{}
p := new(FlowProjection)
config.ProjectionName = FlowTriggerTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -20,7 +20,7 @@ const (
)
func NewIAMProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IAMProjection {
p := &IAMProjection{}
p := new(IAMProjection)
config.ProjectionName = IAMProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -22,7 +22,7 @@ const (
)
func NewIAMMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IAMMemberProjection {
p := &IAMMemberProjection{}
p := new(IAMMemberProjection)
config.ProjectionName = IAMMemberProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -26,7 +26,7 @@ const (
)
func NewIDPProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IDPProjection {
p := &IDPProjection{}
p := new(IDPProjection)
config.ProjectionName = IDPTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -23,7 +23,7 @@ const (
)
func NewIDPLoginPolicyLinkProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IDPLoginPolicyLinkProjection {
p := &IDPLoginPolicyLinkProjection{}
p := new(IDPLoginPolicyLinkProjection)
config.ProjectionName = IDPLoginPolicyLinkTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -18,7 +18,7 @@ type IDPUserLinkProjection struct {
}
func NewIDPUserLinkProjection(ctx context.Context, config crdb.StatementHandlerConfig) *IDPUserLinkProjection {
p := &IDPUserLinkProjection{}
p := new(IDPUserLinkProjection)
config.ProjectionName = IDPUserLinkTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -27,7 +27,7 @@ const (
)
func NewKeyProjection(ctx context.Context, config crdb.StatementHandlerConfig, keyConfig *crypto.KeyConfig, keyChan chan<- interface{}) (_ *KeyProjection, err error) {
p := &KeyProjection{}
p := new(KeyProjection)
config.ProjectionName = KeyProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -23,7 +23,7 @@ const (
)
func NewLabelPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *LabelPolicyProjection {
p := &LabelPolicyProjection{}
p := new(LabelPolicyProjection)
config.ProjectionName = LabelPolicyTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -33,7 +33,7 @@ const (
)
func NewLockoutPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *LockoutPolicyProjection {
p := &LockoutPolicyProjection{}
p := new(LockoutPolicyProjection)
config.ProjectionName = LockoutPolicyTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -26,7 +26,7 @@ const (
)
func NewLoginNameProjection(ctx context.Context, config crdb.StatementHandlerConfig) *LoginNameProjection {
p := &LoginNameProjection{}
p := new(LoginNameProjection)
config.ProjectionName = LoginNameProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -23,7 +23,7 @@ const (
)
func NewLoginPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *LoginPolicyProjection {
p := &LoginPolicyProjection{}
p := new(LoginPolicyProjection)
config.ProjectionName = LoginPolicyTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -31,7 +31,7 @@ const (
)
func NewMailTemplateProjection(ctx context.Context, config crdb.StatementHandlerConfig) *MailTemplateProjection {
p := &MailTemplateProjection{}
p := new(MailTemplateProjection)
config.ProjectionName = MailTemplateTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -39,7 +39,7 @@ const (
)
func NewMessageTextProjection(ctx context.Context, config crdb.StatementHandlerConfig) *MessageTextProjection {
p := &MessageTextProjection{}
p := new(MessageTextProjection)
config.ProjectionName = MessageTextTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -0,0 +1,114 @@
package projection
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/handler/crdb"
"github.com/caos/zitadel/internal/repository/iam"
"github.com/caos/zitadel/internal/repository/project"
)
type OIDCSettingsProjection struct {
crdb.StatementHandler
}
const (
OIDCSettingsProjectionTable = "zitadel.projections.oidc_settings"
)
func NewOIDCSettingsProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OIDCSettingsProjection {
p := new(OIDCSettingsProjection)
config.ProjectionName = OIDCSettingsProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)
return p
}
func (p *OIDCSettingsProjection) reducers() []handler.AggregateReducer {
return []handler.AggregateReducer{
{
Aggregate: project.AggregateType,
EventRedusers: []handler.EventReducer{
{
Event: iam.OIDCSettingsAddedEventType,
Reduce: p.reduceOIDCSettingsAdded,
},
{
Event: iam.OIDCSettingsChangedEventType,
Reduce: p.reduceOIDCSettingsChanged,
},
},
},
}
}
const (
OIDCSettingsColumnAggregateID = "aggregate_id"
OIDCSettingsColumnCreationDate = "creation_date"
OIDCSettingsColumnChangeDate = "change_date"
OIDCSettingsColumnResourceOwner = "resource_owner"
OIDCSettingsColumnSequence = "sequence"
OIDCSettingsColumnAccessTokenLifetime = "access_token_lifetime"
OIDCSettingsColumnIdTokenLifetime = "id_token_lifetime"
OIDCSettingsColumnRefreshTokenIdleExpiration = "refresh_token_idle_expiration"
OIDCSettingsColumnRefreshTokenExpiration = "refresh_token_expiration"
)
func (p *OIDCSettingsProjection) reduceOIDCSettingsAdded(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*iam.OIDCSettingsAddedEvent)
if !ok {
logging.WithFields("seq", event.Sequence(), "expectedType", iam.OIDCSettingsAddedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-f9nwf", "reduce.wrong.event.type")
}
return crdb.NewCreateStatement(
e,
[]handler.Column{
handler.NewCol(OIDCSettingsColumnAggregateID, e.Aggregate().ID),
handler.NewCol(OIDCSettingsColumnCreationDate, e.CreationDate()),
handler.NewCol(OIDCSettingsColumnChangeDate, e.CreationDate()),
handler.NewCol(OIDCSettingsColumnResourceOwner, e.Aggregate().ResourceOwner),
handler.NewCol(OIDCSettingsColumnSequence, e.Sequence()),
handler.NewCol(OIDCSettingsColumnAccessTokenLifetime, e.AccessTokenLifetime),
handler.NewCol(OIDCSettingsColumnIdTokenLifetime, e.IdTokenLifetime),
handler.NewCol(OIDCSettingsColumnRefreshTokenIdleExpiration, e.RefreshTokenIdleExpiration),
handler.NewCol(OIDCSettingsColumnRefreshTokenExpiration, e.RefreshTokenExpiration),
},
), nil
}
func (p *OIDCSettingsProjection) reduceOIDCSettingsChanged(event eventstore.Event) (*handler.Statement, error) {
e, ok := event.(*iam.OIDCSettingsChangedEvent)
if !ok {
logging.WithFields("seq", event.Sequence(), "expected", iam.OIDCSettingsChangedEventType).Error("wrong event type")
return nil, errors.ThrowInvalidArgument(nil, "HANDL-8JJ2d", "reduce.wrong.event.type")
}
columns := make([]handler.Column, 0, 6)
columns = append(columns,
handler.NewCol(OIDCSettingsColumnChangeDate, e.CreationDate()),
handler.NewCol(OIDCSettingsColumnSequence, e.Sequence()),
)
if e.AccessTokenLifetime != nil {
columns = append(columns, handler.NewCol(OIDCSettingsColumnAccessTokenLifetime, *e.AccessTokenLifetime))
}
if e.IdTokenLifetime != nil {
columns = append(columns, handler.NewCol(OIDCSettingsColumnIdTokenLifetime, *e.IdTokenLifetime))
}
if e.RefreshTokenIdleExpiration != nil {
columns = append(columns, handler.NewCol(OIDCSettingsColumnRefreshTokenIdleExpiration, *e.RefreshTokenIdleExpiration))
}
if e.RefreshTokenExpiration != nil {
columns = append(columns, handler.NewCol(OIDCSettingsColumnRefreshTokenExpiration, *e.RefreshTokenExpiration))
}
return crdb.NewUpdateStatement(
e,
columns,
[]handler.Condition{
handler.NewCond(OIDCSettingsColumnAggregateID, e.Aggregate().ID),
},
), nil
}

View File

@ -0,0 +1,106 @@
package projection
import (
"testing"
"time"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/handler"
"github.com/caos/zitadel/internal/eventstore/repository"
"github.com/caos/zitadel/internal/repository/iam"
)
func TestOIDCSettingsProjection_reduces(t *testing.T) {
type args struct {
event func(t *testing.T) eventstore.Event
}
tests := []struct {
name string
args args
reduce func(event eventstore.Event) (*handler.Statement, error)
want wantReduce
}{
{
name: "reduceOIDCSettingsChanged",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.OIDCSettingsChangedEventType),
iam.AggregateType,
[]byte(`{"accessTokenLifetime": 10000000, "idTokenLifetime": 10000000, "refreshTokenIdleExpiration": 10000000, "refreshTokenExpiration": 10000000}`),
), iam.OIDCSettingsChangedEventMapper),
},
reduce: (&OIDCSettingsProjection{}).reduceOIDCSettingsChanged,
want: wantReduce{
projection: OIDCSettingsProjectionTable,
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "UPDATE zitadel.projections.oidc_settings SET (change_date, sequence, access_token_lifetime, id_token_lifetime, refresh_token_idle_expiration, refresh_token_expiration) = ($1, $2, $3, $4, $5, $6) WHERE (aggregate_id = $7)",
expectedArgs: []interface{}{
anyArg{},
uint64(15),
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
"agg-id",
},
},
},
},
},
},
{
name: "reduceOIDCSettingsAdded",
args: args{
event: getEvent(testEvent(
repository.EventType(iam.OIDCSettingsAddedEventType),
iam.AggregateType,
[]byte(`{"accessTokenLifetime": 10000000, "idTokenLifetime": 10000000, "refreshTokenIdleExpiration": 10000000, "refreshTokenExpiration": 10000000}`),
), iam.OIDCSettingsAddedEventMapper),
},
reduce: (&OIDCSettingsProjection{}).reduceOIDCSettingsAdded,
want: wantReduce{
projection: OIDCSettingsProjectionTable,
aggregateType: eventstore.AggregateType("iam"),
sequence: 15,
previousSequence: 10,
executer: &testExecuter{
executions: []execution{
{
expectedStmt: "INSERT INTO zitadel.projections.oidc_settings (aggregate_id, creation_date, change_date, resource_owner, sequence, access_token_lifetime, id_token_lifetime, refresh_token_idle_expiration, refresh_token_expiration) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9)",
expectedArgs: []interface{}{
"agg-id",
anyArg{},
anyArg{},
"ro-id",
uint64(15),
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
time.Millisecond * 10,
},
},
},
},
},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
event := baseEvent(t)
got, err := tt.reduce(event)
if _, ok := err.(errors.InvalidArgument); !ok {
t.Errorf("no wrong event mapping: %v, got: %v", err, got)
}
event = tt.args.event(t)
got, err = tt.reduce(event)
assertReduce(t, got, err, tt.want)
})
}
}

View File

@ -22,7 +22,7 @@ const (
)
func NewOrgProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgProjection {
p := &OrgProjection{}
p := new(OrgProjection)
config.ProjectionName = OrgProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -22,7 +22,7 @@ const (
)
func NewOrgDomainProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgDomainProjection {
p := &OrgDomainProjection{}
p := new(OrgDomainProjection)
config.ProjectionName = OrgDomainTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -32,7 +32,7 @@ const (
)
func NewOrgIAMPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgIAMPolicyProjection {
p := &OrgIAMPolicyProjection{}
p := new(OrgIAMPolicyProjection)
config.ProjectionName = OrgIAMPolicyTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -22,7 +22,7 @@ const (
)
func NewOrgMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *OrgMemberProjection {
p := &OrgMemberProjection{}
p := new(OrgMemberProjection)
config.ProjectionName = OrgMemberProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -23,7 +23,7 @@ const (
)
func NewPasswordAgeProjection(ctx context.Context, config crdb.StatementHandlerConfig) *PasswordAgeProjection {
p := &PasswordAgeProjection{}
p := new(PasswordAgeProjection)
config.ProjectionName = PasswordAgeTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -23,7 +23,7 @@ const (
)
func NewPasswordComplexityProjection(ctx context.Context, config crdb.StatementHandlerConfig) *PasswordComplexityProjection {
p := &PasswordComplexityProjection{}
p := new(PasswordComplexityProjection)
config.ProjectionName = PasswordComplexityTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -33,7 +33,7 @@ const (
)
func NewPrivacyPolicyProjection(ctx context.Context, config crdb.StatementHandlerConfig) *PrivacyPolicyProjection {
p := &PrivacyPolicyProjection{}
p := new(PrivacyPolicyProjection)
config.ProjectionName = PrivacyPolicyTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -21,7 +21,7 @@ const (
)
func NewProjectProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectProjection {
p := &ProjectProjection{}
p := new(ProjectProjection)
config.ProjectionName = ProjectProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -20,7 +20,7 @@ type ProjectGrantProjection struct {
const ProjectGrantProjectionTable = "zitadel.projections.project_grants"
func NewProjectGrantProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectGrantProjection {
p := &ProjectGrantProjection{}
p := new(ProjectGrantProjection)
config.ProjectionName = ProjectGrantProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -24,7 +24,7 @@ const (
)
func NewProjectGrantMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectGrantMemberProjection {
p := &ProjectGrantMemberProjection{}
p := new(ProjectGrantMemberProjection)
config.ProjectionName = ProjectGrantMemberProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -24,7 +24,7 @@ const (
)
func NewProjectMemberProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectMemberProjection {
p := &ProjectMemberProjection{}
p := new(ProjectMemberProjection)
config.ProjectionName = ProjectMemberProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -18,7 +18,7 @@ type ProjectRoleProjection struct {
const ProjectRoleProjectionTable = "zitadel.projections.project_roles"
func NewProjectRoleProjection(ctx context.Context, config crdb.StatementHandlerConfig) *ProjectRoleProjection {
p := &ProjectRoleProjection{}
p := new(ProjectRoleProjection)
config.ProjectionName = ProjectRoleProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -72,6 +72,7 @@ func Start(ctx context.Context, sqlClient *sql.DB, es *eventstore.Eventstore, co
NewSecretGeneratorProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["secret_generators"]))
NewSMTPConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["smtp_configs"]))
NewSMSConfigProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["sms_config"]))
NewOIDCSettingsProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["oidc_settings"]))
_, err := NewKeyProjection(ctx, applyCustomConfig(projectionConfig, config.Customizations["keys"]), keyConfig, keyChan)
return err

View File

@ -21,7 +21,7 @@ const (
)
func NewSecretGeneratorProjection(ctx context.Context, config crdb.StatementHandlerConfig) *SecretGeneratorProjection {
p := &SecretGeneratorProjection{}
p := new(SecretGeneratorProjection)
config.ProjectionName = SecretGeneratorProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -22,7 +22,7 @@ const (
)
func NewSMSConfigProjection(ctx context.Context, config crdb.StatementHandlerConfig) *SMSConfigProjection {
p := &SMSConfigProjection{}
p := new(SMSConfigProjection)
config.ProjectionName = SMSConfigProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -21,7 +21,7 @@ const (
)
func NewSMTPConfigProjection(ctx context.Context, config crdb.StatementHandlerConfig) *SMTPConfigProjection {
p := &SMTPConfigProjection{}
p := new(SMTPConfigProjection)
config.ProjectionName = SMTPConfigProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -25,7 +25,7 @@ const (
)
func NewUserProjection(ctx context.Context, config crdb.StatementHandlerConfig) *UserProjection {
p := &UserProjection{}
p := new(UserProjection)
config.ProjectionName = UserTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -21,7 +21,7 @@ const (
)
func NewUserAuthMethodProjection(ctx context.Context, config crdb.StatementHandlerConfig) *UserAuthMethodProjection {
p := &UserAuthMethodProjection{}
p := new(UserAuthMethodProjection)
config.ProjectionName = UserAuthMethodTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -25,7 +25,7 @@ const (
)
func NewUserGrantProjection(ctx context.Context, config crdb.StatementHandlerConfig) *UserGrantProjection {
p := &UserGrantProjection{}
p := new(UserGrantProjection)
config.ProjectionName = UserGrantProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -19,7 +19,7 @@ type UserMetadataProjection struct {
const UserMetadataProjectionTable = "zitadel.projections.user_metadata"
func NewUserMetadataProjection(ctx context.Context, config crdb.StatementHandlerConfig) *UserMetadataProjection {
p := &UserMetadataProjection{}
p := new(UserMetadataProjection)
config.ProjectionName = UserMetadataProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -22,7 +22,7 @@ const (
)
func NewPersonalAccessTokenProjection(ctx context.Context, config crdb.StatementHandlerConfig) *PersonalAccessTokenProjection {
p := &PersonalAccessTokenProjection{}
p := new(PersonalAccessTokenProjection)
config.ProjectionName = PersonalAccessTokenProjectionTable
config.Reducers = p.reducers()
p.StatementHandler = crdb.NewStatementHandler(ctx, config)

View File

@ -23,6 +23,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
RegisterFilterEventMapper(SMSConfigActivatedEventType, SMSConfigActivatedEventMapper).
RegisterFilterEventMapper(SMSConfigDeactivatedEventType, SMSConfigDeactivatedEventMapper).
RegisterFilterEventMapper(SMSConfigRemovedEventType, SMSConfigRemovedEventMapper).
RegisterFilterEventMapper(OIDCSettingsAddedEventType, OIDCSettingsAddedEventMapper).
RegisterFilterEventMapper(OIDCSettingsChangedEventType, OIDCSettingsChangedEventMapper).
RegisterFilterEventMapper(LabelPolicyAddedEventType, LabelPolicyAddedEventMapper).
RegisterFilterEventMapper(LabelPolicyChangedEventType, LabelPolicyChangedEventMapper).
RegisterFilterEventMapper(LabelPolicyActivatedEventType, LabelPolicyActivatedEventMapper).

View File

@ -0,0 +1,145 @@
package iam
import (
"context"
"encoding/json"
"time"
"github.com/caos/zitadel/internal/errors"
"github.com/caos/zitadel/internal/eventstore"
"github.com/caos/zitadel/internal/eventstore/repository"
)
const (
oidcSettingsPrefix = "oidc.settings."
OIDCSettingsAddedEventType = iamEventTypePrefix + oidcSettingsPrefix + "added"
OIDCSettingsChangedEventType = iamEventTypePrefix + oidcSettingsPrefix + "changed"
OIDCSettingsRemovedEventType = iamEventTypePrefix + oidcSettingsPrefix + "removed"
)
type OIDCSettingsAddedEvent struct {
eventstore.BaseEvent `json:"-"`
AccessTokenLifetime time.Duration `json:"accessTokenLifetime,omitempty"`
IdTokenLifetime time.Duration `json:"idTokenLifetime,omitempty"`
RefreshTokenIdleExpiration time.Duration `json:"refreshTokenIdleExpiration,omitempty"`
RefreshTokenExpiration time.Duration `json:"refreshTokenExpiration,omitempty"`
}
func NewOIDCSettingsAddedEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
accessTokenLifetime,
idTokenLifetime,
refreshTokenIdleExpiration,
refreshTokenExpiration time.Duration,
) *OIDCSettingsAddedEvent {
return &OIDCSettingsAddedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OIDCSettingsAddedEventType,
),
AccessTokenLifetime: accessTokenLifetime,
IdTokenLifetime: idTokenLifetime,
RefreshTokenIdleExpiration: refreshTokenIdleExpiration,
RefreshTokenExpiration: refreshTokenExpiration,
}
}
func (e *OIDCSettingsAddedEvent) Data() interface{} {
return e
}
func (e *OIDCSettingsAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func OIDCSettingsAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
oidcSettingsAdded := &OIDCSettingsAddedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, oidcSettingsAdded)
if err != nil {
return nil, errors.ThrowInternal(err, "IAM-soiwj", "unable to unmarshal oidc config added")
}
return oidcSettingsAdded, nil
}
type OIDCSettingsChangedEvent struct {
eventstore.BaseEvent `json:"-"`
AccessTokenLifetime *time.Duration `json:"accessTokenLifetime,omitempty"`
IdTokenLifetime *time.Duration `json:"idTokenLifetime,omitempty"`
RefreshTokenIdleExpiration *time.Duration `json:"refreshTokenIdleExpiration,omitempty"`
RefreshTokenExpiration *time.Duration `json:"refreshTokenExpiration,omitempty"`
}
func (e *OIDCSettingsChangedEvent) Data() interface{} {
return e
}
func (e *OIDCSettingsChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
return nil
}
func NewOIDCSettingsChangeEvent(
ctx context.Context,
aggregate *eventstore.Aggregate,
changes []OIDCSettingsChanges,
) (*OIDCSettingsChangedEvent, error) {
if len(changes) == 0 {
return nil, errors.ThrowPreconditionFailed(nil, "IAM-dnlwe", "Errors.NoChangesFound")
}
changeEvent := &OIDCSettingsChangedEvent{
BaseEvent: *eventstore.NewBaseEventForPush(
ctx,
aggregate,
OIDCSettingsChangedEventType,
),
}
for _, change := range changes {
change(changeEvent)
}
return changeEvent, nil
}
type OIDCSettingsChanges func(event *OIDCSettingsChangedEvent)
func ChangeOIDCSettingsAccessTokenLifetime(accessTokenLifetime time.Duration) func(event *OIDCSettingsChangedEvent) {
return func(e *OIDCSettingsChangedEvent) {
e.AccessTokenLifetime = &accessTokenLifetime
}
}
func ChangeOIDCSettingsIdTokenLifetime(idTokenLifetime time.Duration) func(event *OIDCSettingsChangedEvent) {
return func(e *OIDCSettingsChangedEvent) {
e.IdTokenLifetime = &idTokenLifetime
}
}
func ChangeOIDCSettingsRefreshTokenIdleExpiration(refreshTokenIdleExpiration time.Duration) func(event *OIDCSettingsChangedEvent) {
return func(e *OIDCSettingsChangedEvent) {
e.RefreshTokenIdleExpiration = &refreshTokenIdleExpiration
}
}
func ChangeOIDCSettingsRefreshTokenExpiration(refreshTokenExpiration time.Duration) func(event *OIDCSettingsChangedEvent) {
return func(e *OIDCSettingsChangedEvent) {
e.RefreshTokenExpiration = &refreshTokenExpiration
}
}
func OIDCSettingsChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
e := &OIDCSettingsChangedEvent{
BaseEvent: *eventstore.BaseEventFromRepo(event),
}
err := json.Unmarshal(event.Data, e)
if err != nil {
return nil, errors.ThrowInternal(err, "IAM-f98uf", "unable to unmarshal oidc settings changed")
}
return e, nil
}

View File

@ -29,6 +29,20 @@ Errors:
ExceedsDefault: Limit überschreitet default Limit
Language:
NotParsed: Sprache konnte nicht gemapped werde
OIDCSettings:
NotFound: OIDC Konfiguration konnte nicht gefunden werden
AlreadyExists: OIDC Konfiguration existiert bereits
SecretGenerator:
AlreadyExists: Passwort Generator exisiter bereits
TypeMissing: Passwort Generator Typ fehlt
NotFound: Passwort Generator nicht gefunden
SMSConfig:
NotFound: SMS Konfiguration nicht gefunden
AlreadyActive: SMS Konfiguration ist bereits aktiviert
AlreadyDeactivated: SMS Konfiguration ist bereits deaktiviert
SMTPConfig:
NotFound: SMTP Konfiguration nicht gefunden
AlreadyExists: SMTP Konfiguration existiert bereits
User:
NotFound: Benutzer konnte nicht gefunden werden
AlreadyExists: Benutzer existierts bereits
@ -816,6 +830,35 @@ EventTypes:
removed: Schrift von Label Richtlinie entfernt
assets:
removed: Bilder und Schrift von Label Richtlinie entfernt
default:
language:
set: Standard Sprache gesetzt
oidc:
settings:
added: OIDC Konfiguration hinzugefügt
changed: OIDC Konfiguration geändert
removed: OIDC Konfiguration gelöscht
secret:
generator:
added: Passwort Generator hinzugefügt
changed: Passwort Generator geändert
removed: Passwort Generator gelöscht
smtp:
config:
added: SMTP Konfiguration hinzugefügt
changed: SMTP Konfiguration geändert
password:
changed: SMTP Konfigurations Passwort geändert
sms:
config:
twilio:
added: Twilio SMS Provider hinzugefügt
changed: Twilio SMS Provider geändert
token:
changed: Twilio SMS Provider Token geändert
removed: Twilio SMS Provider entfernt
activated: Twilio SMS Provider aktiviert
deactivated: Twilio SMS Provider deaktiviert
key_pair:
added: Schlüsselpaar hinzugefügt
action:

View File

@ -29,6 +29,20 @@ Errors:
ExceedsDefault: Limit exceeds default limit
Language:
NotParsed: Could not parse languge
OIDCSettings:
NotFound: OIDC Configuration not found
AlreadyExists: OIDC configuration already exists
SecretGenerator:
AlreadyExists: Secret generator already exists
TypeMissing: Secret generator type missing
NotFound: Secret generator not found
SMSConfig:
NotFound: SMS configuration not found
AlreadyActive: SMS configuration already active
AlreadyDeactivated: SMS configuration already deactived
SMTPConfig:
NotFound: SMTP configuration not found
AlreadyExists: SMTP configuration already exists
User:
NotFound: User could not be found
AlreadyExists: User already exists
@ -813,6 +827,35 @@ EventTypes:
removed: Font removed from Label Policy
assets:
removed: Assets removed from Label Policy
default:
language:
set: Default language set
oidc:
settings:
added: OIDC configuration added
changed: OIDC configuration changed
removed: OIDC configuration removed
secret:
generator:
added: Secret generator added
changed: Secret generator changed
removed: Secret generator removed
smtp:
config:
added: SMTP configuration added
changed: SMTP configuration changed
password:
changed: SMTP configuration secret changed
sms:
config:
twilio:
added: Twilio SMS provider added
changed: Twilio SMS provider changed
token:
changed: Twilio SMS provider token changed
removed: Twilio SMS provider removed
activated: Twilio SMS provider activated
deactivated: Twilio SMS provider deactivated
key_pair:
added: Key pair added
action:
@ -821,6 +864,7 @@ EventTypes:
deactivated: Action deactivated
reactivated: Action reactivated
removed: Action removed
Application:
OIDC:
UnsupportedVersion: Your OIDC version is not supported

View File

@ -29,6 +29,20 @@ Errors:
ExceedsDefault: Il limite supera quello predefinito
Language:
NotParsed: Impossibile analizzare la lingua
OIDCSettings:
NotFound: Impossibile trovare la configurazione OIDC
AlreadyExists: La configurazione OIDC esiste già
SecretGenerator:
AlreadyExists: Il generatore di segreti esiste già
TypeMissing: Manca il tipo di generatore segreto
NotFound: Generatore segreto non trovato
SMSConfig:
NotFound: Configurazione SMS non trovata
AlreadyActive: Configurazione SMS già attiva
AlreadyDeactivated: Configurazione SMS già disattivata
SMTPConfig:
NotFound: Configurazione SMTP non trovata
AlreadyExists: La configurazione SMTP esiste già
User:
NotFound: L'utente non è stato trovato
AlreadyExists: L'utente già esistente
@ -811,6 +825,35 @@ EventTypes:
removed: Font rimosso
assets:
removed: Asset rimosse
default:
language:
set: lingua predefinita impostata
oidc:
settings:
added: Configurazione OIDC aggiunta
changed: Configurazione OIDC cambiata
removed: Configurazione OIDC rimossa
secret:
generator:
added: Generatore di segreti aggiunto
changed: Generatore di segreti cambiato
removed: Generatore di segreti cambiato
smtp:
config:
added: SMTP configuration added
changed: SMTP configuration changed
password:
changed: SMTP configuration secret changed
sms:
config:
twilio:
added: Aggiunto il fornitore di SMS Twilio
changed: Twilio SMS Provider cambiato
token:
changed: Twilio SMS Provider token cambiato
removed: Provider SMS Twilio rimosso
activated: Provider SMS Twilio attivato
deactivated: Provider SMS Twilio disattivato
key_pair:
added: Keypair aggiunto
action:

View File

@ -313,6 +313,28 @@ service AdminService {
};
}
// Get OIDC settings (e.g token lifetimes, etc.)
rpc GetOIDCSettings(GetOIDCSettingsRequest) returns (GetOIDCSettingsResponse) {
option (google.api.http) = {
get: "/settings/oidc";
};
option (zitadel.v1.auth_option) = {
permission: "iam.read";
};
}
// Update oidc settings (e.g token lifetimes, etc)
rpc UpdateOIDCSettings(UpdateOIDCSettingsRequest) returns (UpdateOIDCSettingsResponse) {
option (google.api.http) = {
put: "/settings/oidc";
body: "*"
};
option (zitadel.v1.auth_option) = {
permission: "iam.write";
};
}
// Returns an organisation by id
rpc GetOrgByID(GetOrgByIDRequest) returns (GetOrgByIDResponse) {
@ -2539,6 +2561,24 @@ message UpdateSMSProviderTwilioTokenResponse {
zitadel.v1.ObjectDetails details = 1;
}
// This is an empty request
message GetOIDCSettingsRequest {}
message GetOIDCSettingsResponse {
zitadel.settings.v1.OIDCSettings settings = 1;
}
message UpdateOIDCSettingsRequest {
google.protobuf.Duration access_token_lifetime = 1;
google.protobuf.Duration id_token_lifetime = 2;
google.protobuf.Duration refresh_token_idle_expiration = 3;
google.protobuf.Duration refresh_token_expiration = 4;
}
message UpdateOIDCSettingsResponse {
zitadel.v1.ObjectDetails details = 1;
}
// if name or domain is already in use, org is not unique
message IsOrgUniqueRequest {
option (grpc.gateway.protoc_gen_openapiv2.options.openapiv2_schema) = {

View File

@ -20,7 +20,6 @@ message SecretGenerator {
bool include_symbols = 8;
}
message SecretGeneratorQuery {
oneof query {
option (validate.required) = true;
@ -67,9 +66,16 @@ message TwilioConfig {
string sender_number = 2;
}
enum SMSProviderConfigState {
SMS_PROVIDER_CONFIG_STATE_UNSPECIFIED = 0;
SMS_PROVIDER_CONFIG_ACTIVE = 1;
SMS_PROVIDER_CONFIG_INACTIVE = 2;
}
message OIDCSettings {
zitadel.v1.ObjectDetails details = 1;
google.protobuf.Duration access_token_lifetime = 2;
google.protobuf.Duration id_token_lifetime = 3;
google.protobuf.Duration refresh_token_idle_expiration = 4;
google.protobuf.Duration refresh_token_expiration = 5;
}