mirror of
https://github.com/zitadel/zitadel.git
synced 2025-01-05 14:37:45 +00:00
feat: add azure provider templates (#5441)
Adds possibility to manage and use Microsoft Azure template based providers
This commit is contained in:
parent
93e1fe0056
commit
5a307afe62
@ -241,6 +241,27 @@ func (s *Server) UpdateJWTProvider(ctx context.Context, req *admin_pb.UpdateJWTP
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddAzureADProvider(ctx context.Context, req *admin_pb.AddAzureADProviderRequest) (*admin_pb.AddAzureADProviderResponse, error) {
|
||||
id, details, err := s.command.AddInstanceAzureADProvider(ctx, addAzureADProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.AddAzureADProviderResponse{
|
||||
Id: id,
|
||||
Details: object_pb.DomainToAddDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateAzureADProvider(ctx context.Context, req *admin_pb.UpdateAzureADProviderRequest) (*admin_pb.UpdateAzureADProviderResponse, error) {
|
||||
details, err := s.command.UpdateInstanceAzureADProvider(ctx, req.Id, updateAzureADProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateAzureADProviderResponse{
|
||||
Details: object_pb.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddGitHubProvider(ctx context.Context, req *admin_pb.AddGitHubProviderRequest) (*admin_pb.AddGitHubProviderResponse, error) {
|
||||
id, details, err := s.command.AddInstanceGitHubProvider(ctx, addGitHubProviderToCommand(req))
|
||||
if err != nil {
|
||||
|
@ -273,6 +273,30 @@ func updateJWTProviderToCommand(req *admin_pb.UpdateJWTProviderRequest) command.
|
||||
}
|
||||
}
|
||||
|
||||
func addAzureADProviderToCommand(req *admin_pb.AddAzureADProviderRequest) command.AzureADProvider {
|
||||
return command.AzureADProvider{
|
||||
Name: req.Name,
|
||||
ClientID: req.ClientId,
|
||||
ClientSecret: req.ClientSecret,
|
||||
Scopes: req.Scopes,
|
||||
Tenant: idp_grpc.AzureADTenantToCommand(req.Tenant),
|
||||
EmailVerified: req.EmailVerified,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func updateAzureADProviderToCommand(req *admin_pb.UpdateAzureADProviderRequest) command.AzureADProvider {
|
||||
return command.AzureADProvider{
|
||||
Name: req.Name,
|
||||
ClientID: req.ClientId,
|
||||
ClientSecret: req.ClientSecret,
|
||||
Scopes: req.Scopes,
|
||||
Tenant: idp_grpc.AzureADTenantToCommand(req.Tenant),
|
||||
EmailVerified: req.EmailVerified,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func addGitHubProviderToCommand(req *admin_pb.AddGitHubProviderRequest) command.GitHubProvider {
|
||||
return command.GitHubProvider{
|
||||
Name: req.Name,
|
||||
|
@ -4,6 +4,7 @@ import (
|
||||
obj_grpc "github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
iam_model "github.com/zitadel/zitadel/internal/iam/model"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/azuread"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/repository/idp"
|
||||
idp_pb "github.com/zitadel/zitadel/pkg/grpc/idp"
|
||||
@ -329,6 +330,33 @@ func LDAPAttributesToCommand(attributes *idp_pb.LDAPAttributes) idp.LDAPAttribut
|
||||
}
|
||||
}
|
||||
|
||||
func AzureADTenantToCommand(tenant *idp_pb.AzureADTenant) string {
|
||||
if tenant == nil {
|
||||
return string(azuread.CommonTenant)
|
||||
}
|
||||
switch t := tenant.Type.(type) {
|
||||
case *idp_pb.AzureADTenant_TenantType:
|
||||
return string(azureADTenantTypeToCommand(t.TenantType))
|
||||
case *idp_pb.AzureADTenant_TenantId:
|
||||
return t.TenantId
|
||||
default:
|
||||
return string(azuread.CommonTenant)
|
||||
}
|
||||
}
|
||||
|
||||
func azureADTenantTypeToCommand(tenantType idp_pb.AzureADTenantType) azuread.TenantType {
|
||||
switch tenantType {
|
||||
case idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_COMMON:
|
||||
return azuread.CommonTenant
|
||||
case idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS:
|
||||
return azuread.OrganizationsTenant
|
||||
case idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_CONSUMERS:
|
||||
return azuread.ConsumersTenant
|
||||
default:
|
||||
return azuread.CommonTenant
|
||||
}
|
||||
}
|
||||
|
||||
func ProvidersToPb(providers []*query.IDPTemplate) []*idp_pb.Provider {
|
||||
list := make([]*idp_pb.Provider, len(providers))
|
||||
for i, provider := range providers {
|
||||
@ -412,6 +440,10 @@ func configToPb(config *query.IDPTemplate) *idp_pb.ProviderConfig {
|
||||
jwtConfigToPb(providerConfig, config.JWTIDPTemplate)
|
||||
return providerConfig
|
||||
}
|
||||
if config.AzureADIDPTemplate != nil {
|
||||
azureConfigToPb(providerConfig, config.AzureADIDPTemplate)
|
||||
return providerConfig
|
||||
}
|
||||
if config.GitHubIDPTemplate != nil {
|
||||
githubConfigToPb(providerConfig, config.GitHubIDPTemplate)
|
||||
return providerConfig
|
||||
@ -473,6 +505,32 @@ func jwtConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.JWTIDP
|
||||
}
|
||||
}
|
||||
|
||||
func azureConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.AzureADIDPTemplate) {
|
||||
providerConfig.Config = &idp_pb.ProviderConfig_AzureAd{
|
||||
AzureAd: &idp_pb.AzureADConfig{
|
||||
ClientId: template.ClientID,
|
||||
Tenant: azureTenantToPb(template.Tenant),
|
||||
EmailVerified: template.IsEmailVerified,
|
||||
Scopes: template.Scopes,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func azureTenantToPb(tenant string) *idp_pb.AzureADTenant {
|
||||
var tenantType idp_pb.IsAzureADTenantType
|
||||
switch azuread.TenantType(tenant) {
|
||||
case azuread.CommonTenant:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantType{TenantType: idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_COMMON}
|
||||
case azuread.OrganizationsTenant:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantType{TenantType: idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_ORGANISATIONS}
|
||||
case azuread.ConsumersTenant:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantType{TenantType: idp_pb.AzureADTenantType_AZURE_AD_TENANT_TYPE_CONSUMERS}
|
||||
default:
|
||||
tenantType = &idp_pb.AzureADTenant_TenantId{TenantId: tenant}
|
||||
}
|
||||
return &idp_pb.AzureADTenant{Type: tenantType}
|
||||
}
|
||||
|
||||
func githubConfigToPb(providerConfig *idp_pb.ProviderConfig, template *query.GitHubIDPTemplate) {
|
||||
providerConfig.Config = &idp_pb.ProviderConfig_Github{
|
||||
Github: &idp_pb.GitHubConfig{
|
||||
|
@ -233,6 +233,27 @@ func (s *Server) UpdateJWTProvider(ctx context.Context, req *mgmt_pb.UpdateJWTPr
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddAzureADProvider(ctx context.Context, req *mgmt_pb.AddAzureADProviderRequest) (*mgmt_pb.AddAzureADProviderResponse, error) {
|
||||
id, details, err := s.command.AddOrgAzureADProvider(ctx, authz.GetCtxData(ctx).OrgID, addAzureADProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddAzureADProviderResponse{
|
||||
Id: id,
|
||||
Details: object_pb.DomainToAddDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateAzureADProvider(ctx context.Context, req *mgmt_pb.UpdateAzureADProviderRequest) (*mgmt_pb.UpdateAzureADProviderResponse, error) {
|
||||
details, err := s.command.UpdateOrgAzureADProvider(ctx, authz.GetCtxData(ctx).OrgID, req.Id, updateAzureADProviderToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.UpdateAzureADProviderResponse{
|
||||
Details: object_pb.DomainToChangeDetailsPb(details),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) AddGitHubProvider(ctx context.Context, req *mgmt_pb.AddGitHubProviderRequest) (*mgmt_pb.AddGitHubProviderResponse, error) {
|
||||
id, details, err := s.command.AddOrgGitHubProvider(ctx, authz.GetCtxData(ctx).OrgID, addGitHubProviderToCommand(req))
|
||||
if err != nil {
|
||||
|
@ -290,6 +290,28 @@ func updateJWTProviderToCommand(req *mgmt_pb.UpdateJWTProviderRequest) command.J
|
||||
}
|
||||
}
|
||||
|
||||
func addAzureADProviderToCommand(req *mgmt_pb.AddAzureADProviderRequest) command.AzureADProvider {
|
||||
return command.AzureADProvider{
|
||||
Name: req.Name,
|
||||
ClientID: req.ClientId,
|
||||
ClientSecret: req.ClientSecret,
|
||||
Tenant: idp_grpc.AzureADTenantToCommand(req.Tenant),
|
||||
EmailVerified: req.EmailVerified,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func updateAzureADProviderToCommand(req *mgmt_pb.UpdateAzureADProviderRequest) command.AzureADProvider {
|
||||
return command.AzureADProvider{
|
||||
Name: req.Name,
|
||||
ClientID: req.ClientId,
|
||||
ClientSecret: req.ClientSecret,
|
||||
Tenant: idp_grpc.AzureADTenantToCommand(req.Tenant),
|
||||
EmailVerified: req.EmailVerified,
|
||||
IDPOptions: idp_grpc.OptionsToCommand(req.ProviderOptions),
|
||||
}
|
||||
}
|
||||
|
||||
func addGitHubProviderToCommand(req *mgmt_pb.AddGitHubProviderRequest) command.GitHubProvider {
|
||||
return command.GitHubProvider{
|
||||
Name: req.Name,
|
||||
|
@ -18,6 +18,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/azuread"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/github"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/gitlab"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/google"
|
||||
@ -144,6 +145,8 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domai
|
||||
provider, err = l.oidcProvider(r.Context(), identityProvider)
|
||||
case domain.IDPTypeJWT:
|
||||
provider, err = l.jwtProvider(identityProvider)
|
||||
case domain.IDPTypeAzureAD:
|
||||
provider, err = l.azureProvider(r.Context(), identityProvider)
|
||||
case domain.IDPTypeGitHub:
|
||||
provider, err = l.githubProvider(r.Context(), identityProvider)
|
||||
case domain.IDPTypeGitHubEnterprise:
|
||||
@ -155,7 +158,6 @@ func (l *Login) handleIDP(w http.ResponseWriter, r *http.Request, authReq *domai
|
||||
case domain.IDPTypeGoogle:
|
||||
provider, err = l.googleProvider(r.Context(), identityProvider)
|
||||
case domain.IDPTypeLDAP,
|
||||
domain.IDPTypeAzureAD,
|
||||
domain.IDPTypeUnspecified:
|
||||
fallthrough
|
||||
default:
|
||||
@ -212,6 +214,13 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
|
||||
return
|
||||
}
|
||||
session = &openid.Session{Provider: provider.(*openid.Provider), Code: data.Code}
|
||||
case domain.IDPTypeAzureAD:
|
||||
provider, err = l.azureProvider(r.Context(), identityProvider)
|
||||
if err != nil {
|
||||
l.externalAuthFailed(w, r, authReq, nil, nil, err)
|
||||
return
|
||||
}
|
||||
session = &oauth.Session{Provider: provider.(*azuread.Provider).Provider, Code: data.Code}
|
||||
case domain.IDPTypeGitHub:
|
||||
provider, err = l.githubProvider(r.Context(), identityProvider)
|
||||
if err != nil {
|
||||
@ -249,7 +258,6 @@ func (l *Login) handleExternalLoginCallback(w http.ResponseWriter, r *http.Reque
|
||||
session = &openid.Session{Provider: provider.(*google.Provider).Provider, Code: data.Code}
|
||||
case domain.IDPTypeJWT,
|
||||
domain.IDPTypeLDAP,
|
||||
domain.IDPTypeAzureAD,
|
||||
domain.IDPTypeUnspecified:
|
||||
fallthrough
|
||||
default:
|
||||
@ -666,6 +674,28 @@ func (l *Login) oauthProvider(ctx context.Context, identityProvider *query.IDPTe
|
||||
)
|
||||
}
|
||||
|
||||
func (l *Login) azureProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*azuread.Provider, error) {
|
||||
secret, err := crypto.DecryptString(identityProvider.AzureADIDPTemplate.ClientSecret, l.idpConfigAlg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts := make([]azuread.ProviderOptions, 0, 2)
|
||||
if identityProvider.AzureADIDPTemplate.IsEmailVerified {
|
||||
opts = append(opts, azuread.WithEmailVerified())
|
||||
}
|
||||
if identityProvider.AzureADIDPTemplate.Tenant != "" {
|
||||
opts = append(opts, azuread.WithTenant(azuread.TenantType(identityProvider.AzureADIDPTemplate.Tenant)))
|
||||
}
|
||||
return azuread.New(
|
||||
identityProvider.Name,
|
||||
identityProvider.AzureADIDPTemplate.ClientID,
|
||||
secret,
|
||||
l.baseURL(ctx)+EndpointExternalLoginCallback,
|
||||
identityProvider.AzureADIDPTemplate.Scopes,
|
||||
opts...,
|
||||
)
|
||||
}
|
||||
|
||||
func (l *Login) githubProvider(ctx context.Context, identityProvider *query.IDPTemplate) (*github.Provider, error) {
|
||||
secret, err := crypto.DecryptString(identityProvider.GitHubIDPTemplate.ClientSecret, l.idpConfigAlg)
|
||||
if err != nil {
|
||||
|
@ -38,6 +38,16 @@ type JWTProvider struct {
|
||||
IDPOptions idp.Options
|
||||
}
|
||||
|
||||
type AzureADProvider struct {
|
||||
Name string
|
||||
ClientID string
|
||||
ClientSecret string
|
||||
Scopes []string
|
||||
Tenant string
|
||||
EmailVerified bool
|
||||
IDPOptions idp.Options
|
||||
}
|
||||
|
||||
type GitHubProvider struct {
|
||||
Name string
|
||||
ClientID string
|
||||
|
@ -413,6 +413,111 @@ func (wm *JWTIDPWriteModel) reduceJWTConfigChangedEvent(e *idpconfig.JWTConfigCh
|
||||
}
|
||||
}
|
||||
|
||||
type AzureADIDPWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
ID string
|
||||
Name string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
Scopes []string
|
||||
Tenant string
|
||||
IsEmailVerified bool
|
||||
idp.Options
|
||||
|
||||
State domain.IDPState
|
||||
}
|
||||
|
||||
func (wm *AzureADIDPWriteModel) Reduce() error {
|
||||
for _, event := range wm.Events {
|
||||
switch e := event.(type) {
|
||||
case *idp.AzureADIDPAddedEvent:
|
||||
wm.reduceAddedEvent(e)
|
||||
case *idp.AzureADIDPChangedEvent:
|
||||
wm.reduceChangedEvent(e)
|
||||
case *idp.RemovedEvent:
|
||||
wm.State = domain.IDPStateRemoved
|
||||
}
|
||||
}
|
||||
return wm.WriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *AzureADIDPWriteModel) reduceAddedEvent(e *idp.AzureADIDPAddedEvent) {
|
||||
wm.Name = e.Name
|
||||
wm.ClientID = e.ClientID
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
wm.Scopes = e.Scopes
|
||||
wm.Tenant = e.Tenant
|
||||
wm.IsEmailVerified = e.IsEmailVerified
|
||||
wm.Options = e.Options
|
||||
wm.State = domain.IDPStateActive
|
||||
}
|
||||
|
||||
func (wm *AzureADIDPWriteModel) reduceChangedEvent(e *idp.AzureADIDPChangedEvent) {
|
||||
if e.ClientID != nil {
|
||||
wm.ClientID = *e.ClientID
|
||||
}
|
||||
if e.ClientSecret != nil {
|
||||
wm.ClientSecret = e.ClientSecret
|
||||
}
|
||||
if e.Name != nil {
|
||||
wm.Name = *e.Name
|
||||
}
|
||||
if e.Scopes != nil {
|
||||
wm.Scopes = e.Scopes
|
||||
}
|
||||
if e.Tenant != nil {
|
||||
wm.Tenant = *e.Tenant
|
||||
}
|
||||
if e.IsEmailVerified != nil {
|
||||
wm.IsEmailVerified = *e.IsEmailVerified
|
||||
}
|
||||
wm.Options.ReduceChanges(e.OptionChanges)
|
||||
}
|
||||
|
||||
func (wm *AzureADIDPWriteModel) NewChanges(
|
||||
name string,
|
||||
clientID string,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
options idp.Options,
|
||||
) ([]idp.AzureADIDPChanges, error) {
|
||||
changes := make([]idp.AzureADIDPChanges, 0)
|
||||
var clientSecret *crypto.CryptoValue
|
||||
var err error
|
||||
if clientSecretString != "" {
|
||||
clientSecret, err = crypto.Crypt([]byte(clientSecretString), secretCrypto)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
changes = append(changes, idp.ChangeAzureADClientSecret(clientSecret))
|
||||
}
|
||||
if wm.Name != name {
|
||||
changes = append(changes, idp.ChangeAzureADName(name))
|
||||
}
|
||||
if wm.ClientID != clientID {
|
||||
changes = append(changes, idp.ChangeAzureADClientID(clientID))
|
||||
}
|
||||
if wm.Tenant != tenant {
|
||||
changes = append(changes, idp.ChangeAzureADTenant(tenant))
|
||||
}
|
||||
if wm.IsEmailVerified != isEmailVerified {
|
||||
changes = append(changes, idp.ChangeAzureADIsEmailVerified(isEmailVerified))
|
||||
}
|
||||
if !reflect.DeepEqual(wm.Scopes, scopes) {
|
||||
changes = append(changes, idp.ChangeAzureADScopes(scopes))
|
||||
}
|
||||
|
||||
opts := wm.Options.Changes(options)
|
||||
if !opts.IsZero() {
|
||||
changes = append(changes, idp.ChangeAzureADOptions(opts))
|
||||
}
|
||||
return changes, nil
|
||||
}
|
||||
|
||||
type GitHubIDPWriteModel struct {
|
||||
eventstore.WriteModel
|
||||
|
||||
@ -1049,6 +1154,8 @@ func (wm *IDPRemoveWriteModel) Reduce() error {
|
||||
wm.reduceAdded(e.ID)
|
||||
case *idp.JWTIDPAddedEvent:
|
||||
wm.reduceAdded(e.ID)
|
||||
case *idp.AzureADIDPAddedEvent:
|
||||
wm.reduceAdded(e.ID)
|
||||
case *idp.GitHubIDPAddedEvent:
|
||||
wm.reduceAdded(e.ID)
|
||||
case *idp.GitHubEnterpriseIDPAddedEvent:
|
||||
|
@ -139,6 +139,48 @@ func (c *Commands) UpdateInstanceJWTProvider(ctx context.Context, id string, pro
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddInstanceAzureADProvider(ctx context.Context, provider AzureADProvider) (string, *domain.ObjectDetails, error) {
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
instanceAgg := instance.NewAggregate(instanceID)
|
||||
id, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
writeModel := NewAzureADInstanceIDPWriteModel(instanceID, id)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddInstanceAzureADProvider(instanceAgg, writeModel, provider))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return id, pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) UpdateInstanceAzureADProvider(ctx context.Context, id string, provider AzureADProvider) (*domain.ObjectDetails, error) {
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
instanceAgg := instance.NewAggregate(instanceID)
|
||||
writeModel := NewAzureADInstanceIDPWriteModel(instanceID, id)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateInstanceAzureADProvider(instanceAgg, writeModel, provider))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cmds) == 0 {
|
||||
// no change, so return directly
|
||||
return &domain.ObjectDetails{
|
||||
Sequence: writeModel.ProcessedSequence,
|
||||
EventDate: writeModel.ChangeDate,
|
||||
ResourceOwner: writeModel.ResourceOwner,
|
||||
}, nil
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddInstanceGitHubProvider(ctx context.Context, provider GitHubProvider) (string, *domain.ObjectDetails, error) {
|
||||
instanceID := authz.GetInstance(ctx).InstanceID()
|
||||
instanceAgg := instance.NewAggregate(instanceID)
|
||||
@ -719,6 +761,92 @@ func (c *Commands) prepareUpdateInstanceJWTProvider(a *instance.Aggregate, write
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) prepareAddInstanceAzureADProvider(a *instance.Aggregate, writeModel *InstanceAzureADIDPWriteModel, provider AzureADProvider) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INST-sdf3g", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Fhbr2", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INST-Dzh3g", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{
|
||||
instance.NewAzureADIDPAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
writeModel.ID,
|
||||
provider.Name,
|
||||
provider.ClientID,
|
||||
secret,
|
||||
provider.Scopes,
|
||||
provider.Tenant,
|
||||
provider.EmailVerified,
|
||||
provider.IDPOptions,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) prepareUpdateInstanceAzureADProvider(a *instance.Aggregate, writeModel *InstanceAzureADIDPWriteModel, provider AzureADProvider) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INST-SAgh2", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INST-fh3h1", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "INST-dmitg", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.State.Exists() {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "INST-BHz3q", "Errors.Instance.IDPConfig.NotExisting")
|
||||
}
|
||||
event, err := writeModel.NewChangedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
writeModel.ID,
|
||||
provider.Name,
|
||||
provider.ClientID,
|
||||
provider.ClientSecret,
|
||||
c.idpConfigEncryption,
|
||||
provider.Scopes,
|
||||
provider.Tenant,
|
||||
provider.EmailVerified,
|
||||
provider.IDPOptions,
|
||||
)
|
||||
if err != nil || event == nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{event}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) prepareAddInstanceGitHubProvider(a *instance.Aggregate, writeModel *InstanceGitHubIDPWriteModel, provider GitHubProvider) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" {
|
||||
|
@ -279,6 +279,82 @@ func (wm *InstanceJWTIDPWriteModel) NewChangedEvent(
|
||||
return instance.NewJWTIDPChangedEvent(ctx, aggregate, id, changes)
|
||||
}
|
||||
|
||||
type InstanceAzureADIDPWriteModel struct {
|
||||
AzureADIDPWriteModel
|
||||
}
|
||||
|
||||
func NewAzureADInstanceIDPWriteModel(instanceID, id string) *InstanceAzureADIDPWriteModel {
|
||||
return &InstanceAzureADIDPWriteModel{
|
||||
AzureADIDPWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: instanceID,
|
||||
ResourceOwner: instanceID,
|
||||
},
|
||||
ID: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *InstanceAzureADIDPWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *instance.AzureADIDPAddedEvent:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPAddedEvent)
|
||||
case *instance.AzureADIDPChangedEvent:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPChangedEvent)
|
||||
case *instance.IDPRemovedEvent:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(&e.RemovedEvent)
|
||||
default:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *InstanceAzureADIDPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(instance.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
instance.AzureADIDPAddedEventType,
|
||||
instance.AzureADIDPChangedEventType,
|
||||
instance.IDPRemovedEventType,
|
||||
).
|
||||
EventData(map[string]interface{}{"id": wm.ID}).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *InstanceAzureADIDPWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
id,
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
options idp.Options,
|
||||
) (*instance.AzureADIDPChangedEvent, error) {
|
||||
|
||||
changes, err := wm.AzureADIDPWriteModel.NewChanges(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString,
|
||||
secretCrypto,
|
||||
scopes,
|
||||
tenant,
|
||||
isEmailVerified,
|
||||
options,
|
||||
)
|
||||
if err != nil || len(changes) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
return instance.NewAzureADIDPChangedEvent(ctx, aggregate, id, changes)
|
||||
}
|
||||
|
||||
type InstanceGitHubIDPWriteModel struct {
|
||||
GitHubIDPWriteModel
|
||||
}
|
||||
@ -726,6 +802,8 @@ func (wm *InstanceIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event)
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.OIDCIDPAddedEvent)
|
||||
case *instance.JWTIDPAddedEvent:
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.JWTIDPAddedEvent)
|
||||
case *instance.AzureADIDPAddedEvent:
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.AzureADIDPAddedEvent)
|
||||
case *instance.GitHubIDPAddedEvent:
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubIDPAddedEvent)
|
||||
case *instance.GitHubEnterpriseIDPAddedEvent:
|
||||
@ -760,6 +838,7 @@ func (wm *InstanceIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
instance.OAuthIDPAddedEventType,
|
||||
instance.OIDCIDPAddedEventType,
|
||||
instance.JWTIDPAddedEventType,
|
||||
instance.AzureADIDPAddedEventType,
|
||||
instance.GitHubIDPAddedEventType,
|
||||
instance.GitHubEnterpriseIDPAddedEventType,
|
||||
instance.GitLabIDPAddedEventType,
|
||||
|
@ -637,6 +637,428 @@ func TestCommandSide_UpdateInstanceGenericOAuthIDP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_AddInstanceAzureADIDP(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
secretCrypto crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
provider AzureADProvider
|
||||
}
|
||||
type res struct {
|
||||
id string
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"invalid name",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
provider: AzureADProvider{},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-sdf3g", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid client id",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Fhbr2", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid client secret",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-Dzh3g", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"instance1",
|
||||
instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
idp.Options{},
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
ClientSecret: "clientSecret",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
id: "id1",
|
||||
want: &domain.ObjectDetails{ResourceOwner: "instance1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok all set",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"instance1",
|
||||
instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
[]string{"openid"},
|
||||
"tenant",
|
||||
true,
|
||||
idp.Options{
|
||||
IsCreationAllowed: true,
|
||||
IsLinkingAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
)),
|
||||
},
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
ClientSecret: "clientSecret",
|
||||
Scopes: []string{"openid"},
|
||||
Tenant: "tenant",
|
||||
EmailVerified: true,
|
||||
IDPOptions: idp.Options{
|
||||
IsCreationAllowed: true,
|
||||
IsLinkingAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
id: "id1",
|
||||
want: &domain.ObjectDetails{ResourceOwner: "instance1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
idpConfigEncryption: tt.fields.secretCrypto,
|
||||
}
|
||||
id, got, err := c.AddInstanceAzureADProvider(tt.args.ctx, tt.args.provider)
|
||||
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.id, id)
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_UpdateInstanceAzureADIDP(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
secretCrypto crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
id string
|
||||
provider AzureADProvider
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"invalid id",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
provider: AzureADProvider{},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-SAgh2", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid name",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
id: "id1",
|
||||
provider: AzureADProvider{},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-fh3h1", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid client id",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "INST-dmitg", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errors.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
idp.Options{},
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{ResourceOwner: "instance1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
instance.NewAzureADIDPAddedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
idp.Options{},
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
[]*repository.Event{
|
||||
eventFromEventPusherWithInstanceID(
|
||||
"instance1",
|
||||
func() eventstore.Command {
|
||||
t := true
|
||||
event, _ := instance.NewAzureADIDPChangedEvent(context.Background(), &instance.NewAggregate("instance1").Aggregate,
|
||||
"id1",
|
||||
[]idp.AzureADIDPChanges{
|
||||
idp.ChangeAzureADName("new name"),
|
||||
idp.ChangeAzureADClientID("new clientID"),
|
||||
idp.ChangeAzureADClientSecret(&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("new clientSecret"),
|
||||
}),
|
||||
idp.ChangeAzureADScopes([]string{"openid", "profile"}),
|
||||
idp.ChangeAzureADTenant("new tenant"),
|
||||
idp.ChangeAzureADIsEmailVerified(true),
|
||||
idp.ChangeAzureADOptions(idp.OptionChanges{
|
||||
IsCreationAllowed: &t,
|
||||
IsLinkingAllowed: &t,
|
||||
IsAutoCreation: &t,
|
||||
IsAutoUpdate: &t,
|
||||
}),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}(),
|
||||
),
|
||||
},
|
||||
),
|
||||
),
|
||||
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: authz.WithInstanceID(context.Background(), "instance1"),
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "new name",
|
||||
ClientID: "new clientID",
|
||||
ClientSecret: "new clientSecret",
|
||||
Scopes: []string{"openid", "profile"},
|
||||
Tenant: "new tenant",
|
||||
EmailVerified: true,
|
||||
IDPOptions: idp.Options{
|
||||
IsCreationAllowed: true,
|
||||
IsLinkingAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{ResourceOwner: "instance1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idpConfigEncryption: tt.fields.secretCrypto,
|
||||
}
|
||||
got, err := c.UpdateInstanceAzureADProvider(tt.args.ctx, tt.args.id, tt.args.provider)
|
||||
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_AddInstanceGitHubIDP(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
|
@ -131,6 +131,45 @@ func (c *Commands) UpdateOrgJWTProvider(ctx context.Context, resourceOwner, id s
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
func (c *Commands) AddOrgAzureADProvider(ctx context.Context, resourceOwner string, provider AzureADProvider) (string, *domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(resourceOwner)
|
||||
id, err := c.idGenerator.Next()
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
writeModel := NewAzureADOrgIDPWriteModel(resourceOwner, id)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareAddOrgAzureADProvider(orgAgg, writeModel, provider))
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return "", nil, err
|
||||
}
|
||||
return id, pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) UpdateOrgAzureADProvider(ctx context.Context, resourceOwner, id string, provider AzureADProvider) (*domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(resourceOwner)
|
||||
writeModel := NewAzureADOrgIDPWriteModel(resourceOwner, id)
|
||||
cmds, err := preparation.PrepareCommands(ctx, c.eventstore.Filter, c.prepareUpdateOrgAzureADProvider(orgAgg, writeModel, provider))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(cmds) == 0 {
|
||||
// no change, so return directly
|
||||
return &domain.ObjectDetails{
|
||||
Sequence: writeModel.ProcessedSequence,
|
||||
EventDate: writeModel.ChangeDate,
|
||||
ResourceOwner: writeModel.ResourceOwner,
|
||||
}, nil
|
||||
}
|
||||
pushedEvents, err := c.eventstore.Push(ctx, cmds...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pushedEventsToObjectDetails(pushedEvents), nil
|
||||
}
|
||||
|
||||
func (c *Commands) AddOrgGitHubProvider(ctx context.Context, resourceOwner string, provider GitHubProvider) (string, *domain.ObjectDetails, error) {
|
||||
orgAgg := org.NewAggregate(resourceOwner)
|
||||
@ -700,6 +739,92 @@ func (c *Commands) prepareUpdateOrgJWTProvider(a *org.Aggregate, writeModel *Org
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) prepareAddOrgAzureADProvider(a *org.Aggregate, writeModel *OrgAzureADIDPWriteModel, provider AzureADProvider) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-sdf3g", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Fhbr2", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.ClientSecret = strings.TrimSpace(provider.ClientSecret); provider.ClientSecret == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-Dzh3g", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
secret, err := crypto.Encrypt([]byte(provider.ClientSecret), c.idpConfigEncryption)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{
|
||||
org.NewAzureADIDPAddedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
writeModel.ID,
|
||||
provider.Name,
|
||||
provider.ClientID,
|
||||
secret,
|
||||
provider.Scopes,
|
||||
provider.Tenant,
|
||||
provider.EmailVerified,
|
||||
provider.IDPOptions,
|
||||
),
|
||||
}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) prepareUpdateOrgAzureADProvider(a *org.Aggregate, writeModel *OrgAzureADIDPWriteModel, provider AzureADProvider) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if writeModel.ID = strings.TrimSpace(writeModel.ID); writeModel.ID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-SAgh2", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.Name = strings.TrimSpace(provider.Name); provider.Name == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-fh3h1", "Errors.Invalid.Argument")
|
||||
}
|
||||
if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" {
|
||||
return nil, caos_errs.ThrowInvalidArgument(nil, "ORG-dmitg", "Errors.Invalid.Argument")
|
||||
}
|
||||
return func(ctx context.Context, filter preparation.FilterToQueryReducer) ([]eventstore.Command, error) {
|
||||
events, err := filter(ctx, writeModel.Query())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
writeModel.AppendEvents(events...)
|
||||
if err = writeModel.Reduce(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !writeModel.State.Exists() {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "ORG-BHz3q", "Errors.Org.IDPConfig.NotExisting")
|
||||
}
|
||||
event, err := writeModel.NewChangedEvent(
|
||||
ctx,
|
||||
&a.Aggregate,
|
||||
writeModel.ID,
|
||||
provider.Name,
|
||||
provider.ClientID,
|
||||
provider.ClientSecret,
|
||||
c.idpConfigEncryption,
|
||||
provider.Scopes,
|
||||
provider.Tenant,
|
||||
provider.EmailVerified,
|
||||
provider.IDPOptions,
|
||||
)
|
||||
if err != nil || event == nil {
|
||||
return nil, err
|
||||
}
|
||||
return []eventstore.Command{event}, nil
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Commands) prepareAddOrgGitHubProvider(a *org.Aggregate, writeModel *OrgGitHubIDPWriteModel, provider GitHubProvider) preparation.Validation {
|
||||
return func() (preparation.CreateCommands, error) {
|
||||
if provider.ClientID = strings.TrimSpace(provider.ClientID); provider.ClientID == "" {
|
||||
|
@ -281,6 +281,86 @@ func (wm *OrgJWTIDPWriteModel) NewChangedEvent(
|
||||
return org.NewJWTIDPChangedEvent(ctx, aggregate, id, changes)
|
||||
}
|
||||
|
||||
type OrgAzureADIDPWriteModel struct {
|
||||
AzureADIDPWriteModel
|
||||
}
|
||||
|
||||
func NewAzureADOrgIDPWriteModel(orgID, id string) *OrgAzureADIDPWriteModel {
|
||||
return &OrgAzureADIDPWriteModel{
|
||||
AzureADIDPWriteModel{
|
||||
WriteModel: eventstore.WriteModel{
|
||||
AggregateID: orgID,
|
||||
ResourceOwner: orgID,
|
||||
},
|
||||
ID: id,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgAzureADIDPWriteModel) Reduce() error {
|
||||
return wm.AzureADIDPWriteModel.Reduce()
|
||||
}
|
||||
|
||||
func (wm *OrgAzureADIDPWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
for _, event := range events {
|
||||
switch e := event.(type) {
|
||||
case *org.AzureADIDPAddedEvent:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPAddedEvent)
|
||||
case *org.AzureADIDPChangedEvent:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(&e.AzureADIDPChangedEvent)
|
||||
case *org.IDPRemovedEvent:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(&e.RemovedEvent)
|
||||
default:
|
||||
wm.AzureADIDPWriteModel.AppendEvents(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (wm *OrgAzureADIDPWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
||||
ResourceOwner(wm.ResourceOwner).
|
||||
AddQuery().
|
||||
AggregateTypes(org.AggregateType).
|
||||
AggregateIDs(wm.AggregateID).
|
||||
EventTypes(
|
||||
org.AzureADIDPAddedEventType,
|
||||
org.AzureADIDPChangedEventType,
|
||||
org.IDPRemovedEventType,
|
||||
).
|
||||
EventData(map[string]interface{}{"id": wm.ID}).
|
||||
Builder()
|
||||
}
|
||||
|
||||
func (wm *OrgAzureADIDPWriteModel) NewChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
id,
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString string,
|
||||
secretCrypto crypto.Crypto,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
options idp.Options,
|
||||
) (*org.AzureADIDPChangedEvent, error) {
|
||||
|
||||
changes, err := wm.AzureADIDPWriteModel.NewChanges(
|
||||
name,
|
||||
clientID,
|
||||
clientSecretString,
|
||||
secretCrypto,
|
||||
scopes,
|
||||
tenant,
|
||||
isEmailVerified,
|
||||
options,
|
||||
)
|
||||
if err != nil || len(changes) == 0 {
|
||||
return nil, err
|
||||
}
|
||||
return org.NewAzureADIDPChangedEvent(ctx, aggregate, id, changes)
|
||||
}
|
||||
|
||||
type OrgGitHubIDPWriteModel struct {
|
||||
GitHubIDPWriteModel
|
||||
}
|
||||
@ -732,6 +812,8 @@ func (wm *OrgIDPRemoveWriteModel) AppendEvents(events ...eventstore.Event) {
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.OIDCIDPAddedEvent)
|
||||
case *org.JWTIDPAddedEvent:
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.JWTIDPAddedEvent)
|
||||
case *org.AzureADIDPAddedEvent:
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.AzureADIDPAddedEvent)
|
||||
case *org.GitHubIDPAddedEvent:
|
||||
wm.IDPRemoveWriteModel.AppendEvents(&e.GitHubIDPAddedEvent)
|
||||
case *org.GitHubEnterpriseIDPAddedEvent:
|
||||
@ -766,6 +848,7 @@ func (wm *OrgIDPRemoveWriteModel) Query() *eventstore.SearchQueryBuilder {
|
||||
org.OAuthIDPAddedEventType,
|
||||
org.OIDCIDPAddedEventType,
|
||||
org.JWTIDPAddedEventType,
|
||||
org.AzureADIDPAddedEventType,
|
||||
org.GitHubIDPAddedEventType,
|
||||
org.GitHubEnterpriseIDPAddedEventType,
|
||||
org.GitLabIDPAddedEventType,
|
||||
|
@ -648,6 +648,432 @@ func TestCommandSide_UpdateOrgGenericOAuthIDP(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_AddOrgAzureADIDP(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
idGenerator id.Generator
|
||||
secretCrypto crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
provider AzureADProvider
|
||||
}
|
||||
type res struct {
|
||||
id string
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"invalid name",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
provider: AzureADProvider{},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-sdf3g", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid client id",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Fhbr2", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid client secret",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-Dzh3g", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
eventPusherToEvents(
|
||||
org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
idp.Options{},
|
||||
)),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
ClientSecret: "clientSecret",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
id: "id1",
|
||||
want: &domain.ObjectDetails{ResourceOwner: "org1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "ok all set",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(),
|
||||
expectPush(
|
||||
eventPusherToEvents(
|
||||
org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
[]string{"openid"},
|
||||
"tenant",
|
||||
true,
|
||||
idp.Options{
|
||||
IsCreationAllowed: true,
|
||||
IsLinkingAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
)),
|
||||
),
|
||||
),
|
||||
idGenerator: id_mock.NewIDGeneratorExpectIDs(t, "id1"),
|
||||
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
ClientSecret: "clientSecret",
|
||||
Scopes: []string{"openid"},
|
||||
Tenant: "tenant",
|
||||
EmailVerified: true,
|
||||
IDPOptions: idp.Options{
|
||||
IsCreationAllowed: true,
|
||||
IsLinkingAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
id: "id1",
|
||||
want: &domain.ObjectDetails{ResourceOwner: "org1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idGenerator: tt.fields.idGenerator,
|
||||
idpConfigEncryption: tt.fields.secretCrypto,
|
||||
}
|
||||
id, got, err := c.AddOrgAzureADProvider(tt.args.ctx, tt.args.resourceOwner, tt.args.provider)
|
||||
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.id, id)
|
||||
assert.Equal(t, tt.res.want, got)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCommandSide_UpdateOrgAzureADIDP(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
secretCrypto crypto.EncryptionAlgorithm
|
||||
}
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
resourceOwner string
|
||||
id string
|
||||
provider AzureADProvider
|
||||
}
|
||||
type res struct {
|
||||
want *domain.ObjectDetails
|
||||
err func(error) bool
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
fields fields
|
||||
args args
|
||||
res res
|
||||
}{
|
||||
{
|
||||
"invalid id",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
provider: AzureADProvider{},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-SAgh2", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid name",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
id: "id1",
|
||||
provider: AzureADProvider{},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-fh3h1", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
"invalid client id",
|
||||
fields{
|
||||
eventstore: eventstoreExpect(t),
|
||||
},
|
||||
args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
},
|
||||
},
|
||||
res{
|
||||
err: func(err error) bool {
|
||||
return errors.Is(err, caos_errors.ThrowInvalidArgument(nil, "ORG-dmitg", ""))
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "not found",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
err: caos_errors.IsNotFound,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "no changes",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
idp.Options{},
|
||||
)),
|
||||
),
|
||||
),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "name",
|
||||
ClientID: "clientID",
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{ResourceOwner: "org1"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "change ok",
|
||||
fields: fields{
|
||||
eventstore: eventstoreExpect(t,
|
||||
expectFilter(
|
||||
eventFromEventPusher(
|
||||
org.NewAzureADIDPAddedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
|
||||
"id1",
|
||||
"name",
|
||||
"clientID",
|
||||
&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("clientSecret"),
|
||||
},
|
||||
nil,
|
||||
"",
|
||||
false,
|
||||
idp.Options{},
|
||||
)),
|
||||
),
|
||||
expectPush(
|
||||
eventPusherToEvents(
|
||||
func() eventstore.Command {
|
||||
t := true
|
||||
event, _ := org.NewAzureADIDPChangedEvent(context.Background(), &org.NewAggregate("org1").Aggregate,
|
||||
"id1",
|
||||
[]idp.AzureADIDPChanges{
|
||||
idp.ChangeAzureADName("new name"),
|
||||
idp.ChangeAzureADClientID("new clientID"),
|
||||
idp.ChangeAzureADClientSecret(&crypto.CryptoValue{
|
||||
CryptoType: crypto.TypeEncryption,
|
||||
Algorithm: "enc",
|
||||
KeyID: "id",
|
||||
Crypted: []byte("new clientSecret"),
|
||||
}),
|
||||
idp.ChangeAzureADScopes([]string{"openid", "profile"}),
|
||||
idp.ChangeAzureADTenant("new tenant"),
|
||||
idp.ChangeAzureADIsEmailVerified(true),
|
||||
idp.ChangeAzureADOptions(idp.OptionChanges{
|
||||
IsCreationAllowed: &t,
|
||||
IsLinkingAllowed: &t,
|
||||
IsAutoCreation: &t,
|
||||
IsAutoUpdate: &t,
|
||||
}),
|
||||
},
|
||||
)
|
||||
return event
|
||||
}(),
|
||||
),
|
||||
),
|
||||
),
|
||||
secretCrypto: crypto.CreateMockEncryptionAlg(gomock.NewController(t)),
|
||||
},
|
||||
args: args{
|
||||
ctx: context.Background(),
|
||||
resourceOwner: "org1",
|
||||
id: "id1",
|
||||
provider: AzureADProvider{
|
||||
Name: "new name",
|
||||
ClientID: "new clientID",
|
||||
ClientSecret: "new clientSecret",
|
||||
Scopes: []string{"openid", "profile"},
|
||||
Tenant: "new tenant",
|
||||
EmailVerified: true,
|
||||
IDPOptions: idp.Options{
|
||||
IsCreationAllowed: true,
|
||||
IsLinkingAllowed: true,
|
||||
IsAutoCreation: true,
|
||||
IsAutoUpdate: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
res: res{
|
||||
want: &domain.ObjectDetails{ResourceOwner: "org1"},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
c := &Commands{
|
||||
eventstore: tt.fields.eventstore,
|
||||
idpConfigEncryption: tt.fields.secretCrypto,
|
||||
}
|
||||
got, err := c.UpdateOrgAzureADProvider(tt.args.ctx, tt.args.resourceOwner, tt.args.id, tt.args.provider)
|
||||
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_AddOrgGitHubIDP(t *testing.T) {
|
||||
type fields struct {
|
||||
eventstore *eventstore.Eventstore
|
||||
|
@ -15,7 +15,7 @@ import (
|
||||
const (
|
||||
authURLTemplate string = "https://login.microsoftonline.com/%s/oauth2/v2.0/authorize"
|
||||
tokenURLTemplate string = "https://login.microsoftonline.com/%s/oauth2/v2.0/token"
|
||||
userinfoURL string = "https://graph.microsoft.com/oidc/userinfo"
|
||||
userinfoURL string = "https://graph.microsoft.com/v1.0/me"
|
||||
)
|
||||
|
||||
// TenantType are the well known tenant types to scope the users that can authenticate. TenantType is not an
|
||||
@ -73,7 +73,7 @@ func WithOAuthOptions(opts ...oauth.ProviderOpts) ProviderOptions {
|
||||
|
||||
// New creates an AzureAD provider using the [oauth.Provider] (OAuth 2.0 generic provider).
|
||||
// By default, it uses the [CommonTenant] and unverified emails.
|
||||
func New(name, clientID, clientSecret, redirectURI string, opts ...ProviderOptions) (*Provider, error) {
|
||||
func New(name, clientID, clientSecret, redirectURI string, scopes []string, opts ...ProviderOptions) (*Provider, error) {
|
||||
provider := &Provider{
|
||||
tenant: CommonTenant,
|
||||
options: make([]oauth.ProviderOpts, 0),
|
||||
@ -81,7 +81,7 @@ func New(name, clientID, clientSecret, redirectURI string, opts ...ProviderOptio
|
||||
for _, opt := range opts {
|
||||
opt(provider)
|
||||
}
|
||||
config := newConfig(provider.tenant, clientID, clientSecret, redirectURI, []string{oidc.ScopeOpenID, oidc.ScopeProfile, oidc.ScopeEmail})
|
||||
config := newConfig(provider.tenant, clientID, clientSecret, redirectURI, scopes)
|
||||
rp, err := oauth.New(
|
||||
config,
|
||||
name,
|
||||
@ -121,34 +121,38 @@ func newConfig(tenant TenantType, clientID, secret, callbackURL string, scopes [
|
||||
// AzureAD does not return an `email_verified` claim.
|
||||
// The verification can be automatically activated on the provider ([WithEmailVerified])
|
||||
type User struct {
|
||||
Sub string `json:"sub"`
|
||||
FamilyName string `json:"family_name"`
|
||||
GivenName string `json:"given_name"`
|
||||
Name string `json:"name"`
|
||||
PreferredUsername string `json:"preferred_username"`
|
||||
Email domain.EmailAddress `json:"email"`
|
||||
Picture string `json:"picture"`
|
||||
ID string `json:"id"`
|
||||
BusinessPhones []domain.PhoneNumber `json:"businessPhones"`
|
||||
DisplayName string `json:"displayName"`
|
||||
FirstName string `json:"givenName"`
|
||||
JobTitle string `json:"jobTitle"`
|
||||
Email domain.EmailAddress `json:"mail"`
|
||||
MobilePhone domain.PhoneNumber `json:"mobilePhone"`
|
||||
OfficeLocation string `json:"officeLocation"`
|
||||
PreferredLanguage string `json:"preferredLanguage"`
|
||||
LastName string `json:"surname"`
|
||||
UserPrincipalName string `json:"userPrincipalName"`
|
||||
isEmailVerified bool
|
||||
}
|
||||
|
||||
// GetID is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetID() string {
|
||||
return u.Sub
|
||||
return u.ID
|
||||
}
|
||||
|
||||
// GetFirstName is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetFirstName() string {
|
||||
return u.GivenName
|
||||
return u.FirstName
|
||||
}
|
||||
|
||||
// GetLastName is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetLastName() string {
|
||||
return u.FamilyName
|
||||
return u.LastName
|
||||
}
|
||||
|
||||
// GetDisplayName is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetDisplayName() string {
|
||||
return u.Name
|
||||
return u.DisplayName
|
||||
}
|
||||
|
||||
// GetNickname is an implementation of the [idp.User] interface.
|
||||
@ -159,11 +163,16 @@ func (u *User) GetNickname() string {
|
||||
|
||||
// GetPreferredUsername is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetPreferredUsername() string {
|
||||
return u.PreferredUsername
|
||||
return u.UserPrincipalName
|
||||
}
|
||||
|
||||
// GetEmail is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetEmail() domain.EmailAddress {
|
||||
if u.Email == "" {
|
||||
// if the user used a social login on Azure as well, the email will be empty
|
||||
// but is used as username
|
||||
return domain.EmailAddress(u.UserPrincipalName)
|
||||
}
|
||||
return u.Email
|
||||
}
|
||||
|
||||
@ -188,10 +197,8 @@ func (u *User) IsPhoneVerified() bool {
|
||||
}
|
||||
|
||||
// GetPreferredLanguage is an implementation of the [idp.User] interface.
|
||||
// It returns [language.Und] because AzureAD does not provide the user's language
|
||||
func (u *User) GetPreferredLanguage() language.Tag {
|
||||
// AzureAD does not provide the user's language
|
||||
return language.Und
|
||||
return language.Make(u.PreferredLanguage)
|
||||
}
|
||||
|
||||
// GetProfile is an implementation of the [idp.User] interface.
|
||||
@ -202,5 +209,5 @@ func (u *User) GetProfile() string {
|
||||
|
||||
// GetAvatarURL is an implementation of the [idp.User] interface.
|
||||
func (u *User) GetAvatarURL() string {
|
||||
return u.Picture
|
||||
return ""
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/zitadel/oidc/v2/pkg/client/rp"
|
||||
openid "github.com/zitadel/oidc/v2/pkg/oidc"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/idp"
|
||||
"github.com/zitadel/zitadel/internal/idp/providers/oauth"
|
||||
@ -19,6 +20,7 @@ func TestProvider_BeginAuth(t *testing.T) {
|
||||
clientID string
|
||||
clientSecret string
|
||||
redirectURI string
|
||||
scopes []string
|
||||
options []ProviderOptions
|
||||
}
|
||||
tests := []struct {
|
||||
@ -34,7 +36,7 @@ func TestProvider_BeginAuth(t *testing.T) {
|
||||
redirectURI: "redirectURI",
|
||||
},
|
||||
want: &oidc.Session{
|
||||
AuthURL: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid+profile+email&state=testState",
|
||||
AuthURL: "https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid&state=testState",
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -48,7 +50,22 @@ func TestProvider_BeginAuth(t *testing.T) {
|
||||
},
|
||||
},
|
||||
want: &oidc.Session{
|
||||
AuthURL: "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid+profile+email&state=testState",
|
||||
AuthURL: "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid&state=testState",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "custom scopes",
|
||||
fields: fields{
|
||||
clientID: "clientID",
|
||||
clientSecret: "clientSecret",
|
||||
redirectURI: "redirectURI",
|
||||
scopes: []string{openid.ScopeOpenID, openid.ScopeProfile, "user"},
|
||||
options: []ProviderOptions{
|
||||
WithTenant(ConsumersTenant),
|
||||
},
|
||||
},
|
||||
want: &oidc.Session{
|
||||
AuthURL: "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=clientID&prompt=select_account&redirect_uri=redirectURI&response_type=code&scope=openid+profile+user&state=testState",
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -57,7 +74,7 @@ func TestProvider_BeginAuth(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
r := require.New(t)
|
||||
|
||||
provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.options...)
|
||||
provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes, tt.fields.options...)
|
||||
r.NoError(err)
|
||||
|
||||
session, err := provider.BeginAuth(context.Background(), "testState")
|
||||
@ -74,6 +91,7 @@ func TestProvider_Options(t *testing.T) {
|
||||
clientID string
|
||||
clientSecret string
|
||||
redirectURI string
|
||||
scopes []string
|
||||
options []ProviderOptions
|
||||
}
|
||||
type want struct {
|
||||
@ -98,6 +116,7 @@ func TestProvider_Options(t *testing.T) {
|
||||
clientID: "clientID",
|
||||
clientSecret: "clientSecret",
|
||||
redirectURI: "redirectURI",
|
||||
scopes: nil,
|
||||
options: nil,
|
||||
},
|
||||
want: want{
|
||||
@ -146,7 +165,7 @@ func TestProvider_Options(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
a := assert.New(t)
|
||||
|
||||
provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.options...)
|
||||
provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes, tt.fields.options...)
|
||||
require.NoError(t, err)
|
||||
|
||||
a.Equal(tt.want.name, provider.Name())
|
||||
|
@ -25,6 +25,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
clientID string
|
||||
clientSecret string
|
||||
redirectURI string
|
||||
scopes []string
|
||||
httpMock func()
|
||||
options []ProviderOptions
|
||||
authURL string
|
||||
@ -61,7 +62,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
redirectURI: "redirectURI",
|
||||
httpMock: func() {
|
||||
gock.New("https://graph.microsoft.com").
|
||||
Get("/oidc/userinfo").
|
||||
Get("/v1.0/me").
|
||||
Reply(200).
|
||||
JSON(userinfo())
|
||||
},
|
||||
@ -82,7 +83,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
redirectURI: "redirectURI",
|
||||
httpMock: func() {
|
||||
gock.New("https://graph.microsoft.com").
|
||||
Get("/oidc/userinfo").
|
||||
Get("/v1.0/me").
|
||||
Reply(http.StatusInternalServerError)
|
||||
},
|
||||
authURL: "https://login.microsoftonline.com/consumers/oauth2/v2.0/authorize?client_id=clientID&redirect_uri=redirectURI&response_type=code&scope=openid+profile+email&state=testState",
|
||||
@ -119,7 +120,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
redirectURI: "redirectURI",
|
||||
httpMock: func() {
|
||||
gock.New("https://graph.microsoft.com").
|
||||
Get("/oidc/userinfo").
|
||||
Get("/v1.0/me").
|
||||
Reply(200).
|
||||
JSON(userinfo())
|
||||
},
|
||||
@ -145,16 +146,20 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
user: &User{
|
||||
Sub: "sub",
|
||||
FamilyName: "lastname",
|
||||
GivenName: "firstname",
|
||||
Name: "firstname lastname",
|
||||
PreferredUsername: "username",
|
||||
ID: "id",
|
||||
BusinessPhones: []domain.PhoneNumber{"phone1", "phone2"},
|
||||
DisplayName: "firstname lastname",
|
||||
FirstName: "firstname",
|
||||
JobTitle: "title",
|
||||
Email: "email",
|
||||
Picture: "picture",
|
||||
MobilePhone: "mobile",
|
||||
OfficeLocation: "office",
|
||||
PreferredLanguage: "en",
|
||||
LastName: "lastname",
|
||||
UserPrincipalName: "username",
|
||||
isEmailVerified: false,
|
||||
},
|
||||
id: "sub",
|
||||
id: "id",
|
||||
firstName: "firstname",
|
||||
lastName: "lastname",
|
||||
displayName: "firstname lastname",
|
||||
@ -164,8 +169,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
isEmailVerified: false,
|
||||
phone: "",
|
||||
isPhoneVerified: false,
|
||||
preferredLanguage: language.Und,
|
||||
avatarURL: "picture",
|
||||
preferredLanguage: language.English,
|
||||
profile: "",
|
||||
},
|
||||
},
|
||||
@ -180,7 +184,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
},
|
||||
httpMock: func() {
|
||||
gock.New("https://graph.microsoft.com").
|
||||
Get("/oidc/userinfo").
|
||||
Get("/v1.0/me").
|
||||
Reply(200).
|
||||
JSON(userinfo())
|
||||
},
|
||||
@ -206,16 +210,20 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
user: &User{
|
||||
Sub: "sub",
|
||||
FamilyName: "lastname",
|
||||
GivenName: "firstname",
|
||||
Name: "firstname lastname",
|
||||
PreferredUsername: "username",
|
||||
ID: "id",
|
||||
BusinessPhones: []domain.PhoneNumber{"phone1", "phone2"},
|
||||
DisplayName: "firstname lastname",
|
||||
FirstName: "firstname",
|
||||
JobTitle: "title",
|
||||
Email: "email",
|
||||
Picture: "picture",
|
||||
MobilePhone: "mobile",
|
||||
OfficeLocation: "office",
|
||||
PreferredLanguage: "en",
|
||||
LastName: "lastname",
|
||||
UserPrincipalName: "username",
|
||||
isEmailVerified: true,
|
||||
},
|
||||
id: "sub",
|
||||
id: "id",
|
||||
firstName: "firstname",
|
||||
lastName: "lastname",
|
||||
displayName: "firstname lastname",
|
||||
@ -225,8 +233,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
isEmailVerified: true,
|
||||
phone: "",
|
||||
isPhoneVerified: false,
|
||||
preferredLanguage: language.Und,
|
||||
avatarURL: "picture",
|
||||
preferredLanguage: language.English,
|
||||
profile: "",
|
||||
},
|
||||
},
|
||||
@ -237,7 +244,7 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
tt.fields.httpMock()
|
||||
a := assert.New(t)
|
||||
|
||||
provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.options...)
|
||||
provider, err := New(tt.fields.name, tt.fields.clientID, tt.fields.clientSecret, tt.fields.redirectURI, tt.fields.scopes, tt.fields.options...)
|
||||
require.NoError(t, err)
|
||||
|
||||
session := &oauth.Session{
|
||||
@ -272,15 +279,18 @@ func TestSession_FetchUser(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func userinfo() oidc.UserInfoSetter {
|
||||
userinfo := oidc.NewUserInfo()
|
||||
userinfo.SetSubject("sub")
|
||||
userinfo.SetName("firstname lastname")
|
||||
userinfo.SetPreferredUsername("username")
|
||||
userinfo.SetNickname("nickname")
|
||||
userinfo.SetEmail("email", false) // azure add does not send the email_verified claim
|
||||
userinfo.SetPicture("picture")
|
||||
userinfo.SetGivenName("firstname")
|
||||
userinfo.SetFamilyName("lastname")
|
||||
return userinfo
|
||||
func userinfo() *User {
|
||||
return &User{
|
||||
ID: "id",
|
||||
BusinessPhones: []domain.PhoneNumber{"phone1", "phone2"},
|
||||
DisplayName: "firstname lastname",
|
||||
FirstName: "firstname",
|
||||
JobTitle: "title",
|
||||
Email: "email",
|
||||
MobilePhone: "mobile",
|
||||
OfficeLocation: "office",
|
||||
PreferredLanguage: "en",
|
||||
LastName: "lastname",
|
||||
UserPrincipalName: "username",
|
||||
}
|
||||
}
|
||||
|
@ -37,6 +37,7 @@ type IDPTemplate struct {
|
||||
*OAuthIDPTemplate
|
||||
*OIDCIDPTemplate
|
||||
*JWTIDPTemplate
|
||||
*AzureADIDPTemplate
|
||||
*GitHubIDPTemplate
|
||||
*GitHubEnterpriseIDPTemplate
|
||||
*GitLabIDPTemplate
|
||||
@ -77,6 +78,15 @@ type JWTIDPTemplate struct {
|
||||
Endpoint string
|
||||
}
|
||||
|
||||
type AzureADIDPTemplate struct {
|
||||
IDPID string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
Scopes database.StringArray
|
||||
Tenant string
|
||||
IsEmailVerified bool
|
||||
}
|
||||
|
||||
type GitHubIDPTemplate struct {
|
||||
IDPID string
|
||||
ClientID string
|
||||
@ -301,6 +311,41 @@ var (
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
azureadIdpTemplateTable = table{
|
||||
name: projection.IDPTemplateAzureADTable,
|
||||
instanceIDCol: projection.AzureADInstanceIDCol,
|
||||
}
|
||||
AzureADIDCol = Column{
|
||||
name: projection.AzureADIDCol,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
AzureADInstanceIDCol = Column{
|
||||
name: projection.AzureADInstanceIDCol,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
AzureADClientIDCol = Column{
|
||||
name: projection.AzureADClientIDCol,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
AzureADClientSecretCol = Column{
|
||||
name: projection.AzureADClientSecretCol,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
AzureADScopesCol = Column{
|
||||
name: projection.AzureADScopesCol,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
AzureADTenantCol = Column{
|
||||
name: projection.AzureADTenantCol,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
AzureADIsEmailVerified = Column{
|
||||
name: projection.AzureADIsEmailVerified,
|
||||
table: azureadIdpTemplateTable,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
githubIdpTemplateTable = table{
|
||||
name: projection.IDPTemplateGitHubTable,
|
||||
@ -683,6 +728,13 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se
|
||||
JWTEndpointCol.identifier(),
|
||||
JWTKeysEndpointCol.identifier(),
|
||||
JWTHeaderNameCol.identifier(),
|
||||
// azure
|
||||
AzureADIDCol.identifier(),
|
||||
AzureADClientIDCol.identifier(),
|
||||
AzureADClientSecretCol.identifier(),
|
||||
AzureADScopesCol.identifier(),
|
||||
AzureADTenantCol.identifier(),
|
||||
AzureADIsEmailVerified.identifier(),
|
||||
// github
|
||||
GitHubIDCol.identifier(),
|
||||
GitHubClientIDCol.identifier(),
|
||||
@ -739,6 +791,7 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se
|
||||
LeftJoin(join(OAuthIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(OIDCIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(JWTIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(AzureADIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(GitHubIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(GitHubEnterpriseIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(GitLabIDCol, IDPTemplateIDCol)).
|
||||
@ -772,6 +825,13 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se
|
||||
jwtKeysEndpoint := sql.NullString{}
|
||||
jwtHeaderName := sql.NullString{}
|
||||
|
||||
azureadID := sql.NullString{}
|
||||
azureadClientID := sql.NullString{}
|
||||
azureadClientSecret := new(crypto.CryptoValue)
|
||||
azureadScopes := database.StringArray{}
|
||||
azureadTenant := sql.NullString{}
|
||||
azureadIsEmailVerified := sql.NullBool{}
|
||||
|
||||
githubID := sql.NullString{}
|
||||
githubClientID := sql.NullString{}
|
||||
githubClientSecret := new(crypto.CryptoValue)
|
||||
@ -859,6 +919,13 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se
|
||||
&jwtEndpoint,
|
||||
&jwtKeysEndpoint,
|
||||
&jwtHeaderName,
|
||||
// azure
|
||||
&azureadID,
|
||||
&azureadClientID,
|
||||
&azureadClientSecret,
|
||||
&azureadScopes,
|
||||
&azureadTenant,
|
||||
&azureadIsEmailVerified,
|
||||
// github
|
||||
&githubID,
|
||||
&githubClientID,
|
||||
@ -951,6 +1018,16 @@ func prepareIDPTemplateByIDQuery(ctx context.Context, db prepareDatabase) (sq.Se
|
||||
Endpoint: jwtEndpoint.String,
|
||||
}
|
||||
}
|
||||
if azureadID.Valid {
|
||||
idpTemplate.AzureADIDPTemplate = &AzureADIDPTemplate{
|
||||
IDPID: azureadID.String,
|
||||
ClientID: azureadClientID.String,
|
||||
ClientSecret: azureadClientSecret,
|
||||
Scopes: azureadScopes,
|
||||
Tenant: azureadTenant.String,
|
||||
IsEmailVerified: azureadIsEmailVerified.Bool,
|
||||
}
|
||||
}
|
||||
if githubID.Valid {
|
||||
idpTemplate.GitHubIDPTemplate = &GitHubIDPTemplate{
|
||||
IDPID: githubID.String,
|
||||
@ -1064,6 +1141,13 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
||||
JWTEndpointCol.identifier(),
|
||||
JWTKeysEndpointCol.identifier(),
|
||||
JWTHeaderNameCol.identifier(),
|
||||
// azure
|
||||
AzureADIDCol.identifier(),
|
||||
AzureADClientIDCol.identifier(),
|
||||
AzureADClientSecretCol.identifier(),
|
||||
AzureADScopesCol.identifier(),
|
||||
AzureADTenantCol.identifier(),
|
||||
AzureADIsEmailVerified.identifier(),
|
||||
// github
|
||||
GitHubIDCol.identifier(),
|
||||
GitHubClientIDCol.identifier(),
|
||||
@ -1121,6 +1205,7 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
||||
LeftJoin(join(OAuthIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(OIDCIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(JWTIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(AzureADIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(GitHubIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(GitHubEnterpriseIDCol, IDPTemplateIDCol)).
|
||||
LeftJoin(join(GitLabIDCol, IDPTemplateIDCol)).
|
||||
@ -1157,6 +1242,13 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
||||
jwtKeysEndpoint := sql.NullString{}
|
||||
jwtHeaderName := sql.NullString{}
|
||||
|
||||
azureadID := sql.NullString{}
|
||||
azureadClientID := sql.NullString{}
|
||||
azureadClientSecret := new(crypto.CryptoValue)
|
||||
azureadScopes := database.StringArray{}
|
||||
azureadTenant := sql.NullString{}
|
||||
azureadIsEmailVerified := sql.NullBool{}
|
||||
|
||||
githubID := sql.NullString{}
|
||||
githubClientID := sql.NullString{}
|
||||
githubClientSecret := new(crypto.CryptoValue)
|
||||
@ -1244,6 +1336,13 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
||||
&jwtEndpoint,
|
||||
&jwtKeysEndpoint,
|
||||
&jwtHeaderName,
|
||||
// azure
|
||||
&azureadID,
|
||||
&azureadClientID,
|
||||
&azureadClientSecret,
|
||||
&azureadScopes,
|
||||
&azureadTenant,
|
||||
&azureadIsEmailVerified,
|
||||
// github
|
||||
&githubID,
|
||||
&githubClientID,
|
||||
@ -1335,6 +1434,16 @@ func prepareIDPTemplatesQuery(ctx context.Context, db prepareDatabase) (sq.Selec
|
||||
Endpoint: jwtEndpoint.String,
|
||||
}
|
||||
}
|
||||
if azureadID.Valid {
|
||||
idpTemplate.AzureADIDPTemplate = &AzureADIDPTemplate{
|
||||
IDPID: azureadID.String,
|
||||
ClientID: azureadClientID.String,
|
||||
ClientSecret: azureadClientSecret,
|
||||
Scopes: azureadScopes,
|
||||
Tenant: azureadTenant.String,
|
||||
IsEmailVerified: azureadIsEmailVerified.Bool,
|
||||
}
|
||||
}
|
||||
if githubID.Valid {
|
||||
idpTemplate.GitHubIDPTemplate = &GitHubIDPTemplate{
|
||||
IDPID: githubID.String,
|
||||
|
@ -49,6 +49,13 @@ var (
|
||||
` projections.idp_templates3_jwt.jwt_endpoint,` +
|
||||
` projections.idp_templates3_jwt.keys_endpoint,` +
|
||||
` projections.idp_templates3_jwt.header_name,` +
|
||||
// azure
|
||||
` projections.idp_templates3_azure.idp_id,` +
|
||||
` projections.idp_templates3_azure.client_id,` +
|
||||
` projections.idp_templates3_azure.client_secret,` +
|
||||
` projections.idp_templates3_azure.scopes,` +
|
||||
` projections.idp_templates3_azure.tenant,` +
|
||||
` projections.idp_templates3_azure.is_email_verified,` +
|
||||
// github
|
||||
` projections.idp_templates3_github.idp_id,` +
|
||||
` projections.idp_templates3_github.client_id,` +
|
||||
@ -105,6 +112,7 @@ var (
|
||||
` LEFT JOIN projections.idp_templates3_oauth2 ON projections.idp_templates3.id = projections.idp_templates3_oauth2.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oauth2.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_oidc ON projections.idp_templates3.id = projections.idp_templates3_oidc.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oidc.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_jwt ON projections.idp_templates3.id = projections.idp_templates3_jwt.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_jwt.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_azure ON projections.idp_templates3.id = projections.idp_templates3_azure.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_azure.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_github ON projections.idp_templates3.id = projections.idp_templates3_github.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_github_enterprise ON projections.idp_templates3.id = projections.idp_templates3_github_enterprise.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github_enterprise.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_gitlab ON projections.idp_templates3.id = projections.idp_templates3_gitlab.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_gitlab.instance_id` +
|
||||
@ -147,6 +155,13 @@ var (
|
||||
"jwt_endpoint",
|
||||
"keys_endpoint",
|
||||
"header_name",
|
||||
// azure
|
||||
"idp_id",
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"scopes",
|
||||
"tenant",
|
||||
"is_email_verified",
|
||||
// github config
|
||||
"idp_id",
|
||||
"client_id",
|
||||
@ -234,6 +249,13 @@ var (
|
||||
` projections.idp_templates3_jwt.jwt_endpoint,` +
|
||||
` projections.idp_templates3_jwt.keys_endpoint,` +
|
||||
` projections.idp_templates3_jwt.header_name,` +
|
||||
// azure
|
||||
` projections.idp_templates3_azure.idp_id,` +
|
||||
` projections.idp_templates3_azure.client_id,` +
|
||||
` projections.idp_templates3_azure.client_secret,` +
|
||||
` projections.idp_templates3_azure.scopes,` +
|
||||
` projections.idp_templates3_azure.tenant,` +
|
||||
` projections.idp_templates3_azure.is_email_verified,` +
|
||||
// github
|
||||
` projections.idp_templates3_github.idp_id,` +
|
||||
` projections.idp_templates3_github.client_id,` +
|
||||
@ -291,6 +313,7 @@ var (
|
||||
` LEFT JOIN projections.idp_templates3_oauth2 ON projections.idp_templates3.id = projections.idp_templates3_oauth2.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oauth2.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_oidc ON projections.idp_templates3.id = projections.idp_templates3_oidc.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_oidc.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_jwt ON projections.idp_templates3.id = projections.idp_templates3_jwt.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_jwt.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_azure ON projections.idp_templates3.id = projections.idp_templates3_azure.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_azure.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_github ON projections.idp_templates3.id = projections.idp_templates3_github.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_github_enterprise ON projections.idp_templates3.id = projections.idp_templates3_github_enterprise.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_github_enterprise.instance_id` +
|
||||
` LEFT JOIN projections.idp_templates3_gitlab ON projections.idp_templates3.id = projections.idp_templates3_gitlab.idp_id AND projections.idp_templates3.instance_id = projections.idp_templates3_gitlab.instance_id` +
|
||||
@ -333,6 +356,13 @@ var (
|
||||
"jwt_endpoint",
|
||||
"keys_endpoint",
|
||||
"header_name",
|
||||
// azure
|
||||
"idp_id",
|
||||
"client_id",
|
||||
"client_secret",
|
||||
"scopes",
|
||||
"tenant",
|
||||
"is_email_verified",
|
||||
// github config
|
||||
"idp_id",
|
||||
"client_id",
|
||||
@ -460,6 +490,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -583,6 +620,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -703,6 +747,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
"jwt",
|
||||
"keys",
|
||||
"header",
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -823,6 +874,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
"idp-id",
|
||||
"client_id",
|
||||
@ -942,6 +1000,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1061,6 +1126,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1181,6 +1253,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1300,6 +1379,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1438,6 +1524,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1587,6 +1680,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1734,6 +1834,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1856,6 +1963,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -1944,6 +2058,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -2032,6 +2153,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -2120,6 +2248,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
@ -2208,6 +2343,13 @@ func Test_IDPTemplateTemplatesPrepares(t *testing.T) {
|
||||
"jwt",
|
||||
"keys",
|
||||
"header",
|
||||
// azure
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
// github
|
||||
nil,
|
||||
nil,
|
||||
|
@ -21,6 +21,7 @@ const (
|
||||
IDPTemplateOAuthTable = IDPTemplateTable + "_" + IDPTemplateOAuthSuffix
|
||||
IDPTemplateOIDCTable = IDPTemplateTable + "_" + IDPTemplateOIDCSuffix
|
||||
IDPTemplateJWTTable = IDPTemplateTable + "_" + IDPTemplateJWTSuffix
|
||||
IDPTemplateAzureADTable = IDPTemplateTable + "_" + IDPTemplateAzureADSuffix
|
||||
IDPTemplateGitHubTable = IDPTemplateTable + "_" + IDPTemplateGitHubSuffix
|
||||
IDPTemplateGitHubEnterpriseTable = IDPTemplateTable + "_" + IDPTemplateGitHubEnterpriseSuffix
|
||||
IDPTemplateGitLabTable = IDPTemplateTable + "_" + IDPTemplateGitLabSuffix
|
||||
@ -31,6 +32,7 @@ const (
|
||||
IDPTemplateOAuthSuffix = "oauth2"
|
||||
IDPTemplateOIDCSuffix = "oidc"
|
||||
IDPTemplateJWTSuffix = "jwt"
|
||||
IDPTemplateAzureADSuffix = "azure"
|
||||
IDPTemplateGitHubSuffix = "github"
|
||||
IDPTemplateGitHubEnterpriseSuffix = "github_enterprise"
|
||||
IDPTemplateGitLabSuffix = "gitlab"
|
||||
@ -78,6 +80,14 @@ const (
|
||||
JWTKeysEndpointCol = "keys_endpoint"
|
||||
JWTHeaderNameCol = "header_name"
|
||||
|
||||
AzureADIDCol = "idp_id"
|
||||
AzureADInstanceIDCol = "instance_id"
|
||||
AzureADClientIDCol = "client_id"
|
||||
AzureADClientSecretCol = "client_secret"
|
||||
AzureADScopesCol = "scopes"
|
||||
AzureADTenantCol = "tenant"
|
||||
AzureADIsEmailVerified = "is_email_verified"
|
||||
|
||||
GitHubIDCol = "idp_id"
|
||||
GitHubInstanceIDCol = "instance_id"
|
||||
GitHubClientIDCol = "client_id"
|
||||
@ -206,6 +216,19 @@ func newIDPTemplateProjection(ctx context.Context, config crdb.StatementHandlerC
|
||||
IDPTemplateJWTSuffix,
|
||||
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()),
|
||||
),
|
||||
crdb.NewSuffixedTable([]*crdb.Column{
|
||||
crdb.NewColumn(AzureADIDCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(AzureADInstanceIDCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(AzureADClientIDCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(AzureADClientSecretCol, crdb.ColumnTypeJSONB),
|
||||
crdb.NewColumn(AzureADScopesCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||
crdb.NewColumn(AzureADTenantCol, crdb.ColumnTypeText, crdb.Nullable()),
|
||||
crdb.NewColumn(AzureADIsEmailVerified, crdb.ColumnTypeBool, crdb.Default(false)),
|
||||
},
|
||||
crdb.NewPrimaryKey(AzureADInstanceIDCol, AzureADIDCol),
|
||||
IDPTemplateAzureADSuffix,
|
||||
crdb.WithForeignKey(crdb.NewForeignKeyOfPublicKeys()),
|
||||
),
|
||||
crdb.NewSuffixedTable([]*crdb.Column{
|
||||
crdb.NewColumn(GitHubIDCol, crdb.ColumnTypeText),
|
||||
crdb.NewColumn(GitHubInstanceIDCol, crdb.ColumnTypeText),
|
||||
@ -352,6 +375,14 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer {
|
||||
Event: instance.IDPJWTConfigChangedEventType,
|
||||
Reduce: p.reduceOldJWTConfigChanged,
|
||||
},
|
||||
{
|
||||
Event: instance.AzureADIDPAddedEventType,
|
||||
Reduce: p.reduceAzureADIDPAdded,
|
||||
},
|
||||
{
|
||||
Event: instance.AzureADIDPChangedEventType,
|
||||
Reduce: p.reduceAzureADIDPChanged,
|
||||
},
|
||||
{
|
||||
Event: instance.GitHubIDPAddedEventType,
|
||||
Reduce: p.reduceGitHubIDPAdded,
|
||||
@ -429,7 +460,6 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer {
|
||||
Event: org.OIDCIDPChangedEventType,
|
||||
Reduce: p.reduceOIDCIDPChanged,
|
||||
},
|
||||
|
||||
{
|
||||
Event: org.JWTIDPAddedEventType,
|
||||
Reduce: p.reduceJWTIDPAdded,
|
||||
@ -462,6 +492,14 @@ func (p *idpTemplateProjection) reducers() []handler.AggregateReducer {
|
||||
Event: org.IDPJWTConfigChangedEventType,
|
||||
Reduce: p.reduceOldJWTConfigChanged,
|
||||
},
|
||||
{
|
||||
Event: org.AzureADIDPAddedEventType,
|
||||
Reduce: p.reduceAzureADIDPAdded,
|
||||
},
|
||||
{
|
||||
Event: org.AzureADIDPChangedEventType,
|
||||
Reduce: p.reduceAzureADIDPChanged,
|
||||
},
|
||||
{
|
||||
Event: org.GitHubIDPAddedEventType,
|
||||
Reduce: p.reduceGitHubIDPAdded,
|
||||
@ -1049,6 +1087,96 @@ func (p *idpTemplateProjection) reduceOldJWTConfigChanged(event eventstore.Event
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *idpTemplateProjection) reduceAzureADIDPAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||
var idpEvent idp.AzureADIDPAddedEvent
|
||||
var idpOwnerType domain.IdentityProviderType
|
||||
switch e := event.(type) {
|
||||
case *org.AzureADIDPAddedEvent:
|
||||
idpEvent = e.AzureADIDPAddedEvent
|
||||
idpOwnerType = domain.IdentityProviderTypeOrg
|
||||
case *instance.AzureADIDPAddedEvent:
|
||||
idpEvent = e.AzureADIDPAddedEvent
|
||||
idpOwnerType = domain.IdentityProviderTypeSystem
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-x9a022b", "reduce.wrong.event.type %v", []eventstore.EventType{org.AzureADIDPAddedEventType, instance.AzureADIDPAddedEventType})
|
||||
}
|
||||
|
||||
return crdb.NewMultiStatement(
|
||||
&idpEvent,
|
||||
crdb.AddCreateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(IDPTemplateIDCol, idpEvent.ID),
|
||||
handler.NewCol(IDPTemplateCreationDateCol, idpEvent.CreationDate()),
|
||||
handler.NewCol(IDPTemplateChangeDateCol, idpEvent.CreationDate()),
|
||||
handler.NewCol(IDPTemplateSequenceCol, idpEvent.Sequence()),
|
||||
handler.NewCol(IDPTemplateResourceOwnerCol, idpEvent.Aggregate().ResourceOwner),
|
||||
handler.NewCol(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID),
|
||||
handler.NewCol(IDPTemplateStateCol, domain.IDPStateActive),
|
||||
handler.NewCol(IDPTemplateNameCol, idpEvent.Name),
|
||||
handler.NewCol(IDPTemplateOwnerTypeCol, idpOwnerType),
|
||||
handler.NewCol(IDPTemplateTypeCol, domain.IDPTypeAzureAD),
|
||||
handler.NewCol(IDPTemplateIsCreationAllowedCol, idpEvent.IsCreationAllowed),
|
||||
handler.NewCol(IDPTemplateIsLinkingAllowedCol, idpEvent.IsLinkingAllowed),
|
||||
handler.NewCol(IDPTemplateIsAutoCreationCol, idpEvent.IsAutoCreation),
|
||||
handler.NewCol(IDPTemplateIsAutoUpdateCol, idpEvent.IsAutoUpdate),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
[]handler.Column{
|
||||
handler.NewCol(AzureADIDCol, idpEvent.ID),
|
||||
handler.NewCol(AzureADInstanceIDCol, idpEvent.Aggregate().InstanceID),
|
||||
handler.NewCol(AzureADClientIDCol, idpEvent.ClientID),
|
||||
handler.NewCol(AzureADClientSecretCol, idpEvent.ClientSecret),
|
||||
handler.NewCol(AzureADScopesCol, database.StringArray(idpEvent.Scopes)),
|
||||
handler.NewCol(AzureADTenantCol, idpEvent.Tenant),
|
||||
handler.NewCol(AzureADIsEmailVerified, idpEvent.IsEmailVerified),
|
||||
},
|
||||
crdb.WithTableSuffix(IDPTemplateAzureADSuffix),
|
||||
),
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *idpTemplateProjection) reduceAzureADIDPChanged(event eventstore.Event) (*handler.Statement, error) {
|
||||
var idpEvent idp.AzureADIDPChangedEvent
|
||||
switch e := event.(type) {
|
||||
case *org.AzureADIDPChangedEvent:
|
||||
idpEvent = e.AzureADIDPChangedEvent
|
||||
case *instance.AzureADIDPChangedEvent:
|
||||
idpEvent = e.AzureADIDPChangedEvent
|
||||
default:
|
||||
return nil, errors.ThrowInvalidArgumentf(nil, "HANDL-p1582ks", "reduce.wrong.event.type %v", []eventstore.EventType{org.AzureADIDPChangedEventType, instance.AzureADIDPChangedEventType})
|
||||
}
|
||||
|
||||
ops := make([]func(eventstore.Event) crdb.Exec, 0, 2)
|
||||
ops = append(ops,
|
||||
crdb.AddUpdateStatement(
|
||||
reduceIDPChangedTemplateColumns(idpEvent.Name, idpEvent.CreationDate(), idpEvent.Sequence(), idpEvent.OptionChanges),
|
||||
[]handler.Condition{
|
||||
handler.NewCond(IDPTemplateIDCol, idpEvent.ID),
|
||||
handler.NewCond(IDPTemplateInstanceIDCol, idpEvent.Aggregate().InstanceID),
|
||||
},
|
||||
),
|
||||
)
|
||||
githubCols := reduceAzureADIDPChangedColumns(idpEvent)
|
||||
if len(githubCols) > 0 {
|
||||
ops = append(ops,
|
||||
crdb.AddUpdateStatement(
|
||||
githubCols,
|
||||
[]handler.Condition{
|
||||
handler.NewCond(AzureADIDCol, idpEvent.ID),
|
||||
handler.NewCond(AzureADInstanceIDCol, idpEvent.Aggregate().InstanceID),
|
||||
},
|
||||
crdb.WithTableSuffix(IDPTemplateAzureADSuffix),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
return crdb.NewMultiStatement(
|
||||
&idpEvent,
|
||||
ops...,
|
||||
), nil
|
||||
}
|
||||
|
||||
func (p *idpTemplateProjection) reduceGitHubIDPAdded(event eventstore.Event) (*handler.Statement, error) {
|
||||
var idpEvent idp.GitHubIDPAddedEvent
|
||||
var idpOwnerType domain.IdentityProviderType
|
||||
@ -1723,6 +1851,26 @@ func reduceJWTIDPChangedColumns(idpEvent idp.JWTIDPChangedEvent) []handler.Colum
|
||||
return jwtCols
|
||||
}
|
||||
|
||||
func reduceAzureADIDPChangedColumns(idpEvent idp.AzureADIDPChangedEvent) []handler.Column {
|
||||
azureADCols := make([]handler.Column, 0, 5)
|
||||
if idpEvent.ClientID != nil {
|
||||
azureADCols = append(azureADCols, handler.NewCol(AzureADClientIDCol, *idpEvent.ClientID))
|
||||
}
|
||||
if idpEvent.ClientSecret != nil {
|
||||
azureADCols = append(azureADCols, handler.NewCol(AzureADClientSecretCol, *idpEvent.ClientSecret))
|
||||
}
|
||||
if idpEvent.Scopes != nil {
|
||||
azureADCols = append(azureADCols, handler.NewCol(AzureADScopesCol, database.StringArray(idpEvent.Scopes)))
|
||||
}
|
||||
if idpEvent.Tenant != nil {
|
||||
azureADCols = append(azureADCols, handler.NewCol(AzureADTenantCol, *idpEvent.Tenant))
|
||||
}
|
||||
if idpEvent.IsEmailVerified != nil {
|
||||
azureADCols = append(azureADCols, handler.NewCol(AzureADIsEmailVerified, *idpEvent.IsEmailVerified))
|
||||
}
|
||||
return azureADCols
|
||||
}
|
||||
|
||||
func reduceGitHubIDPChangedColumns(idpEvent idp.GitHubIDPChangedEvent) []handler.Column {
|
||||
oauthCols := make([]handler.Column, 0, 3)
|
||||
if idpEvent.ClientID != nil {
|
||||
|
@ -410,6 +410,330 @@ func TestIDPTemplateProjection_reducesOAuth(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDPTemplateProjection_reducesAzureAD(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: "instance reduceAzureADIDPAdded minimal",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.AzureADIDPAddedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{
|
||||
"id": "idp-id",
|
||||
"name": "name",
|
||||
"client_id": "client_id",
|
||||
"client_secret": {
|
||||
"cryptoType": 0,
|
||||
"algorithm": "RSA-265",
|
||||
"keyId": "key-id"
|
||||
}
|
||||
}`),
|
||||
), instance.AzureADIDPAddedEventMapper),
|
||||
},
|
||||
reduce: (&idpTemplateProjection{}).reduceAzureADIDPAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: idpTemplateInsertStmt,
|
||||
expectedArgs: []interface{}{
|
||||
"idp-id",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"ro-id",
|
||||
"instance-id",
|
||||
domain.IDPStateActive,
|
||||
"name",
|
||||
domain.IdentityProviderTypeSystem,
|
||||
domain.IDPTypeAzureAD,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.idp_templates3_azure (idp_id, instance_id, client_id, client_secret, scopes, tenant, is_email_verified) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedArgs: []interface{}{
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
"client_id",
|
||||
anyArg{},
|
||||
database.StringArray(nil),
|
||||
"",
|
||||
false,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "instance reduceAzureADIDPAdded",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.AzureADIDPAddedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{
|
||||
"id": "idp-id",
|
||||
"name": "name",
|
||||
"client_id": "client_id",
|
||||
"client_secret": {
|
||||
"cryptoType": 0,
|
||||
"algorithm": "RSA-265",
|
||||
"keyId": "key-id"
|
||||
},
|
||||
"tenant": "tenant",
|
||||
"isEmailVerified": true,
|
||||
"scopes": ["profile"],
|
||||
"isCreationAllowed": true,
|
||||
"isLinkingAllowed": true,
|
||||
"isAutoCreation": true,
|
||||
"isAutoUpdate": true
|
||||
}`),
|
||||
), instance.AzureADIDPAddedEventMapper),
|
||||
},
|
||||
reduce: (&idpTemplateProjection{}).reduceAzureADIDPAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: idpTemplateInsertStmt,
|
||||
expectedArgs: []interface{}{
|
||||
"idp-id",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"ro-id",
|
||||
"instance-id",
|
||||
domain.IDPStateActive,
|
||||
"name",
|
||||
domain.IdentityProviderTypeSystem,
|
||||
domain.IDPTypeAzureAD,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.idp_templates3_azure (idp_id, instance_id, client_id, client_secret, scopes, tenant, is_email_verified) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedArgs: []interface{}{
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
"client_id",
|
||||
anyArg{},
|
||||
database.StringArray{"profile"},
|
||||
"tenant",
|
||||
true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "org reduceAzureADIDPAdded",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(org.AzureADIDPAddedEventType),
|
||||
org.AggregateType,
|
||||
[]byte(`{
|
||||
"id": "idp-id",
|
||||
"name": "name",
|
||||
"client_id": "client_id",
|
||||
"client_secret": {
|
||||
"cryptoType": 0,
|
||||
"algorithm": "RSA-265",
|
||||
"keyId": "key-id"
|
||||
},
|
||||
"tenant": "tenant",
|
||||
"isEmailVerified": true,
|
||||
"scopes": ["profile"],
|
||||
"isCreationAllowed": true,
|
||||
"isLinkingAllowed": true,
|
||||
"isAutoCreation": true,
|
||||
"isAutoUpdate": true
|
||||
}`),
|
||||
), org.AzureADIDPAddedEventMapper),
|
||||
},
|
||||
reduce: (&idpTemplateProjection{}).reduceAzureADIDPAdded,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("org"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: idpTemplateInsertStmt,
|
||||
expectedArgs: []interface{}{
|
||||
"idp-id",
|
||||
anyArg{},
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"ro-id",
|
||||
"instance-id",
|
||||
domain.IDPStateActive,
|
||||
"name",
|
||||
domain.IdentityProviderTypeOrg,
|
||||
domain.IDPTypeAzureAD,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "INSERT INTO projections.idp_templates3_azure (idp_id, instance_id, client_id, client_secret, scopes, tenant, is_email_verified) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedArgs: []interface{}{
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
"client_id",
|
||||
anyArg{},
|
||||
database.StringArray{"profile"},
|
||||
"tenant",
|
||||
true,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "instance reduceAzureADIDPChanged minimal",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.AzureADIDPChangedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{
|
||||
"id": "idp-id",
|
||||
"isCreationAllowed": true,
|
||||
"client_id": "id"
|
||||
}`),
|
||||
), instance.AzureADIDPChangedEventMapper),
|
||||
},
|
||||
reduce: (&idpTemplateProjection{}).reduceAzureADIDPChanged,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: idpTemplateUpdateMinimalStmt,
|
||||
expectedArgs: []interface{}{
|
||||
true,
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.idp_templates3_azure SET client_id = $1 WHERE (idp_id = $2) AND (instance_id = $3)",
|
||||
expectedArgs: []interface{}{
|
||||
"id",
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "instance reduceAzureADIDPChanged",
|
||||
args: args{
|
||||
event: getEvent(testEvent(
|
||||
repository.EventType(instance.AzureADIDPChangedEventType),
|
||||
instance.AggregateType,
|
||||
[]byte(`{
|
||||
"id": "idp-id",
|
||||
"name": "name",
|
||||
"client_id": "client_id",
|
||||
"client_secret": {
|
||||
"cryptoType": 0,
|
||||
"algorithm": "RSA-265",
|
||||
"keyId": "key-id"
|
||||
},
|
||||
"tenant": "tenant",
|
||||
"isEmailVerified": true,
|
||||
"scopes": ["profile"],
|
||||
"isCreationAllowed": true,
|
||||
"isLinkingAllowed": true,
|
||||
"isAutoCreation": true,
|
||||
"isAutoUpdate": true
|
||||
}`),
|
||||
), instance.AzureADIDPChangedEventMapper),
|
||||
},
|
||||
reduce: (&idpTemplateProjection{}).reduceAzureADIDPChanged,
|
||||
want: wantReduce{
|
||||
aggregateType: eventstore.AggregateType("instance"),
|
||||
sequence: 15,
|
||||
previousSequence: 10,
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: idpTemplateUpdateStmt,
|
||||
expectedArgs: []interface{}{
|
||||
"name",
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
anyArg{},
|
||||
uint64(15),
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE projections.idp_templates3_azure SET (client_id, client_secret, scopes, tenant, is_email_verified) = ($1, $2, $3, $4, $5) WHERE (idp_id = $6) AND (instance_id = $7)",
|
||||
expectedArgs: []interface{}{
|
||||
"client_id",
|
||||
anyArg{},
|
||||
database.StringArray{"profile"},
|
||||
"tenant",
|
||||
true,
|
||||
"idp-id",
|
||||
"instance-id",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
event := baseEvent(t)
|
||||
got, err := tt.reduce(event)
|
||||
if !errors.IsErrorInvalidArgument(err) {
|
||||
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, IDPTemplateTable, tt.want)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestIDPTemplateProjection_reducesGitHub(t *testing.T) {
|
||||
type args struct {
|
||||
event func(t *testing.T) eventstore.Event
|
||||
|
164
internal/repository/idp/azuread.go
Normal file
164
internal/repository/idp/azuread.go
Normal file
@ -0,0 +1,164 @@
|
||||
package idp
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/crypto"
|
||||
"github.com/zitadel/zitadel/internal/errors"
|
||||
"github.com/zitadel/zitadel/internal/eventstore"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/repository"
|
||||
)
|
||||
|
||||
type AzureADIDPAddedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
ID string `json:"id"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ClientID string `json:"client_id,omitempty"`
|
||||
ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"`
|
||||
Scopes []string `json:"scopes,omitempty"`
|
||||
Tenant string `json:"tenant,omitempty"`
|
||||
IsEmailVerified bool `json:"isEmailVerified,omitempty"`
|
||||
Options
|
||||
}
|
||||
|
||||
func NewAzureADIDPAddedEvent(
|
||||
base *eventstore.BaseEvent,
|
||||
id,
|
||||
name,
|
||||
clientID string,
|
||||
clientSecret *crypto.CryptoValue,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
options Options,
|
||||
) *AzureADIDPAddedEvent {
|
||||
return &AzureADIDPAddedEvent{
|
||||
BaseEvent: *base,
|
||||
ID: id,
|
||||
Name: name,
|
||||
ClientID: clientID,
|
||||
ClientSecret: clientSecret,
|
||||
Scopes: scopes,
|
||||
Tenant: tenant,
|
||||
IsEmailVerified: isEmailVerified,
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
func (e *AzureADIDPAddedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *AzureADIDPAddedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AzureADIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e := &AzureADIDPAddedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(event.Data, e)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "IDP-Grh2g", "unable to unmarshal event")
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
||||
|
||||
type AzureADIDPChangedEvent struct {
|
||||
eventstore.BaseEvent `json:"-"`
|
||||
|
||||
ID string `json:"id"`
|
||||
Name *string `json:"name,omitempty"`
|
||||
ClientID *string `json:"client_id,omitempty"`
|
||||
ClientSecret *crypto.CryptoValue `json:"client_secret,omitempty"`
|
||||
Scopes []string `json:"scopes,omitempty"`
|
||||
Tenant *string `json:"tenant,omitempty"`
|
||||
IsEmailVerified *bool `json:"isEmailVerified,omitempty"`
|
||||
OptionChanges
|
||||
}
|
||||
|
||||
func NewAzureADIDPChangedEvent(
|
||||
base *eventstore.BaseEvent,
|
||||
id string,
|
||||
changes []AzureADIDPChanges,
|
||||
) (*AzureADIDPChangedEvent, error) {
|
||||
if len(changes) == 0 {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "IDP-BH3dl", "Errors.NoChangesFound")
|
||||
}
|
||||
changedEvent := &AzureADIDPChangedEvent{
|
||||
BaseEvent: *base,
|
||||
ID: id,
|
||||
}
|
||||
for _, change := range changes {
|
||||
change(changedEvent)
|
||||
}
|
||||
return changedEvent, nil
|
||||
}
|
||||
|
||||
type AzureADIDPChanges func(*AzureADIDPChangedEvent)
|
||||
|
||||
func ChangeAzureADName(name string) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.Name = &name
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeAzureADClientID(clientID string) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.ClientID = &clientID
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeAzureADClientSecret(clientSecret *crypto.CryptoValue) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.ClientSecret = clientSecret
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeAzureADOptions(options OptionChanges) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.OptionChanges = options
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeAzureADScopes(scopes []string) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.Scopes = scopes
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeAzureADTenant(tenant string) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.Tenant = &tenant
|
||||
}
|
||||
}
|
||||
|
||||
func ChangeAzureADIsEmailVerified(isEmailVerified bool) func(*AzureADIDPChangedEvent) {
|
||||
return func(e *AzureADIDPChangedEvent) {
|
||||
e.IsEmailVerified = &isEmailVerified
|
||||
}
|
||||
}
|
||||
|
||||
func (e *AzureADIDPChangedEvent) Data() interface{} {
|
||||
return e
|
||||
}
|
||||
|
||||
func (e *AzureADIDPChangedEvent) UniqueConstraints() []*eventstore.EventUniqueConstraint {
|
||||
return nil
|
||||
}
|
||||
|
||||
func AzureADIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e := &AzureADIDPChangedEvent{
|
||||
BaseEvent: *eventstore.BaseEventFromRepo(event),
|
||||
}
|
||||
|
||||
err := json.Unmarshal(event.Data, e)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "IDP-D3gjzh", "unable to unmarshal event")
|
||||
}
|
||||
|
||||
return e, nil
|
||||
}
|
@ -76,6 +76,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, AzureADIDPAddedEventType, AzureADIDPAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, AzureADIDPChangedEventType, AzureADIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, GitHubIDPAddedEventType, GitHubIDPAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, GitHubIDPChangedEventType, GitHubIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPAddedEventType, GitHubEnterpriseIDPAddedEventMapper).
|
||||
|
@ -16,6 +16,8 @@ const (
|
||||
OIDCIDPChangedEventType eventstore.EventType = "instance.idp.oidc.changed"
|
||||
JWTIDPAddedEventType eventstore.EventType = "instance.idp.jwt.added"
|
||||
JWTIDPChangedEventType eventstore.EventType = "instance.idp.jwt.changed"
|
||||
AzureADIDPAddedEventType eventstore.EventType = "instance.idp.azure.added"
|
||||
AzureADIDPChangedEventType eventstore.EventType = "instance.idp.azure.changed"
|
||||
GitHubIDPAddedEventType eventstore.EventType = "instance.idp.github.added"
|
||||
GitHubIDPChangedEventType eventstore.EventType = "instance.idp.github.changed"
|
||||
GitHubEnterpriseIDPAddedEventType eventstore.EventType = "instance.idp.github_enterprise.added"
|
||||
@ -271,6 +273,86 @@ func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error)
|
||||
return &JWTIDPChangedEvent{JWTIDPChangedEvent: *e.(*idp.JWTIDPChangedEvent)}, nil
|
||||
}
|
||||
|
||||
type AzureADIDPAddedEvent struct {
|
||||
idp.AzureADIDPAddedEvent
|
||||
}
|
||||
|
||||
func NewAzureADIDPAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
id,
|
||||
name,
|
||||
clientID string,
|
||||
clientSecret *crypto.CryptoValue,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
options idp.Options,
|
||||
) *AzureADIDPAddedEvent {
|
||||
|
||||
return &AzureADIDPAddedEvent{
|
||||
AzureADIDPAddedEvent: *idp.NewAzureADIDPAddedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
AzureADIDPAddedEventType,
|
||||
),
|
||||
id,
|
||||
name,
|
||||
clientID,
|
||||
clientSecret,
|
||||
scopes,
|
||||
tenant,
|
||||
isEmailVerified,
|
||||
options,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func AzureADIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := idp.AzureADIDPAddedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AzureADIDPAddedEvent{AzureADIDPAddedEvent: *e.(*idp.AzureADIDPAddedEvent)}, nil
|
||||
}
|
||||
|
||||
type AzureADIDPChangedEvent struct {
|
||||
idp.AzureADIDPChangedEvent
|
||||
}
|
||||
|
||||
func NewAzureADIDPChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
id string,
|
||||
changes []idp.AzureADIDPChanges,
|
||||
) (*AzureADIDPChangedEvent, error) {
|
||||
|
||||
changedEvent, err := idp.NewAzureADIDPChangedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
AzureADIDPChangedEventType,
|
||||
),
|
||||
id,
|
||||
changes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AzureADIDPChangedEvent{AzureADIDPChangedEvent: *changedEvent}, nil
|
||||
}
|
||||
|
||||
func AzureADIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := idp.AzureADIDPChangedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AzureADIDPChangedEvent{AzureADIDPChangedEvent: *e.(*idp.AzureADIDPChangedEvent)}, nil
|
||||
}
|
||||
|
||||
type GitHubIDPAddedEvent struct {
|
||||
idp.GitHubIDPAddedEvent
|
||||
}
|
||||
|
@ -84,6 +84,8 @@ func RegisterEventMappers(es *eventstore.Eventstore) {
|
||||
RegisterFilterEventMapper(AggregateType, OIDCIDPChangedEventType, OIDCIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, JWTIDPAddedEventType, JWTIDPAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, JWTIDPChangedEventType, JWTIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, AzureADIDPAddedEventType, AzureADIDPAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, AzureADIDPChangedEventType, AzureADIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, GitHubIDPAddedEventType, GitHubIDPAddedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, GitHubIDPChangedEventType, GitHubIDPChangedEventMapper).
|
||||
RegisterFilterEventMapper(AggregateType, GitHubEnterpriseIDPAddedEventType, GitHubEnterpriseIDPAddedEventMapper).
|
||||
|
@ -16,6 +16,8 @@ const (
|
||||
OIDCIDPChangedEventType eventstore.EventType = "org.idp.oidc.changed"
|
||||
JWTIDPAddedEventType eventstore.EventType = "org.idp.jwt.added"
|
||||
JWTIDPChangedEventType eventstore.EventType = "org.idp.jwt.changed"
|
||||
AzureADIDPAddedEventType eventstore.EventType = "org.idp.azure.added"
|
||||
AzureADIDPChangedEventType eventstore.EventType = "org.idp.azure.changed"
|
||||
GitHubIDPAddedEventType eventstore.EventType = "org.idp.github.added"
|
||||
GitHubIDPChangedEventType eventstore.EventType = "org.idp.github.changed"
|
||||
GitHubEnterpriseIDPAddedEventType eventstore.EventType = "org.idp.github_enterprise.added"
|
||||
@ -271,6 +273,86 @@ func JWTIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error)
|
||||
return &JWTIDPChangedEvent{JWTIDPChangedEvent: *e.(*idp.JWTIDPChangedEvent)}, nil
|
||||
}
|
||||
|
||||
type AzureADIDPAddedEvent struct {
|
||||
idp.AzureADIDPAddedEvent
|
||||
}
|
||||
|
||||
func NewAzureADIDPAddedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
id,
|
||||
name,
|
||||
clientID string,
|
||||
clientSecret *crypto.CryptoValue,
|
||||
scopes []string,
|
||||
tenant string,
|
||||
isEmailVerified bool,
|
||||
options idp.Options,
|
||||
) *AzureADIDPAddedEvent {
|
||||
|
||||
return &AzureADIDPAddedEvent{
|
||||
AzureADIDPAddedEvent: *idp.NewAzureADIDPAddedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
AzureADIDPAddedEventType,
|
||||
),
|
||||
id,
|
||||
name,
|
||||
clientID,
|
||||
clientSecret,
|
||||
scopes,
|
||||
tenant,
|
||||
isEmailVerified,
|
||||
options,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func AzureADIDPAddedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := idp.AzureADIDPAddedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AzureADIDPAddedEvent{AzureADIDPAddedEvent: *e.(*idp.AzureADIDPAddedEvent)}, nil
|
||||
}
|
||||
|
||||
type AzureADIDPChangedEvent struct {
|
||||
idp.AzureADIDPChangedEvent
|
||||
}
|
||||
|
||||
func NewAzureADIDPChangedEvent(
|
||||
ctx context.Context,
|
||||
aggregate *eventstore.Aggregate,
|
||||
id string,
|
||||
changes []idp.AzureADIDPChanges,
|
||||
) (*AzureADIDPChangedEvent, error) {
|
||||
|
||||
changedEvent, err := idp.NewAzureADIDPChangedEvent(
|
||||
eventstore.NewBaseEventForPush(
|
||||
ctx,
|
||||
aggregate,
|
||||
AzureADIDPChangedEventType,
|
||||
),
|
||||
id,
|
||||
changes,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &AzureADIDPChangedEvent{AzureADIDPChangedEvent: *changedEvent}, nil
|
||||
}
|
||||
|
||||
func AzureADIDPChangedEventMapper(event *repository.Event) (eventstore.Event, error) {
|
||||
e, err := idp.AzureADIDPChangedEventMapper(event)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &AzureADIDPChangedEvent{AzureADIDPChangedEvent: *e.(*idp.AzureADIDPChangedEvent)}, nil
|
||||
}
|
||||
|
||||
type GitHubIDPAddedEvent struct {
|
||||
idp.GitHubIDPAddedEvent
|
||||
}
|
||||
|
@ -1,3 +1,4 @@
|
||||
package idp
|
||||
|
||||
type IDPConfig = isIDP_Config
|
||||
type IsAzureADTenantType = isAzureADTenant_Type
|
||||
|
@ -1320,6 +1320,30 @@ service AdminService {
|
||||
};
|
||||
}
|
||||
|
||||
// Add a new Azure AD identity provider on the instance
|
||||
rpc AddAzureADProvider(AddAzureADProviderRequest) returns (AddAzureADProviderResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/idps/azure"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.idp.write"
|
||||
};
|
||||
}
|
||||
|
||||
// Change an existing Azure AD identity provider on the instance
|
||||
rpc UpdateAzureADProvider(UpdateAzureADProviderRequest) returns (UpdateAzureADProviderResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/idps/azure/{id}"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "iam.idp.write"
|
||||
};
|
||||
}
|
||||
|
||||
// Add a new GitHub identity provider on the instance
|
||||
rpc AddGitHubProvider(AddGitHubProviderRequest) returns (AddGitHubProviderResponse) {
|
||||
option (google.api.http) = {
|
||||
@ -4525,6 +4549,39 @@ message UpdateJWTProviderResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message AddAzureADProviderRequest {
|
||||
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string client_id = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string client_secret = 3 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
// if not provided the `common` tenant will be used
|
||||
zitadel.idp.v1.AzureADTenant tenant = 4;
|
||||
bool email_verified = 5;
|
||||
repeated string scopes = 6 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}];
|
||||
zitadel.idp.v1.Options provider_options = 7;
|
||||
}
|
||||
|
||||
message AddAzureADProviderResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
string id = 2;
|
||||
}
|
||||
|
||||
message UpdateAzureADProviderRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
// client_secret will only be updated if provided
|
||||
string client_secret = 4 [(validate.rules).string = {max_len: 200}];
|
||||
// if not provided the `common` tenant will be used
|
||||
zitadel.idp.v1.AzureADTenant tenant = 5;
|
||||
bool email_verified = 6;
|
||||
repeated string scopes = 7 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}];
|
||||
zitadel.idp.v1.Options provider_options = 8;
|
||||
}
|
||||
|
||||
message UpdateAzureADProviderResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message AddGitHubProviderRequest {
|
||||
// GitHub will be used as default, if no name is provided
|
||||
string name = 1 [(validate.rules).string = {max_len: 200}];
|
||||
|
@ -271,8 +271,10 @@ message ProviderConfig {
|
||||
GitHubEnterpriseServerConfig github_es = 8;
|
||||
GitLabConfig gitlab = 9;
|
||||
GitLabSelfHostedConfig gitlab_self_hosted = 10;
|
||||
AzureADConfig azure_ad = 11;
|
||||
}
|
||||
}
|
||||
|
||||
message OAuthConfig {
|
||||
string client_id = 1;
|
||||
string authorization_endpoint = 2;
|
||||
@ -329,6 +331,13 @@ message LDAPConfig {
|
||||
Options provider_options = 9;
|
||||
}
|
||||
|
||||
message AzureADConfig {
|
||||
string client_id = 1;
|
||||
AzureADTenant tenant = 2;
|
||||
bool email_verified = 3;
|
||||
repeated string scopes = 4;
|
||||
}
|
||||
|
||||
message Options {
|
||||
bool is_linking_allowed = 1;
|
||||
bool is_creation_allowed = 2;
|
||||
@ -352,3 +361,15 @@ message LDAPAttributes {
|
||||
string profile_attribute = 13 [(validate.rules).string = {max_len: 200}];
|
||||
}
|
||||
|
||||
enum AzureADTenantType {
|
||||
AZURE_AD_TENANT_TYPE_COMMON = 0;
|
||||
AZURE_AD_TENANT_TYPE_ORGANISATIONS = 1;
|
||||
AZURE_AD_TENANT_TYPE_CONSUMERS = 2;
|
||||
}
|
||||
|
||||
message AzureADTenant {
|
||||
oneof type {
|
||||
AzureADTenantType tenant_type = 1;
|
||||
string tenant_id = 2;
|
||||
}
|
||||
}
|
||||
|
@ -6536,6 +6536,30 @@ service ManagementService {
|
||||
};
|
||||
}
|
||||
|
||||
// Add a new Azure AD identity provider in the organisation
|
||||
rpc AddAzureADProvider(AddAzureADProviderRequest) returns (AddAzureADProviderResponse) {
|
||||
option (google.api.http) = {
|
||||
post: "/idps/azure"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "org.idp.write"
|
||||
};
|
||||
}
|
||||
|
||||
// Change an existing Azure AD identity provider in the organisation
|
||||
rpc UpdateAzureADProvider(UpdateAzureADProviderRequest) returns (UpdateAzureADProviderResponse) {
|
||||
option (google.api.http) = {
|
||||
put: "/idps/azure/{id}"
|
||||
body: "*"
|
||||
};
|
||||
|
||||
option (zitadel.v1.auth_option) = {
|
||||
permission: "org.idp.write"
|
||||
};
|
||||
}
|
||||
|
||||
// Add a new GitHub identity provider in the organization
|
||||
rpc AddGitHubProvider(AddGitHubProviderRequest) returns (AddGitHubProviderResponse) {
|
||||
option (google.api.http) = {
|
||||
@ -11199,6 +11223,39 @@ message UpdateJWTProviderResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message AddAzureADProviderRequest {
|
||||
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string client_id = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string client_secret = 3 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
// if not provided the `common` tenant will be used
|
||||
zitadel.idp.v1.AzureADTenant tenant = 4;
|
||||
bool email_verified = 5;
|
||||
repeated string scopes = 6 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}];
|
||||
zitadel.idp.v1.Options provider_options = 7;
|
||||
}
|
||||
|
||||
message AddAzureADProviderResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
string id = 2;
|
||||
}
|
||||
|
||||
message UpdateAzureADProviderRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
string client_id = 3 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
// client_secret will only be updated if provided
|
||||
string client_secret = 4 [(validate.rules).string = {max_len: 200}];
|
||||
// if not provided the `common` tenant will be used
|
||||
zitadel.idp.v1.AzureADTenant tenant = 5;
|
||||
bool email_verified = 6;
|
||||
repeated string scopes = 7 [(validate.rules).repeated = {max_items: 20, items: {string: {min_len: 1, max_len: 100}}}];
|
||||
zitadel.idp.v1.Options provider_options = 8;
|
||||
}
|
||||
|
||||
message UpdateAzureADProviderResponse {
|
||||
zitadel.v1.ObjectDetails details = 1;
|
||||
}
|
||||
|
||||
message AddGitHubProviderRequest {
|
||||
// GitHub will be used as default, if no name is provided
|
||||
string name = 1 [(validate.rules).string = {max_len: 200}];
|
||||
|
Loading…
x
Reference in New Issue
Block a user