feat: dynamic issuer (#3481)

* feat: dynamic issuer

* dynamic domain handling

* key rotation durations

* feat: dynamic issuer

* make webauthn displayname dynamic
This commit is contained in:
Livio Amstutz
2022-04-25 10:01:17 +02:00
committed by GitHub
parent 3d5891eb11
commit 75ec73ca4a
41 changed files with 403 additions and 348 deletions

View File

@@ -42,7 +42,7 @@ type Commands struct {
domainVerificationValidator func(domain, token, verifier string, checkType http.CheckType) error
multifactors domain.MultifactorConfigs
webauthn *webauthn_helper.WebAuthN
webauthnConfig *webauthn_helper.Config
keySize int
keyAlgorithm crypto.EncryptionAlgorithm
privateKeyLifetime time.Duration
@@ -60,7 +60,7 @@ func StartCommands(es *eventstore.Eventstore,
zitadelRoles []authz.RoleMapping,
staticStore static.Storage,
authZRepo authz_repo.Repository,
webAuthN webauthn_helper.Config,
webAuthN *webauthn_helper.Config,
idpConfigEncryption,
otpEncryption,
smtpEncryption,
@@ -84,6 +84,7 @@ func StartCommands(es *eventstore.Eventstore,
userEncryption: userEncryption,
domainVerificationAlg: domainVerificationEncryption,
keyAlgorithm: oidcEncryption,
webauthnConfig: webAuthN,
}
instance_repo.RegisterEventMappers(repo.eventstore)
@@ -107,11 +108,6 @@ func StartCommands(es *eventstore.Eventstore,
repo.domainVerificationGenerator = crypto.NewEncryptionGenerator(defaults.DomainVerification.VerificationGenerator, repo.domainVerificationAlg)
repo.domainVerificationValidator = http.ValidateDomain
web, err := webauthn_helper.StartServer(webAuthN)
if err != nil {
return nil, err
}
repo.webauthn = web
repo.tokenVerifier = authZRepo
return repo, nil

View File

@@ -28,16 +28,6 @@ const (
consolePostLogoutPath = console.HandlerPrefix + "/signedout"
)
type AddInstance struct {
InstanceName string
CustomDomain string
FirstOrgName string
OwnerEmail string
OwnerUsername string
OwnerFirstName string
OwnerLastName string
}
type InstanceSetup struct {
zitadel ZitadelConfig
InstanceName string
@@ -301,7 +291,7 @@ func (c *Commands) SetUpInstance(ctx context.Context, setup *InstanceSetup, exte
}
if setup.CustomDomain != "" {
validations = append(validations, c.addInstanceDomain(instanceAgg, setup.CustomDomain, false))
validations = append(validations, addInstanceDomain(instanceAgg, setup.CustomDomain, false))
}
console := &addOIDCApp{

View File

@@ -15,7 +15,7 @@ import (
func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.addInstanceDomain(instanceAgg, instanceDomain, false)
validation := addInstanceDomain(instanceAgg, instanceDomain, false)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil {
return nil, err
@@ -33,7 +33,7 @@ func (c *Commands) AddInstanceDomain(ctx context.Context, instanceDomain string)
func (c *Commands) SetPrimaryInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.setPrimaryInstanceDomain(instanceAgg, instanceDomain)
validation := setPrimaryInstanceDomain(instanceAgg, instanceDomain)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil {
return nil, err
@@ -51,7 +51,7 @@ func (c *Commands) SetPrimaryInstanceDomain(ctx context.Context, instanceDomain
func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain string) (*domain.ObjectDetails, error) {
instanceAgg := instance.NewAggregate(authz.GetInstance(ctx).InstanceID())
validation := c.removeInstanceDomain(instanceAgg, instanceDomain)
validation := removeInstanceDomain(instanceAgg, instanceDomain)
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, validation)
if err != nil {
return nil, err
@@ -69,16 +69,16 @@ func (c *Commands) RemoveInstanceDomain(ctx context.Context, instanceDomain stri
func (c *Commands) addGeneratedInstanceDomain(a *instance.Aggregate, instanceName string) preparation.Validation {
domain := domain.NewGeneratedInstanceDomain(instanceName, c.iamDomain)
return c.addInstanceDomain(a, domain, true)
return addInstanceDomain(a, domain, true)
}
func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain string, generated bool) preparation.Validation {
func addInstanceDomain(a *instance.Aggregate, instanceDomain string, generated bool) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
return nil, errors.ThrowInvalidArgument(nil, "INST-28nlD", "Errors.Invalid.Argument")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
domainWriteModel, err := c.getInstanceDomainWriteModel(ctx, instanceDomain)
domainWriteModel, err := getInstanceDomainWriteModel(ctx, filter, instanceDomain)
if err != nil {
return nil, err
}
@@ -88,7 +88,7 @@ func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain strin
events := []eventstore.Command{
instance.NewDomainAddedEvent(ctx, &a.Aggregate, instanceDomain, generated),
}
appWriteModel, err := c.getOIDCAppWriteModel(ctx, authz.GetInstance(ctx).ProjectID(), authz.GetInstance(ctx).ConsoleApplicationID(), "")
appWriteModel, err := getOIDCAppWriteModel(ctx, filter, authz.GetInstance(ctx).ProjectID(), authz.GetInstance(ctx).ConsoleApplicationID(), "")
if err != nil {
return nil, err
}
@@ -115,13 +115,13 @@ func (c *Commands) addInstanceDomain(a *instance.Aggregate, instanceDomain strin
}
}
func (c *Commands) setPrimaryInstanceDomain(a *instance.Aggregate, instanceDomain string) preparation.Validation {
func setPrimaryInstanceDomain(a *instance.Aggregate, instanceDomain string) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
return nil, errors.ThrowInvalidArgument(nil, "INST-9mWjf", "Errors.Invalid.Argument")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
domainWriteModel, err := c.getInstanceDomainWriteModel(ctx, instanceDomain)
domainWriteModel, err := getInstanceDomainWriteModel(ctx, filter, instanceDomain)
if err != nil {
return nil, err
}
@@ -133,13 +133,13 @@ func (c *Commands) setPrimaryInstanceDomain(a *instance.Aggregate, instanceDomai
}
}
func (c *Commands) removeInstanceDomain(a *instance.Aggregate, instanceDomain string) preparation.Validation {
func removeInstanceDomain(a *instance.Aggregate, instanceDomain string) preparation.Validation {
return func() (preparation.CreateCommands, error) {
if instanceDomain = strings.TrimSpace(instanceDomain); instanceDomain == "" {
return nil, errors.ThrowInvalidArgument(nil, "INST-39nls", "Errors.Invalid.Argument")
}
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
domainWriteModel, err := c.getInstanceDomainWriteModel(ctx, instanceDomain)
domainWriteModel, err := getInstanceDomainWriteModel(ctx, filter, instanceDomain)
if err != nil {
return nil, err
}
@@ -154,11 +154,16 @@ func (c *Commands) removeInstanceDomain(a *instance.Aggregate, instanceDomain st
}
}
func (c *Commands) getInstanceDomainWriteModel(ctx context.Context, domain string) (*InstanceDomainWriteModel, error) {
func getInstanceDomainWriteModel(ctx context.Context, filter preparation.FilterToQueryReducer, domain string) (*InstanceDomainWriteModel, error) {
domainWriteModel := NewInstanceDomainWriteModel(ctx, domain)
err := c.eventstore.FilterToQueryReducer(ctx, domainWriteModel)
events, err := filter(ctx, domainWriteModel.Query())
if err != nil {
return nil, err
}
return domainWriteModel, nil
if len(events) == 0 {
return domainWriteModel, nil
}
domainWriteModel.AppendEvents(events...)
err = domainWriteModel.Reduce()
return domainWriteModel, err
}

View File

@@ -10,12 +10,7 @@ import (
"github.com/caos/zitadel/internal/repository/keypair"
)
const (
oidcUser = "OIDC"
)
func (c *Commands) GenerateSigningKeyPair(ctx context.Context, algorithm string) error {
ctx = setOIDCCtx(ctx)
privateCrypto, publicCrypto, err := crypto.GenerateEncryptedKeyPair(c.keySize, c.keyAlgorithm)
if err != nil {
return err
@@ -28,8 +23,7 @@ func (c *Commands) GenerateSigningKeyPair(ctx context.Context, algorithm string)
privateKeyExp := time.Now().UTC().Add(c.privateKeyLifetime)
publicKeyExp := time.Now().UTC().Add(c.publicKeyLifetime)
//TODO: InstanceID not available here?
keyPairWriteModel := NewKeyPairWriteModel(keyID, "system") //TODO: change with multi issuer
keyPairWriteModel := NewKeyPairWriteModel(keyID, authz.GetInstance(ctx).InstanceID())
keyAgg := KeyPairAggregateFromWriteModel(&keyPairWriteModel.WriteModel)
_, err = c.eventstore.Push(ctx, keypair.NewAddedEvent(
ctx,
@@ -40,8 +34,3 @@ func (c *Commands) GenerateSigningKeyPair(ctx context.Context, algorithm string)
privateKeyExp, publicKeyExp))
return err
}
func setOIDCCtx(ctx context.Context) context.Context {
//TODO: InstanceID not available here?
return authz.SetCtxData(ctx, authz.CtxData{UserID: oidcUser, OrgID: authz.GetInstance(ctx).InstanceID()})
}

View File

@@ -3,8 +3,7 @@ package command
import (
"github.com/caos/zitadel/internal/domain"
"github.com/caos/zitadel/internal/eventstore"
keypair "github.com/caos/zitadel/internal/repository/keypair"
"github.com/caos/zitadel/internal/repository/project"
"github.com/caos/zitadel/internal/repository/keypair"
)
type KeyPairWriteModel struct {
@@ -52,7 +51,7 @@ func (wm *KeyPairWriteModel) Query() *eventstore.SearchQueryBuilder {
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
ResourceOwner(wm.ResourceOwner).
AddQuery().
AggregateTypes(project.AggregateType).
AggregateTypes(keypair.AggregateType).
AggregateIDs(wm.AggregateID).
EventTypes(keypair.AddedEventType).
Builder()

View File

@@ -321,3 +321,17 @@ func (c *Commands) getOIDCAppWriteModel(ctx context.Context, projectID, appID, r
}
return appWriteModel, nil
}
func getOIDCAppWriteModel(ctx context.Context, filter preparation.FilterToQueryReducer, projectID, appID, resourceOwner string) (*OIDCApplicationWriteModel, error) {
appWriteModel := NewOIDCApplicationWriteModelWithAppID(projectID, appID, resourceOwner)
events, err := filter(ctx, appWriteModel.Query())
if err != nil {
return nil, err
}
if len(events) == 0 {
return appWriteModel, nil
}
appWriteModel.AppendEvents(events...)
err = appWriteModel.Reduce()
return appWriteModel, err
}

View File

@@ -6,7 +6,7 @@ import (
"testing"
"time"
"github.com/caos/oidc/pkg/oidc"
"github.com/caos/oidc/v2/pkg/oidc"
"github.com/golang/mock/gomock"
"github.com/stretchr/testify/assert"

View File

@@ -157,7 +157,7 @@ func (c *Commands) addHumanWebAuthN(ctx context.Context, userID, resourceowner s
if accountName == "" {
accountName = user.EmailAddress
}
webAuthN, err := c.webauthn.BeginRegistration(user, accountName, authenticatorPlatform, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
webAuthN, err := c.webauthnConfig.BeginRegistration(ctx, user, accountName, authenticatorPlatform, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
if err != nil {
return nil, nil, nil, err
}
@@ -272,7 +272,7 @@ func (c *Commands) verifyHumanWebAuthN(ctx context.Context, userID, resourceowne
return nil, nil, nil, err
}
_, token := domain.GetTokenToVerify(tokens)
webAuthN, err := c.webauthn.FinishRegistration(user, token, tokenName, credentialData, userAgentID != "")
webAuthN, err := c.webauthnConfig.FinishRegistration(ctx, user, token, tokenName, credentialData, userAgentID != "")
if err != nil {
return nil, nil, nil, err
}
@@ -343,7 +343,7 @@ func (c *Commands) beginWebAuthNLogin(ctx context.Context, userID, resourceOwner
if err != nil {
return nil, nil, err
}
webAuthNLogin, err := c.webauthn.BeginLogin(human, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
webAuthNLogin, err := c.webauthnConfig.BeginLogin(ctx, human, domain.UserVerificationRequirementDiscouraged, isLoginUI, tokens...)
if err != nil {
return nil, nil, err
}
@@ -454,7 +454,7 @@ func (c *Commands) finishWebAuthNLogin(ctx context.Context, userID, resourceOwne
if err != nil {
return nil, nil, 0, err
}
keyID, signCount, err := c.webauthn.FinishLogin(human, webAuthN, credentialData, isLoginUI, tokens...)
keyID, signCount, err := c.webauthnConfig.FinishLogin(ctx, human, webAuthN, credentialData, isLoginUI, tokens...)
if err != nil && keyID == nil {
return nil, nil, 0, err
}