mirror of
https://github.com/zitadel/zitadel.git
synced 2025-02-28 20:17:23 +00:00
feat: project roles (#843)
* fix logging * token verification * feat: assert roles * feat: add project role assertion on project and token type on app * id and access token role assertion * add project role check * user grant required step in login * update library * fix merge * fix merge * fix merge * update oidc library * fix tests * add tests for GrantRequiredStep * add missing field ProjectRoleCheck on project view model * fix project create * fix project create
This commit is contained in:
parent
f5a7a0a09f
commit
a321d850ae
3
go.mod
3
go.mod
@ -15,7 +15,7 @@ require (
|
||||
github.com/aws/aws-sdk-go v1.34.24 // indirect
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc
|
||||
github.com/caos/logging v0.0.2
|
||||
github.com/caos/oidc v0.11.1
|
||||
github.com/caos/oidc v0.12.0
|
||||
github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect
|
||||
github.com/cockroachdb/cockroach-go/v2 v2.0.7
|
||||
github.com/envoyproxy/protoc-gen-validate v0.4.1
|
||||
@ -37,7 +37,6 @@ require (
|
||||
github.com/kevinburke/go.uuid v1.2.0 // indirect
|
||||
github.com/kevinburke/rest v0.0.0-20200429221318-0d2892b400f8 // indirect
|
||||
github.com/kevinburke/twilio-go v0.0.0-20200810163702-320748330fac
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 // indirect
|
||||
github.com/lib/pq v1.8.0
|
||||
github.com/mattn/go-colorable v0.1.7 // indirect
|
||||
github.com/mattn/go-sqlite3 v2.0.3+incompatible // indirect
|
||||
|
19
go.sum
19
go.sum
@ -71,12 +71,8 @@ github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/caos/logging v0.0.2 h1:ebg5C/HN0ludYR+WkvnFjwSExF4wvyiWPyWGcKMYsoo=
|
||||
github.com/caos/logging v0.0.2/go.mod h1:9LKiDE2ChuGv6CHYif/kiugrfEXu9AwDiFWSreX7Wp0=
|
||||
github.com/caos/oidc v0.10.0 h1:/GKyQgKHvkc2jNCXzmdJs9hCXDYdArCMm3d5FXaiNWA=
|
||||
github.com/caos/oidc v0.10.0/go.mod h1:RREtWSRzH/mXQXJkxB63mFDZ/RUNyzoU6czd6UJfvJI=
|
||||
github.com/caos/oidc v0.11.0 h1:larwR0ur4hcHXkMtlXbHApv4DUr5yu/zbxBeGjcpTXk=
|
||||
github.com/caos/oidc v0.11.0/go.mod h1:R9UKITZmSo5vNhSLUYcTDH8pAaV2xwPASXg8wpEs2xQ=
|
||||
github.com/caos/oidc v0.11.1 h1:7Nkup+fiU/zZVN61BfGOTzuloD7aOdqA3V9eNrJv5xc=
|
||||
github.com/caos/oidc v0.11.1/go.mod h1:R9UKITZmSo5vNhSLUYcTDH8pAaV2xwPASXg8wpEs2xQ=
|
||||
github.com/caos/oidc v0.12.0 h1:BcEwgeq8fpum2hdc47ZDOTwnc4KdHpos1NRbE1JcHN8=
|
||||
github.com/caos/oidc v0.12.0/go.mod h1:R9UKITZmSo5vNhSLUYcTDH8pAaV2xwPASXg8wpEs2xQ=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1 h1:glEXhBS5PSLLv4IXzLA5yPRVX4bilULVyxxbrfOtDAk=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
@ -280,8 +276,6 @@ github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+o
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
@ -349,8 +343,6 @@ github.com/shopspring/decimal v0.0.0-20200419222939-1884f454f8ea/go.mod h1:DKyhr
|
||||
github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q=
|
||||
github.com/sirupsen/logrus v1.4.2 h1:SPIRibHv4MatM3XXNO2BJeFLZwZ2LvZgfQ5+UNI2im4=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0 h1:UBcNElsrwanuuMsnGSlYmtmgbb23qDR5dG+6X6Oo89I=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.7.0 h1:ShrD1U9pZB12TX0cVy0DtePoCH97K8EtX+mg7ZARUtM=
|
||||
github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
|
||||
github.com/sony/sonyflake v1.0.0 h1:MpU6Ro7tfXwgn2l5eluf9xQvQJDROTBImNCfRXn/YeM=
|
||||
@ -409,8 +401,6 @@ golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnk
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee h1:4yd7jl+vXjalO5ztz6Vc1VADv+S/80LGJmyl1ROJ2AI=
|
||||
golang.org/x/crypto v0.0.0-20201012173705-84dcc777aaee/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
@ -484,8 +474,6 @@ golang.org/x/net v0.0.0-20200822124328-c89045814202 h1:VvcQYSHwXgi7W+TpUR6A9g6Up
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73 h1:MXfv8rhZWmFeqX3GNZRsd6vOLoaCHjYEX3qkRo3YBUA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb h1:mUVeFHoDKis5nxCAzoAi7E8Ghb86EXh/RK6wtvJIqRY=
|
||||
golang.org/x/net v0.0.0-20201010224723-4f7140c49acb/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
@ -546,9 +534,6 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121 h1:rITEj+UZHYC927n8GT97eC3zr
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642 h1:B6caxRw+hozq68X2MY7jEpZh/cr4/aHLv9xU8Kkadrw=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201013081832-0aaa2718063a h1:bhXnJ7fn2SiL+C8iOWPfNBJKDTjUByftpPW7b9CX94U=
|
||||
golang.org/x/sys v0.0.0-20201013081832-0aaa2718063a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
|
@ -52,8 +52,11 @@ func (m *IamMember) processIamMember(event *models.Event) (err error) {
|
||||
member := new(iam_model.IAMMemberView)
|
||||
switch event.Type {
|
||||
case model.IAMMemberAdded:
|
||||
member.AppendEvent(event)
|
||||
m.fillData(member)
|
||||
err = member.AppendEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = m.fillData(member)
|
||||
case model.IAMMemberChanged:
|
||||
err := member.SetData(event)
|
||||
if err != nil {
|
||||
@ -63,7 +66,7 @@ func (m *IamMember) processIamMember(event *models.Event) (err error) {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
member.AppendEvent(event)
|
||||
err = member.AppendEvent(event)
|
||||
case model.IAMMemberRemoved:
|
||||
err := member.SetData(event)
|
||||
if err != nil {
|
||||
|
@ -35,7 +35,10 @@ func (o *Org) Reduce(event *es_models.Event) error {
|
||||
|
||||
switch event.Type {
|
||||
case model.OrgAdded:
|
||||
org.AppendEvent(event)
|
||||
err := org.AppendEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
case model.OrgChanged:
|
||||
err := org.SetData(event)
|
||||
if err != nil {
|
||||
|
@ -44,34 +44,40 @@ func appConfigFromModel(app *proj_model.Application) management.AppConfig {
|
||||
|
||||
func oidcConfigFromModel(config *proj_model.OIDCConfig) *management.OIDCConfig {
|
||||
return &management.OIDCConfig{
|
||||
RedirectUris: config.RedirectUris,
|
||||
ResponseTypes: oidcResponseTypesFromModel(config.ResponseTypes),
|
||||
GrantTypes: oidcGrantTypesFromModel(config.GrantTypes),
|
||||
ApplicationType: oidcApplicationTypeFromModel(config.ApplicationType),
|
||||
ClientId: config.ClientID,
|
||||
ClientSecret: config.ClientSecretString,
|
||||
AuthMethodType: oidcAuthMethodTypeFromModel(config.AuthMethodType),
|
||||
PostLogoutRedirectUris: config.PostLogoutRedirectUris,
|
||||
Version: oidcVersionFromModel(config.OIDCVersion),
|
||||
NoneCompliant: config.Compliance.NoneCompliant,
|
||||
ComplianceProblems: complianceProblemsToLocalizedMessages(config.Compliance.Problems),
|
||||
DevMode: config.DevMode,
|
||||
RedirectUris: config.RedirectUris,
|
||||
ResponseTypes: oidcResponseTypesFromModel(config.ResponseTypes),
|
||||
GrantTypes: oidcGrantTypesFromModel(config.GrantTypes),
|
||||
ApplicationType: oidcApplicationTypeFromModel(config.ApplicationType),
|
||||
ClientId: config.ClientID,
|
||||
ClientSecret: config.ClientSecretString,
|
||||
AuthMethodType: oidcAuthMethodTypeFromModel(config.AuthMethodType),
|
||||
PostLogoutRedirectUris: config.PostLogoutRedirectUris,
|
||||
Version: oidcVersionFromModel(config.OIDCVersion),
|
||||
NoneCompliant: config.Compliance.NoneCompliant,
|
||||
ComplianceProblems: complianceProblemsToLocalizedMessages(config.Compliance.Problems),
|
||||
DevMode: config.DevMode,
|
||||
AccessTokenType: oidcTokenTypeFromModel(config.AccessTokenType),
|
||||
AccessTokenRoleAssertion: config.AccessTokenRoleAssertion,
|
||||
IdTokenRoleAssertion: config.IDTokenRoleAssertion,
|
||||
}
|
||||
}
|
||||
|
||||
func oidcConfigFromApplicationViewModel(app *proj_model.ApplicationView) *management.OIDCConfig {
|
||||
return &management.OIDCConfig{
|
||||
RedirectUris: app.OIDCRedirectUris,
|
||||
ResponseTypes: oidcResponseTypesFromModel(app.OIDCResponseTypes),
|
||||
GrantTypes: oidcGrantTypesFromModel(app.OIDCGrantTypes),
|
||||
ApplicationType: oidcApplicationTypeFromModel(app.OIDCApplicationType),
|
||||
ClientId: app.OIDCClientID,
|
||||
AuthMethodType: oidcAuthMethodTypeFromModel(app.OIDCAuthMethodType),
|
||||
PostLogoutRedirectUris: app.OIDCPostLogoutRedirectUris,
|
||||
Version: oidcVersionFromModel(app.OIDCVersion),
|
||||
NoneCompliant: app.NoneCompliant,
|
||||
ComplianceProblems: complianceProblemsToLocalizedMessages(app.ComplianceProblems),
|
||||
DevMode: app.DevMode,
|
||||
RedirectUris: app.OIDCRedirectUris,
|
||||
ResponseTypes: oidcResponseTypesFromModel(app.OIDCResponseTypes),
|
||||
GrantTypes: oidcGrantTypesFromModel(app.OIDCGrantTypes),
|
||||
ApplicationType: oidcApplicationTypeFromModel(app.OIDCApplicationType),
|
||||
ClientId: app.OIDCClientID,
|
||||
AuthMethodType: oidcAuthMethodTypeFromModel(app.OIDCAuthMethodType),
|
||||
PostLogoutRedirectUris: app.OIDCPostLogoutRedirectUris,
|
||||
Version: oidcVersionFromModel(app.OIDCVersion),
|
||||
NoneCompliant: app.NoneCompliant,
|
||||
ComplianceProblems: complianceProblemsToLocalizedMessages(app.ComplianceProblems),
|
||||
DevMode: app.DevMode,
|
||||
AccessTokenType: oidcTokenTypeFromModel(app.AccessTokenType),
|
||||
AccessTokenRoleAssertion: app.AccessTokenRoleAssertion,
|
||||
IdTokenRoleAssertion: app.IDTokenRoleAssertion,
|
||||
}
|
||||
}
|
||||
|
||||
@ -92,14 +98,17 @@ func oidcAppCreateToModel(app *management.OIDCApplicationCreate) *proj_model.App
|
||||
Name: app.Name,
|
||||
Type: proj_model.AppTypeOIDC,
|
||||
OIDCConfig: &proj_model.OIDCConfig{
|
||||
OIDCVersion: oidcVersionToModel(app.Version),
|
||||
RedirectUris: app.RedirectUris,
|
||||
ResponseTypes: oidcResponseTypesToModel(app.ResponseTypes),
|
||||
GrantTypes: oidcGrantTypesToModel(app.GrantTypes),
|
||||
ApplicationType: oidcApplicationTypeToModel(app.ApplicationType),
|
||||
AuthMethodType: oidcAuthMethodTypeToModel(app.AuthMethodType),
|
||||
PostLogoutRedirectUris: app.PostLogoutRedirectUris,
|
||||
DevMode: app.DevMode,
|
||||
OIDCVersion: oidcVersionToModel(app.Version),
|
||||
RedirectUris: app.RedirectUris,
|
||||
ResponseTypes: oidcResponseTypesToModel(app.ResponseTypes),
|
||||
GrantTypes: oidcGrantTypesToModel(app.GrantTypes),
|
||||
ApplicationType: oidcApplicationTypeToModel(app.ApplicationType),
|
||||
AuthMethodType: oidcAuthMethodTypeToModel(app.AuthMethodType),
|
||||
PostLogoutRedirectUris: app.PostLogoutRedirectUris,
|
||||
DevMode: app.DevMode,
|
||||
AccessTokenType: oidcTokenTypeToModel(app.AccessTokenType),
|
||||
AccessTokenRoleAssertion: app.AccessTokenRoleAssertion,
|
||||
IDTokenRoleAssertion: app.IdTokenRoleAssertion,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -119,14 +128,17 @@ func oidcConfigUpdateToModel(app *management.OIDCConfigUpdate) *proj_model.OIDCC
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: app.ProjectId,
|
||||
},
|
||||
AppID: app.ApplicationId,
|
||||
RedirectUris: app.RedirectUris,
|
||||
ResponseTypes: oidcResponseTypesToModel(app.ResponseTypes),
|
||||
GrantTypes: oidcGrantTypesToModel(app.GrantTypes),
|
||||
ApplicationType: oidcApplicationTypeToModel(app.ApplicationType),
|
||||
AuthMethodType: oidcAuthMethodTypeToModel(app.AuthMethodType),
|
||||
PostLogoutRedirectUris: app.PostLogoutRedirectUris,
|
||||
DevMode: app.DevMode,
|
||||
AppID: app.ApplicationId,
|
||||
RedirectUris: app.RedirectUris,
|
||||
ResponseTypes: oidcResponseTypesToModel(app.ResponseTypes),
|
||||
GrantTypes: oidcGrantTypesToModel(app.GrantTypes),
|
||||
ApplicationType: oidcApplicationTypeToModel(app.ApplicationType),
|
||||
AuthMethodType: oidcAuthMethodTypeToModel(app.AuthMethodType),
|
||||
PostLogoutRedirectUris: app.PostLogoutRedirectUris,
|
||||
DevMode: app.DevMode,
|
||||
AccessTokenType: oidcTokenTypeToModel(app.AccessTokenType),
|
||||
AccessTokenRoleAssertion: app.AccessTokenRoleAssertion,
|
||||
IDTokenRoleAssertion: app.IdTokenRoleAssertion,
|
||||
}
|
||||
}
|
||||
|
||||
@ -351,6 +363,28 @@ func oidcAuthMethodTypeFromModel(authType proj_model.OIDCAuthMethodType) managem
|
||||
}
|
||||
}
|
||||
|
||||
func oidcTokenTypeToModel(tokenType management.OIDCTokenType) proj_model.OIDCTokenType {
|
||||
switch tokenType {
|
||||
case management.OIDCTokenType_OIDCTokenType_Bearer:
|
||||
return proj_model.OIDCTokenTypeBearer
|
||||
case management.OIDCTokenType_OIDCTokenType_JWT:
|
||||
return proj_model.OIDCTokenTypeJWT
|
||||
default:
|
||||
return proj_model.OIDCTokenTypeBearer
|
||||
}
|
||||
}
|
||||
|
||||
func oidcTokenTypeFromModel(tokenType proj_model.OIDCTokenType) management.OIDCTokenType {
|
||||
switch tokenType {
|
||||
case proj_model.OIDCTokenTypeBearer:
|
||||
return management.OIDCTokenType_OIDCTokenType_Bearer
|
||||
case proj_model.OIDCTokenTypeJWT:
|
||||
return management.OIDCTokenType_OIDCTokenType_JWT
|
||||
default:
|
||||
return management.OIDCTokenType_OIDCTokenType_Bearer
|
||||
}
|
||||
}
|
||||
|
||||
func oidcVersionFromModel(version proj_model.OIDCVersion) management.OIDCVersion {
|
||||
switch version {
|
||||
case proj_model.OIDCVersionV1:
|
||||
|
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) CreateProject(ctx context.Context, in *management.ProjectCreateRequest) (*management.Project, error) {
|
||||
project, err := s.project.CreateProject(ctx, in.Name)
|
||||
project, err := s.project.CreateProject(ctx, projectCreateToModel(in))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -22,12 +22,14 @@ func projectFromModel(project *proj_model.Project) *management.Project {
|
||||
logging.Log("GRPC-di7rw").OnError(err).Debug("unable to parse timestamp")
|
||||
|
||||
return &management.Project{
|
||||
Id: project.AggregateID,
|
||||
State: projectStateFromModel(project.State),
|
||||
CreationDate: creationDate,
|
||||
ChangeDate: changeDate,
|
||||
Name: project.Name,
|
||||
Sequence: project.Sequence,
|
||||
Id: project.AggregateID,
|
||||
State: projectStateFromModel(project.State),
|
||||
CreationDate: creationDate,
|
||||
ChangeDate: changeDate,
|
||||
Name: project.Name,
|
||||
Sequence: project.Sequence,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,13 +62,15 @@ func projectViewFromModel(project *proj_model.ProjectView) *management.ProjectVi
|
||||
logging.Log("GRPC-sope3").OnError(err).Debug("unable to parse timestamp")
|
||||
|
||||
return &management.ProjectView{
|
||||
ProjectId: project.ProjectID,
|
||||
State: projectStateFromModel(project.State),
|
||||
CreationDate: creationDate,
|
||||
ChangeDate: changeDate,
|
||||
Name: project.Name,
|
||||
Sequence: project.Sequence,
|
||||
ResourceOwner: project.ResourceOwner,
|
||||
ProjectId: project.ProjectID,
|
||||
State: projectStateFromModel(project.State),
|
||||
CreationDate: creationDate,
|
||||
ChangeDate: changeDate,
|
||||
Name: project.Name,
|
||||
Sequence: project.Sequence,
|
||||
ResourceOwner: project.ResourceOwner,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
}
|
||||
}
|
||||
|
||||
@ -117,12 +121,22 @@ func projectStateFromModel(state proj_model.ProjectState) management.ProjectStat
|
||||
}
|
||||
}
|
||||
|
||||
func projectCreateToModel(project *management.ProjectCreateRequest) *proj_model.Project {
|
||||
return &proj_model.Project{
|
||||
Name: project.Name,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
}
|
||||
}
|
||||
|
||||
func projectUpdateToModel(project *management.ProjectUpdateRequest) *proj_model.Project {
|
||||
return &proj_model.Project{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: project.Id,
|
||||
},
|
||||
Name: project.Name,
|
||||
Name: project.Name,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -135,7 +135,7 @@ func userGrantViewFromModel(grant *grant_model.UserGrantView) *management.UserGr
|
||||
Email: grant.Email,
|
||||
ProjectName: grant.ProjectName,
|
||||
OrgName: grant.OrgName,
|
||||
OrgDomain: grant.OrgDomain,
|
||||
OrgDomain: grant.OrgPrimaryDomain,
|
||||
RoleKeys: grant.RoleKeys,
|
||||
UserId: grant.UserID,
|
||||
ProjectId: grant.ProjectID,
|
||||
|
@ -3,6 +3,7 @@ package oidc
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
@ -11,6 +12,7 @@ import (
|
||||
|
||||
"github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
)
|
||||
|
||||
@ -19,6 +21,14 @@ func (o *OPStorage) CreateAuthRequest(ctx context.Context, req *oidc.AuthRequest
|
||||
if !ok {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sd436", "no user agent id")
|
||||
}
|
||||
app, err := o.repo.ApplicationByClientID(ctx, req.ClientID)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-AEG4d", "Errors.Internal")
|
||||
}
|
||||
req.Scopes, err = o.assertProjectRoleScopes(app, req.Scopes)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-Gqrfg", "Errors.Internal")
|
||||
}
|
||||
authRequest := CreateAuthRequestToBusiness(ctx, req, userAgentID, userID)
|
||||
resp, err := o.repo.CreateAuthRequest(ctx, authRequest)
|
||||
if err != nil {
|
||||
@ -102,3 +112,22 @@ func (o *OPStorage) GetKeySet(ctx context.Context) (*jose.JSONWebKeySet, error)
|
||||
func (o *OPStorage) SaveNewKeyPair(ctx context.Context) error {
|
||||
return o.repo.GenerateSigningKeyPair(ctx, o.signingKeyAlgorithm)
|
||||
}
|
||||
|
||||
func (o *OPStorage) assertProjectRoleScopes(app *proj_model.ApplicationView, scopes []string) ([]string, error) {
|
||||
if !app.ProjectRoleAssertion {
|
||||
return scopes, nil
|
||||
}
|
||||
for _, scope := range scopes {
|
||||
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
|
||||
return scopes, nil
|
||||
}
|
||||
}
|
||||
roles, err := o.repo.ProjectRolesByProjectID(app.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, role := range roles {
|
||||
scopes = append(scopes, ScopeProjectRolePrefix+role.Key)
|
||||
}
|
||||
return scopes, nil
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ package oidc
|
||||
|
||||
import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/text/language"
|
||||
"gopkg.in/square/go-jose.v2"
|
||||
@ -15,6 +16,7 @@ import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -24,6 +26,9 @@ const (
|
||||
scopePhone = "phone"
|
||||
scopeAddress = "address"
|
||||
|
||||
ScopeProjectRolePrefix = "urn:zitadel:iam:org:project:role:"
|
||||
ClaimProjectRoles = "urn:zitadel:iam:org:project:roles"
|
||||
|
||||
oidcCtx = "oidc"
|
||||
)
|
||||
|
||||
@ -35,7 +40,15 @@ func (o *OPStorage) GetClientByClientID(ctx context.Context, id string) (op.Clie
|
||||
if client.State != proj_model.AppStateActive {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "OIDC-sdaGg", "client is not active")
|
||||
}
|
||||
return ClientFromBusiness(client, o.defaultLoginURL, o.defaultAccessTokenLifetime, o.defaultIdTokenLifetime)
|
||||
projectRoles, err := o.repo.ProjectRolesByProjectID(client.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allowedScopes := make([]string, len(projectRoles))
|
||||
for i, role := range projectRoles {
|
||||
allowedScopes[i] = ScopeProjectRolePrefix + role.Key
|
||||
}
|
||||
return ClientFromBusiness(client, o.defaultLoginURL, o.defaultAccessTokenLifetime, o.defaultIdTokenLifetime, allowedScopes)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetKeyByIDAndUserID(ctx context.Context, keyID, userID string) (*jose.JSONWebKey, error) {
|
||||
@ -65,10 +78,10 @@ func (o *OPStorage) AuthorizeClientIDSecret(ctx context.Context, id string, secr
|
||||
return o.repo.AuthorizeOIDCApplication(ctx, id, secret)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject, origin string) (*oidc.Userinfo, error) {
|
||||
token, err := o.repo.TokenByID(ctx, tokenID, subject)
|
||||
func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject, origin string) (oidc.UserInfo, error) {
|
||||
token, err := o.repo.TokenByID(ctx, subject, tokenID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
return nil, errors.ThrowPermissionDenied(nil, "OIDC-Dsfb2", "token is not valid or has expired")
|
||||
}
|
||||
if token.ApplicationID != "" {
|
||||
app, err := o.repo.ApplicationByClientID(ctx, token.ApplicationID)
|
||||
@ -79,65 +92,126 @@ func (o *OPStorage) GetUserinfoFromToken(ctx context.Context, tokenID, subject,
|
||||
return nil, errors.ThrowPermissionDenied(nil, "OIDC-da1f3", "origin is not allowed")
|
||||
}
|
||||
}
|
||||
return o.GetUserinfoFromScopes(ctx, token.UserID, token.Scopes)
|
||||
return o.GetUserinfoFromScopes(ctx, token.UserID, token.ApplicationID, token.Scopes)
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID string, scopes []string) (*oidc.Userinfo, error) {
|
||||
func (o *OPStorage) GetUserinfoFromScopes(ctx context.Context, userID, applicationID string, scopes []string) (oidc.UserInfo, error) {
|
||||
user, err := o.repo.UserByID(ctx, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userInfo := new(oidc.Userinfo)
|
||||
userInfo := oidc.NewUserInfo()
|
||||
roles := make([]string, 0)
|
||||
for _, scope := range scopes {
|
||||
switch scope {
|
||||
case scopeOpenID:
|
||||
userInfo.Subject = user.ID
|
||||
case scopeEmail:
|
||||
case oidc.ScopeOpenID:
|
||||
userInfo.SetSubject(user.ID)
|
||||
case oidc.ScopeEmail:
|
||||
if user.HumanView == nil {
|
||||
continue
|
||||
}
|
||||
userInfo.Email = user.Email
|
||||
userInfo.EmailVerified = user.IsEmailVerified
|
||||
case scopeProfile:
|
||||
userInfo.PreferredUsername = user.PreferredLoginName
|
||||
userInfo.UpdatedAt = user.ChangeDate
|
||||
userInfo.SetEmail(user.Email, user.IsEmailVerified)
|
||||
case oidc.ScopeProfile:
|
||||
userInfo.SetPreferredUsername(user.PreferredLoginName)
|
||||
userInfo.SetUpdatedAt(user.ChangeDate)
|
||||
if user.HumanView != nil {
|
||||
userInfo.Name = user.DisplayName
|
||||
userInfo.FamilyName = user.LastName
|
||||
userInfo.GivenName = user.FirstName
|
||||
userInfo.Nickname = user.NickName
|
||||
userInfo.Gender = oidc.Gender(getGender(user.Gender))
|
||||
userInfo.Locale, err = language.Parse(user.PreferredLanguage)
|
||||
userInfo.SetName(user.DisplayName)
|
||||
userInfo.SetFamilyName(user.LastName)
|
||||
userInfo.SetGivenName(user.FirstName)
|
||||
userInfo.SetNickname(user.NickName)
|
||||
userInfo.SetGender(oidc.Gender(getGender(user.Gender)))
|
||||
locale, _ := language.Parse(user.PreferredLanguage)
|
||||
userInfo.SetLocale(locale)
|
||||
} else {
|
||||
userInfo.Name = user.MachineView.Name
|
||||
userInfo.SetName(user.MachineView.Name)
|
||||
}
|
||||
case scopePhone:
|
||||
case oidc.ScopePhone:
|
||||
if user.HumanView == nil {
|
||||
continue
|
||||
}
|
||||
userInfo.PhoneNumber = user.Phone
|
||||
userInfo.PhoneNumberVerified = user.IsPhoneVerified
|
||||
case scopeAddress:
|
||||
userInfo.SetPhone(user.Phone, user.IsPhoneVerified)
|
||||
case oidc.ScopeAddress:
|
||||
if user.HumanView == nil {
|
||||
continue
|
||||
}
|
||||
if user.StreetAddress == "" && user.Locality == "" && user.Region == "" && user.PostalCode == "" && user.Country == "" {
|
||||
continue
|
||||
}
|
||||
userInfo.Address = &oidc.UserinfoAddress{
|
||||
StreetAddress: user.StreetAddress,
|
||||
Locality: user.Locality,
|
||||
Region: user.Region,
|
||||
PostalCode: user.PostalCode,
|
||||
Country: user.Country,
|
||||
}
|
||||
userInfo.SetAddress(oidc.NewUserInfoAddress(user.StreetAddress, user.Locality, user.Region, user.PostalCode, user.Country, ""))
|
||||
default:
|
||||
userInfo.Authorizations = append(userInfo.Authorizations, scope)
|
||||
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
|
||||
roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(roles) == 0 || applicationID == "" {
|
||||
return userInfo, nil
|
||||
}
|
||||
projectRoles, err := o.assertRoles(ctx, userID, applicationID, roles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(projectRoles) > 0 {
|
||||
userInfo.AppendClaims(ClaimProjectRoles, projectRoles)
|
||||
}
|
||||
|
||||
return userInfo, nil
|
||||
}
|
||||
|
||||
func (o *OPStorage) GetPrivateClaimsFromScopes(ctx context.Context, userID, applicationID string, scopes []string) (claims map[string]interface{}, err error) {
|
||||
roles := make([]string, 0)
|
||||
for _, scope := range scopes {
|
||||
if strings.HasPrefix(scope, ScopeProjectRolePrefix) {
|
||||
roles = append(roles, strings.TrimPrefix(scope, ScopeProjectRolePrefix))
|
||||
}
|
||||
}
|
||||
if len(roles) == 0 || applicationID == "" {
|
||||
return nil, nil
|
||||
}
|
||||
projectRoles, err := o.assertRoles(ctx, userID, applicationID, roles)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(projectRoles) > 0 {
|
||||
claims = map[string]interface{}{ClaimProjectRoles: projectRoles}
|
||||
}
|
||||
return claims, err
|
||||
}
|
||||
|
||||
func (o *OPStorage) assertRoles(ctx context.Context, userID, applicationID string, requestedRoles []string) (map[string]map[string]string, error) {
|
||||
app, err := o.repo.ApplicationByClientID(ctx, applicationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grants, err := o.repo.UserGrantsByProjectAndUserID(app.ProjectID, userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
projectRoles := make(map[string]map[string]string)
|
||||
for _, requestedRole := range requestedRoles {
|
||||
for _, grant := range grants {
|
||||
checkGrantedRoles(projectRoles, grant, requestedRole)
|
||||
}
|
||||
}
|
||||
return projectRoles, nil
|
||||
}
|
||||
|
||||
func checkGrantedRoles(roles map[string]map[string]string, grant *grant_model.UserGrantView, requestedRole string) {
|
||||
for _, grantedRole := range grant.RoleKeys {
|
||||
if requestedRole == grantedRole {
|
||||
appendRole(roles, grantedRole, grant.ResourceOwner, grant.OrgPrimaryDomain)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func appendRole(roles map[string]map[string]string, role, orgID, orgPrimaryDomain string) {
|
||||
if roles[role] == nil {
|
||||
roles[role] = make(map[string]string, 0)
|
||||
}
|
||||
roles[role][orgID] = orgPrimaryDomain
|
||||
}
|
||||
|
||||
func getGender(gender user_model.Gender) string {
|
||||
switch gender {
|
||||
case user_model.GenderFemale:
|
||||
|
@ -1,9 +1,9 @@
|
||||
package oidc
|
||||
|
||||
import (
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"time"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/op"
|
||||
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
@ -15,13 +15,20 @@ type Client struct {
|
||||
defaultLoginURL string
|
||||
defaultAccessTokenLifetime time.Duration
|
||||
defaultIdTokenLifetime time.Duration
|
||||
allowedScopes []string
|
||||
}
|
||||
|
||||
func ClientFromBusiness(app *model.ApplicationView, defaultLoginURL string, defaultAccessTokenLifetime, defaultIdTokenLifetime time.Duration) (op.Client, error) {
|
||||
func ClientFromBusiness(app *model.ApplicationView, defaultLoginURL string, defaultAccessTokenLifetime, defaultIdTokenLifetime time.Duration, allowedScopes []string) (op.Client, error) {
|
||||
if !app.IsOIDC {
|
||||
return nil, errors.ThrowInvalidArgument(nil, "OIDC-d5bhD", "client is not a proper oidc application")
|
||||
}
|
||||
return &Client{ApplicationView: app, defaultLoginURL: defaultLoginURL, defaultAccessTokenLifetime: defaultAccessTokenLifetime, defaultIdTokenLifetime: defaultIdTokenLifetime}, nil
|
||||
return &Client{
|
||||
ApplicationView: app,
|
||||
defaultLoginURL: defaultLoginURL,
|
||||
defaultAccessTokenLifetime: defaultAccessTokenLifetime,
|
||||
defaultIdTokenLifetime: defaultIdTokenLifetime,
|
||||
allowedScopes: allowedScopes},
|
||||
nil
|
||||
}
|
||||
|
||||
func (c *Client) ApplicationType() op.ApplicationType {
|
||||
@ -56,6 +63,18 @@ func (c *Client) DevMode() bool {
|
||||
return c.ApplicationView.DevMode
|
||||
}
|
||||
|
||||
func (c *Client) AllowedScopes() []string {
|
||||
return c.allowedScopes
|
||||
}
|
||||
|
||||
func (c *Client) AssertAdditionalIdTokenScopes() bool {
|
||||
return c.IDTokenRoleAssertion
|
||||
}
|
||||
|
||||
func (c *Client) AssertAdditionalAccessTokenScopes() bool {
|
||||
return c.AccessTokenRoleAssertion
|
||||
}
|
||||
|
||||
func (c *Client) AccessTokenLifetime() time.Duration {
|
||||
return c.defaultAccessTokenLifetime //PLANNED: impl from real client
|
||||
}
|
||||
@ -65,7 +84,18 @@ func (c *Client) IDTokenLifetime() time.Duration {
|
||||
}
|
||||
|
||||
func (c *Client) AccessTokenType() op.AccessTokenType {
|
||||
return op.AccessTokenTypeBearer //PLANNED: impl from real client
|
||||
return accessTokenTypeToOIDC(c.ApplicationView.AccessTokenType)
|
||||
}
|
||||
|
||||
func accessTokenTypeToOIDC(tokenType model.OIDCTokenType) op.AccessTokenType {
|
||||
switch tokenType {
|
||||
case model.OIDCTokenTypeBearer:
|
||||
return op.AccessTokenTypeBearer
|
||||
case model.OIDCTokenTypeJWT:
|
||||
return op.AccessTokenTypeJWT
|
||||
default:
|
||||
return op.AccessTokenTypeBearer
|
||||
}
|
||||
}
|
||||
|
||||
func authMethodToOIDC(authType model.OIDCAuthMethodType) op.AuthMethod {
|
||||
|
@ -2,28 +2,30 @@ package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/eventstore/sdk"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
cache "github.com/caos/zitadel/internal/auth_request/repository"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/sdk"
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
iam_es_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
iam_view_model "github.com/caos/zitadel/internal/iam/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/id"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
|
||||
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
project_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||
es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
user_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
grant_view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
)
|
||||
|
||||
type AuthRequestRepo struct {
|
||||
@ -38,6 +40,7 @@ type AuthRequestRepo struct {
|
||||
OrgViewProvider orgViewProvider
|
||||
LoginPolicyViewProvider loginPolicyViewProvider
|
||||
IDPProviderViewProvider idpProviderViewProvider
|
||||
UserGrantProvider userGrantProvider
|
||||
|
||||
IdGenerator id.Generator
|
||||
|
||||
@ -76,6 +79,11 @@ type orgViewProvider interface {
|
||||
OrgByPrimaryDomain(string) (*org_view_model.OrgView, error)
|
||||
}
|
||||
|
||||
type userGrantProvider interface {
|
||||
ApplicationByClientID(context.Context, string) (*project_view_model.ApplicationView, error)
|
||||
UserGrantsByProjectAndUserID(string, string) ([]*grant_view_model.UserGrantView, error)
|
||||
}
|
||||
|
||||
func (repo *AuthRequestRepo) Health(ctx context.Context) error {
|
||||
if err := repo.UserEvents.Health(ctx); err != nil {
|
||||
return err
|
||||
@ -89,14 +97,18 @@ func (repo *AuthRequestRepo) CreateAuthRequest(ctx context.Context, request *mod
|
||||
return nil, err
|
||||
}
|
||||
request.ID = reqID
|
||||
ids, err := repo.View.AppIDsFromProjectByClientID(ctx, request.ApplicationID)
|
||||
app, err := repo.View.ApplicationByClientID(ctx, request.ApplicationID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Audience = ids
|
||||
appIDs, err := repo.View.AppIDsFromProjectID(ctx, app.ProjectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
request.Audience = appIDs
|
||||
if request.LoginHint != "" {
|
||||
err = repo.checkLoginName(ctx, request, request.LoginHint)
|
||||
logging.LogWithFields("EVENT-aG311", "login name", request.LoginHint, "id", request.ID, "applicationID", request.ApplicationID).Debug("login hint invalid")
|
||||
logging.LogWithFields("EVENT-aG311", "login name", request.LoginHint, "id", request.ID, "applicationID", request.ApplicationID).OnError(err).Debug("login hint invalid")
|
||||
}
|
||||
err = repo.AuthRequests.SaveAuthRequest(ctx, request)
|
||||
if err != nil {
|
||||
@ -541,6 +553,15 @@ func (repo *AuthRequestRepo) nextSteps(ctx context.Context, request *model.AuthR
|
||||
|
||||
}
|
||||
//PLANNED: consent step
|
||||
|
||||
missing, err := userGrantRequired(ctx, request, user, repo.UserGrantProvider)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if missing {
|
||||
return append(steps, &model.GrantRequiredStep{}), nil
|
||||
}
|
||||
|
||||
return append(steps, &model.RedirectToCallbackStep{}), nil
|
||||
}
|
||||
|
||||
@ -780,3 +801,23 @@ func linkingIDPConfigExistingInAllowedIDPs(linkingUsers []*model.ExternalUser, i
|
||||
}
|
||||
return true
|
||||
}
|
||||
func userGrantRequired(ctx context.Context, request *model.AuthRequest, user *user_model.UserView, userGrantProvider userGrantProvider) (_ bool, err error) {
|
||||
var app *project_view_model.ApplicationView
|
||||
switch request.Request.Type() {
|
||||
case model.AuthRequestTypeOIDC:
|
||||
app, err = userGrantProvider.ApplicationByClientID(ctx, request.ApplicationID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
default:
|
||||
return false, errors.ThrowPreconditionFailed(nil, "EVENT-dfrw2", "Errors.AuthRequest.RequestTypeNotSupported")
|
||||
}
|
||||
if !app.ProjectRoleCheck {
|
||||
return false, nil
|
||||
}
|
||||
grants, err := userGrantProvider.UserGrantsByProjectAndUserID(app.ProjectID, user.ID)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return len(grants) == 0, nil
|
||||
}
|
||||
|
@ -15,10 +15,12 @@ import (
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
|
||||
proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
user_model "github.com/caos/zitadel/internal/user/model"
|
||||
user_event "github.com/caos/zitadel/internal/user/repository/eventsourcing"
|
||||
user_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
user_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
grant_view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
)
|
||||
|
||||
type mockViewNoUserSession struct{}
|
||||
@ -157,6 +159,23 @@ func (m *mockViewErrOrg) OrgByPrimaryDomain(string) (*org_view_model.OrgView, er
|
||||
return nil, errors.ThrowInternal(nil, "id", "internal error")
|
||||
}
|
||||
|
||||
type mockUserGrants struct {
|
||||
roleCheck bool
|
||||
userGrants int
|
||||
}
|
||||
|
||||
func (m *mockUserGrants) ApplicationByClientID(ctx context.Context, s string) (*proj_view_model.ApplicationView, error) {
|
||||
return &proj_view_model.ApplicationView{ProjectRoleCheck: m.roleCheck}, nil
|
||||
}
|
||||
|
||||
func (m *mockUserGrants) UserGrantsByProjectAndUserID(s string, s2 string) ([]*grant_view_model.UserGrantView, error) {
|
||||
var grants []*grant_view_model.UserGrantView
|
||||
if m.userGrants > 0 {
|
||||
grants = make([]*grant_view_model.UserGrantView, m.userGrants)
|
||||
}
|
||||
return grants, nil
|
||||
}
|
||||
|
||||
func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
type fields struct {
|
||||
UserEvents *user_event.UserEventstore
|
||||
@ -166,6 +185,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
userViewProvider userViewProvider
|
||||
userEventProvider userEventProvider
|
||||
orgViewProvider orgViewProvider
|
||||
userGrantProvider userGrantProvider
|
||||
PasswordCheckLifeTime time.Duration
|
||||
ExternalLoginCheckLifeTime time.Duration
|
||||
MfaInitSkippedLifeTime time.Duration
|
||||
@ -424,10 +444,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
|
||||
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{&model.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID"}, false},
|
||||
args{&model.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID", Request: &model.AuthRequestOIDC{}}, false},
|
||||
[]model.NextStep{&model.RedirectToCallbackStep{}},
|
||||
nil,
|
||||
},
|
||||
@ -460,10 +481,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||
ExternalLoginCheckLifeTime: 10 * 24 * time.Hour,
|
||||
},
|
||||
args{&model.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID"}, false},
|
||||
args{&model.AuthRequest{UserID: "UserID", SelectedIDPConfigID: "IDPConfigID", Request: &model.AuthRequestOIDC{}}, false},
|
||||
[]model.NextStep{&model.RedirectToCallbackStep{}},
|
||||
nil,
|
||||
},
|
||||
@ -590,10 +612,11 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{&model.AuthRequest{UserID: "UserID"}, false},
|
||||
args{&model.AuthRequest{UserID: "UserID", Request: &model.AuthRequestOIDC{}}, false},
|
||||
[]model.NextStep{&model.RedirectToCallbackStep{}},
|
||||
nil,
|
||||
},
|
||||
@ -611,10 +634,61 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{},
|
||||
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{&model.AuthRequest{UserID: "UserID", Prompt: model.PromptNone}, true},
|
||||
args{&model.AuthRequest{UserID: "UserID", Prompt: model.PromptNone, Request: &model.AuthRequestOIDC{}}, true},
|
||||
[]model.NextStep{&model.RedirectToCallbackStep{}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"prompt none, checkLoggedIn true, authenticated and required user grants missing, grant required step",
|
||||
fields{
|
||||
userSessionViewProvider: &mockViewUserSession{
|
||||
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||
},
|
||||
userViewProvider: &mockViewUser{
|
||||
PasswordSet: true,
|
||||
IsEmailVerified: true,
|
||||
MfaMaxSetUp: int32(model.MfaLevelSoftware),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{
|
||||
roleCheck: true,
|
||||
userGrants: 0,
|
||||
},
|
||||
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{&model.AuthRequest{UserID: "UserID", Prompt: model.PromptNone, Request: &model.AuthRequestOIDC{}}, true},
|
||||
[]model.NextStep{&model.GrantRequiredStep{}},
|
||||
nil,
|
||||
},
|
||||
{
|
||||
"prompt none, checkLoggedIn true, authenticated and required user grants exist, redirect to callback step",
|
||||
fields{
|
||||
userSessionViewProvider: &mockViewUserSession{
|
||||
PasswordVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||
MfaSoftwareVerification: time.Now().UTC().Add(-5 * time.Minute),
|
||||
},
|
||||
userViewProvider: &mockViewUser{
|
||||
PasswordSet: true,
|
||||
IsEmailVerified: true,
|
||||
MfaMaxSetUp: int32(model.MfaLevelSoftware),
|
||||
},
|
||||
userEventProvider: &mockEventUser{},
|
||||
orgViewProvider: &mockViewOrg{State: org_model.OrgStateActive},
|
||||
userGrantProvider: &mockUserGrants{
|
||||
roleCheck: true,
|
||||
userGrants: 2,
|
||||
},
|
||||
PasswordCheckLifeTime: 10 * 24 * time.Hour,
|
||||
MfaSoftwareCheckLifeTime: 18 * time.Hour,
|
||||
},
|
||||
args{&model.AuthRequest{UserID: "UserID", Prompt: model.PromptNone, Request: &model.AuthRequestOIDC{}}, true},
|
||||
[]model.NextStep{&model.RedirectToCallbackStep{}},
|
||||
nil,
|
||||
},
|
||||
@ -679,6 +753,7 @@ func TestAuthRequestRepo_nextSteps(t *testing.T) {
|
||||
UserViewProvider: tt.fields.userViewProvider,
|
||||
UserEventProvider: tt.fields.userEventProvider,
|
||||
OrgViewProvider: tt.fields.orgViewProvider,
|
||||
UserGrantProvider: tt.fields.userGrantProvider,
|
||||
PasswordCheckLifeTime: tt.fields.PasswordCheckLifeTime,
|
||||
ExternalLoginCheckLifeTime: tt.fields.ExternalLoginCheckLifeTime,
|
||||
MfaInitSkippedLifeTime: tt.fields.MfaInitSkippedLifeTime,
|
||||
|
21
internal/auth/repository/eventsourcing/eventstore/project.go
Normal file
21
internal/auth/repository/eventsourcing/eventstore/project.go
Normal file
@ -0,0 +1,21 @@
|
||||
package eventstore
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/auth/repository/eventsourcing/view"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
proj_view_model "github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
)
|
||||
|
||||
type ProjectRepo struct {
|
||||
View *view.View
|
||||
ProjectEvents *proj_event.ProjectEventstore
|
||||
}
|
||||
|
||||
func (a *ApplicationRepo) ProjectRolesByProjectID(projectID string) ([]*model.ProjectRoleView, error) {
|
||||
roles, err := a.View.ProjectRolesByProjectID(projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return proj_view_model.ProjectRolesToModel(roles), nil
|
||||
}
|
@ -52,11 +52,27 @@ func (p *Application) Reduce(event *models.Event) (err error) {
|
||||
}
|
||||
err = app.AppendEvent(event)
|
||||
case es_model.ApplicationRemoved:
|
||||
err := app.SetData(event)
|
||||
err = app.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.view.DeleteApplication(app.ID, event.Sequence)
|
||||
case es_model.ProjectChanged:
|
||||
apps, err := p.view.ApplicationsByProjectID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
return p.view.ProcessedApplicationSequence(event.Sequence)
|
||||
}
|
||||
for _, app := range apps {
|
||||
if err := app.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return p.view.PutApplications(apps, event.Sequence)
|
||||
case es_model.ProjectRemoved:
|
||||
return p.view.DeleteApplicationsByProjectID(event.AggregateID)
|
||||
default:
|
||||
return p.view.ProcessedApplicationSequence(event.Sequence)
|
||||
}
|
||||
|
@ -60,6 +60,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, ev
|
||||
&ExternalIDP{handler: handler{view, bulkLimit, configs.cycleDuration("ExternalIDP"), errorCount}, systemDefaults: systemDefaults, orgEvents: repos.OrgEvents, iamEvents: repos.IamEvents},
|
||||
&PasswordComplexityPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("PasswordComplexityPolicy"), errorCount}},
|
||||
&OrgIAMPolicy{handler: handler{view, bulkLimit, configs.cycleDuration("OrgIAMPolicy"), errorCount}},
|
||||
&ProjectRole{handler: handler{view, bulkLimit, configs.cycleDuration("ProjectRole"), errorCount}, projectEvents: repos.ProjectEvents},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,70 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/spooler"
|
||||
"github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
|
||||
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
|
||||
view_model "github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
)
|
||||
|
||||
type ProjectRole struct {
|
||||
handler
|
||||
projectEvents *proj_event.ProjectEventstore
|
||||
}
|
||||
|
||||
const (
|
||||
projectRoleTable = "auth.project_roles"
|
||||
)
|
||||
|
||||
func (p *ProjectRole) ViewModel() string {
|
||||
return projectRoleTable
|
||||
}
|
||||
|
||||
func (p *ProjectRole) EventQuery() (*models.SearchQuery, error) {
|
||||
sequence, err := p.view.GetLatestProjectRoleSequence()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return eventsourcing.ProjectQuery(sequence.CurrentSequence), nil
|
||||
}
|
||||
|
||||
func (p *ProjectRole) Reduce(event *models.Event) (err error) {
|
||||
role := new(view_model.ProjectRoleView)
|
||||
switch event.Type {
|
||||
case es_model.ProjectRoleAdded:
|
||||
err = role.AppendEvent(event)
|
||||
case es_model.ProjectRoleChanged:
|
||||
err = role.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
role, err = p.view.ProjectRoleByIDs(event.AggregateID, event.ResourceOwner, role.Key)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = role.AppendEvent(event)
|
||||
case es_model.ProjectRoleRemoved:
|
||||
err = role.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.view.DeleteProjectRole(event.AggregateID, event.ResourceOwner, role.Key, event.Sequence)
|
||||
case es_model.ProjectRemoved:
|
||||
return p.view.DeleteProjectRolesByProjectID(event.AggregateID)
|
||||
default:
|
||||
return p.view.ProcessedProjectRoleSequence(event.Sequence)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.view.PutProjectRole(role)
|
||||
}
|
||||
|
||||
func (p *ProjectRole) OnError(event *models.Event, err error) error {
|
||||
logging.LogWithFields("SPOOL-lso9w", "id", event.AggregateID).WithError(err).Warn("something went wrong in project role handler")
|
||||
return spooler.HandleError(event, err, p.view.GetLatestProjectRoleFailedEvent, p.view.ProcessedProjectRoleFailedEvent, p.view.ProcessedProjectRoleSequence, p.errorCountUntilSkip)
|
||||
}
|
@ -354,6 +354,12 @@ func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *pr
|
||||
|
||||
func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) {
|
||||
grant.OrgName = org.Name
|
||||
for _, domain := range org.Domains {
|
||||
if domain.Primary {
|
||||
grant.OrgPrimaryDomain = domain.Domain
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) OnError(event *models.Event, err error) error {
|
||||
|
@ -107,7 +107,7 @@ func (m *UserMembership) processOrg(event *models.Event) (err error) {
|
||||
case org_es_model.OrgMemberRemoved:
|
||||
return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event.Sequence)
|
||||
case org_es_model.OrgChanged:
|
||||
err = m.updateOrgName(event)
|
||||
return m.updateOrgName(event)
|
||||
default:
|
||||
return m.view.ProcessedUserMembershipSequence(event.Sequence)
|
||||
}
|
||||
@ -178,7 +178,7 @@ func (m *UserMembership) processProject(event *models.Event) (err error) {
|
||||
case proj_es_model.ProjectGrantMemberRemoved:
|
||||
return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event.Sequence)
|
||||
case proj_es_model.ProjectChanged:
|
||||
err = m.updateProjectDisplayName(event)
|
||||
return m.updateProjectDisplayName(event)
|
||||
case proj_es_model.ProjectRemoved:
|
||||
return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event.Sequence)
|
||||
case proj_es_model.ProjectGrantRemoved:
|
||||
@ -227,6 +227,6 @@ func (m *UserMembership) processUser(event *models.Event) (err error) {
|
||||
}
|
||||
|
||||
func (m *UserMembership) OnError(event *models.Event, err error) error {
|
||||
logging.LogWithFields("SPOOL-Ms3fj", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgmember handler")
|
||||
logging.LogWithFields("SPOOL-Ms3fj", "id", event.AggregateID).WithError(err).Warn("something went wrong in user membership handler")
|
||||
return spooler.HandleError(event, err, m.view.GetLatestUserMembershipFailedEvent, m.view.ProcessedUserMembershipFailedEvent, m.view.ProcessedUserMembershipSequence, m.errorCountUntilSkip)
|
||||
}
|
||||
|
@ -135,6 +135,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, au
|
||||
OrgViewProvider: view,
|
||||
IDPProviderViewProvider: view,
|
||||
LoginPolicyViewProvider: view,
|
||||
UserGrantProvider: view,
|
||||
IdGenerator: idGenerator,
|
||||
PasswordCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
|
||||
ExternalLoginCheckLifeTime: systemDefaults.VerificationLifetimes.PasswordCheck.Duration,
|
||||
@ -156,6 +157,7 @@ func Start(conf Config, authZ authz.Config, systemDefaults sd.SystemDefaults, au
|
||||
View: view,
|
||||
ProjectEvents: project,
|
||||
},
|
||||
|
||||
eventstore.UserSessionRepo{
|
||||
View: view,
|
||||
},
|
||||
|
@ -18,16 +18,28 @@ func (v *View) ApplicationByID(projectID, appID string) (*model.ApplicationView,
|
||||
return view.ApplicationByID(v.Db, applicationTable, projectID, appID)
|
||||
}
|
||||
|
||||
func (v *View) ApplicationsByProjectID(projectID string) ([]*model.ApplicationView, error) {
|
||||
return view.ApplicationsByProjectID(v.Db, applicationTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, uint64, error) {
|
||||
return view.SearchApplications(v.Db, applicationTable, request)
|
||||
}
|
||||
|
||||
func (v *View) PutApplication(project *model.ApplicationView) error {
|
||||
err := view.PutApplication(v.Db, applicationTable, project)
|
||||
func (v *View) PutApplication(app *model.ApplicationView) error {
|
||||
err := view.PutApplication(v.Db, applicationTable, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedApplicationSequence(project.Sequence)
|
||||
return v.ProcessedApplicationSequence(app.Sequence)
|
||||
}
|
||||
|
||||
func (v *View) PutApplications(apps []*model.ApplicationView, sequence uint64) error {
|
||||
err := view.PutApplications(v.Db, applicationTable, apps...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedApplicationSequence(sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteApplication(appID string, eventSequence uint64) error {
|
||||
@ -38,6 +50,10 @@ func (v *View) DeleteApplication(appID string, eventSequence uint64) error {
|
||||
return v.ProcessedApplicationSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteApplicationsByProjectID(projectID string) error {
|
||||
return view.DeleteApplicationsByProjectID(v.Db, applicationTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) {
|
||||
return v.latestSequence(applicationTable)
|
||||
}
|
||||
@ -55,24 +71,7 @@ func (v *View) ProcessedApplicationFailedEvent(failedEvent *repository.FailedEve
|
||||
}
|
||||
|
||||
func (v *View) ApplicationByClientID(_ context.Context, clientID string) (*model.ApplicationView, error) {
|
||||
req := &proj_model.ApplicationSearchRequest{
|
||||
Limit: 1,
|
||||
Queries: []*proj_model.ApplicationSearchQuery{
|
||||
{
|
||||
Key: proj_model.AppSearchKeyOIDCClientID,
|
||||
Method: global_model.SearchMethodEquals,
|
||||
Value: clientID,
|
||||
},
|
||||
},
|
||||
}
|
||||
apps, count, err := view.SearchApplications(v.Db, applicationTable, req)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(err, "VIEW-sd6JQ", "cannot find client")
|
||||
}
|
||||
if count != 1 {
|
||||
return nil, errors.ThrowPreconditionFailed(nil, "VIEW-dfw3as", "cannot find client")
|
||||
}
|
||||
return apps[0], nil
|
||||
return view.ApplicationByOIDCClientID(v.Db, applicationTable, clientID)
|
||||
}
|
||||
|
||||
func (v *View) AppIDsFromProjectByClientID(ctx context.Context, clientID string) ([]string, error) {
|
||||
@ -102,3 +101,27 @@ func (v *View) AppIDsFromProjectByClientID(ctx context.Context, clientID string)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
||||
func (v *View) AppIDsFromProjectID(ctx context.Context, projectID string) ([]string, error) {
|
||||
req := &proj_model.ApplicationSearchRequest{
|
||||
Queries: []*proj_model.ApplicationSearchQuery{
|
||||
{
|
||||
Key: proj_model.AppSearchKeyProjectID,
|
||||
Method: global_model.SearchMethodEquals,
|
||||
Value: projectID,
|
||||
},
|
||||
},
|
||||
}
|
||||
apps, _, err := view.SearchApplications(v.Db, applicationTable, req)
|
||||
if err != nil {
|
||||
return nil, errors.ThrowPreconditionFailed(err, "VIEW-Gd24q", "cannot find applications")
|
||||
}
|
||||
ids := make([]string, 0, len(apps))
|
||||
for _, app := range apps {
|
||||
if !app.IsOIDC {
|
||||
continue
|
||||
}
|
||||
ids = append(ids, app.OIDCClientID)
|
||||
}
|
||||
return ids, nil
|
||||
}
|
||||
|
68
internal/auth/repository/eventsourcing/view/project_role.go
Normal file
68
internal/auth/repository/eventsourcing/view/project_role.go
Normal file
@ -0,0 +1,68 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
"github.com/caos/zitadel/internal/project/repository/view"
|
||||
"github.com/caos/zitadel/internal/project/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/view/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
projectRoleTable = "auth.project_roles"
|
||||
)
|
||||
|
||||
func (v *View) ProjectRoleByIDs(projectID, orgID, key string) (*model.ProjectRoleView, error) {
|
||||
return view.ProjectRoleByIDs(v.Db, projectRoleTable, projectID, orgID, key)
|
||||
}
|
||||
|
||||
func (v *View) ProjectRolesByProjectID(projectID string) ([]*model.ProjectRoleView, error) {
|
||||
return view.ProjectRolesByProjectID(v.Db, projectRoleTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) ResourceOwnerProjectRolesByKey(projectID, resourceowner, key string) ([]*model.ProjectRoleView, error) {
|
||||
return view.ResourceOwnerProjectRolesByKey(v.Db, projectRoleTable, projectID, resourceowner, key)
|
||||
}
|
||||
|
||||
func (v *View) ResourceOwnerProjectRoles(projectID, resourceowner string) ([]*model.ProjectRoleView, error) {
|
||||
return view.ResourceOwnerProjectRoles(v.Db, projectRoleTable, projectID, resourceowner)
|
||||
}
|
||||
|
||||
func (v *View) SearchProjectRoles(request *proj_model.ProjectRoleSearchRequest) ([]*model.ProjectRoleView, uint64, error) {
|
||||
return view.SearchProjectRoles(v.Db, projectRoleTable, request)
|
||||
}
|
||||
|
||||
func (v *View) PutProjectRole(project *model.ProjectRoleView) error {
|
||||
err := view.PutProjectRole(v.Db, projectRoleTable, project)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedProjectRoleSequence(project.Sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteProjectRole(projectID, orgID, key string, eventSequence uint64) error {
|
||||
err := view.DeleteProjectRole(v.Db, projectRoleTable, projectID, orgID, key)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
return v.ProcessedProjectRoleSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteProjectRolesByProjectID(projectID string) error {
|
||||
return view.DeleteProjectRolesByProjectID(v.Db, projectRoleTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestProjectRoleSequence() (*repository.CurrentSequence, error) {
|
||||
return v.latestSequence(projectRoleTable)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedProjectRoleSequence(eventSequence uint64) error {
|
||||
return v.saveCurrentSequence(projectRoleTable, eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestProjectRoleFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
|
||||
return v.latestFailedEvent(projectRoleTable, sequence)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedProjectRoleFailedEvent(failedEvent *repository.FailedEvent) error {
|
||||
return v.saveFailedEvent(failedEvent)
|
||||
}
|
@ -36,7 +36,7 @@ func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence
|
||||
}
|
||||
|
||||
func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64) error {
|
||||
err := view.PutUserMemberships(v.Db, userTable, memberships...)
|
||||
err := view.PutUserMemberships(v.Db, userMembershipTable, memberships...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
9
internal/auth/repository/project.go
Normal file
9
internal/auth/repository/project.go
Normal file
@ -0,0 +1,9 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
|
||||
type ProjectRepository interface {
|
||||
ProjectRolesByProjectID(projectID string) ([]*model.ProjectRoleView, error)
|
||||
}
|
@ -10,6 +10,7 @@ type Repository interface {
|
||||
AuthRequestRepository
|
||||
TokenRepository
|
||||
ApplicationRepository
|
||||
ProjectRepository
|
||||
KeyRepository
|
||||
UserSessionRepository
|
||||
UserGrantRepository
|
||||
|
@ -22,6 +22,7 @@ const (
|
||||
NextStepLinkUsers
|
||||
NextStepExternalNotFoundOption
|
||||
NextStepExternalLogin
|
||||
NextStepGrantRequired
|
||||
)
|
||||
|
||||
type UserSessionState int32
|
||||
@ -127,6 +128,12 @@ func (s *LinkUsersStep) Type() NextStepType {
|
||||
return NextStepLinkUsers
|
||||
}
|
||||
|
||||
type GrantRequiredStep struct{}
|
||||
|
||||
func (s *GrantRequiredStep) Type() NextStepType {
|
||||
return NextStepGrantRequired
|
||||
}
|
||||
|
||||
type RedirectToCallbackStep struct{}
|
||||
|
||||
func (s *RedirectToCallbackStep) Type() NextStepType {
|
||||
|
@ -68,13 +68,12 @@ func (repo *ProjectRepo) ProjectByID(ctx context.Context, id string) (*proj_mode
|
||||
return model.ProjectToModel(project), nil
|
||||
}
|
||||
|
||||
func (repo *ProjectRepo) CreateProject(ctx context.Context, name string) (*proj_model.Project, error) {
|
||||
func (repo *ProjectRepo) CreateProject(ctx context.Context, project *proj_model.Project) (*proj_model.Project, error) {
|
||||
ctxData := authz.GetCtxData(ctx)
|
||||
iam, err := repo.IAMEvents.IAMByID(ctx, repo.IAMID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
project := &proj_model.Project{Name: name}
|
||||
return repo.ProjectEvents.CreateProject(ctx, project, iam.GlobalOrgID == ctxData.OrgID)
|
||||
}
|
||||
|
||||
|
@ -57,6 +57,20 @@ func (p *Application) Reduce(event *models.Event) (err error) {
|
||||
return err
|
||||
}
|
||||
return p.view.DeleteApplication(app.ID, event.Sequence)
|
||||
case es_model.ProjectChanged:
|
||||
apps, err := p.view.ApplicationsByProjectID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(apps) == 0 {
|
||||
return p.view.ProcessedApplicationSequence(event.Sequence)
|
||||
}
|
||||
for _, app := range apps {
|
||||
if err := app.AppendEvent(event); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return p.view.PutApplications(apps, event.Sequence)
|
||||
case es_model.ProjectRemoved:
|
||||
return p.view.DeleteApplicationsByProjectID(event.AggregateID)
|
||||
default:
|
||||
|
@ -180,6 +180,12 @@ func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *pr
|
||||
|
||||
func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) {
|
||||
grant.OrgName = org.Name
|
||||
for _, domain := range org.Domains {
|
||||
if domain.Primary {
|
||||
grant.OrgPrimaryDomain = domain.Domain
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) OnError(event *models.Event, err error) error {
|
||||
|
@ -105,7 +105,7 @@ func (m *UserMembership) processOrg(event *models.Event) (err error) {
|
||||
case org_es_model.OrgMemberRemoved:
|
||||
return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation, event.Sequence)
|
||||
case org_es_model.OrgChanged:
|
||||
err = m.updateOrgDisplayName(event)
|
||||
return m.updateOrgDisplayName(event)
|
||||
default:
|
||||
return m.view.ProcessedUserMembershipSequence(event.Sequence)
|
||||
}
|
||||
@ -166,7 +166,7 @@ func (m *UserMembership) processProject(event *models.Event) (err error) {
|
||||
case proj_es_model.ProjectGrantMemberRemoved:
|
||||
return m.view.DeleteUserMembership(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant, event.Sequence)
|
||||
case proj_es_model.ProjectChanged:
|
||||
err = m.updateProjectDisplayName(event)
|
||||
return m.updateProjectDisplayName(event)
|
||||
case proj_es_model.ProjectRemoved:
|
||||
return m.view.DeleteUserMembershipsByAggregateID(event.AggregateID, event.Sequence)
|
||||
case proj_es_model.ProjectGrantRemoved:
|
||||
@ -215,6 +215,6 @@ func (m *UserMembership) processUser(event *models.Event) (err error) {
|
||||
}
|
||||
|
||||
func (m *UserMembership) OnError(event *models.Event, err error) error {
|
||||
logging.LogWithFields("SPOOL-Ms3fj", "id", event.AggregateID).WithError(err).Warn("something went wrong in orgmember handler")
|
||||
logging.LogWithFields("SPOOL-Fwer2", "id", event.AggregateID).WithError(err).Warn("something went wrong in user membership handler")
|
||||
return spooler.HandleError(event, err, m.view.GetLatestUserMembershipFailedEvent, m.view.ProcessedUserMembershipFailedEvent, m.view.ProcessedUserMembershipSequence, m.errorCountUntilSkip)
|
||||
}
|
||||
|
@ -15,20 +15,28 @@ func (v *View) ApplicationByID(projectID, appID string) (*model.ApplicationView,
|
||||
return view.ApplicationByID(v.Db, applicationTable, projectID, appID)
|
||||
}
|
||||
|
||||
func (v *View) ApplicationsByProjectID(ProjectID string) ([]*model.ApplicationView, error) {
|
||||
return view.ApplicationsByProjectID(v.Db, applicationTable, ProjectID)
|
||||
func (v *View) ApplicationsByProjectID(projectID string) ([]*model.ApplicationView, error) {
|
||||
return view.ApplicationsByProjectID(v.Db, applicationTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) SearchApplications(request *proj_model.ApplicationSearchRequest) ([]*model.ApplicationView, uint64, error) {
|
||||
return view.SearchApplications(v.Db, applicationTable, request)
|
||||
}
|
||||
|
||||
func (v *View) PutApplication(project *model.ApplicationView) error {
|
||||
err := view.PutApplication(v.Db, applicationTable, project)
|
||||
func (v *View) PutApplication(app *model.ApplicationView) error {
|
||||
err := view.PutApplication(v.Db, applicationTable, app)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedApplicationSequence(project.Sequence)
|
||||
return v.ProcessedApplicationSequence(app.Sequence)
|
||||
}
|
||||
|
||||
func (v *View) PutApplications(apps []*model.ApplicationView, sequence uint64) error {
|
||||
err := view.PutApplications(v.Db, applicationTable, apps...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedApplicationSequence(sequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteApplication(appID string, eventSequence uint64) error {
|
||||
@ -39,8 +47,8 @@ func (v *View) DeleteApplication(appID string, eventSequence uint64) error {
|
||||
return v.ProcessedApplicationSequence(eventSequence)
|
||||
}
|
||||
|
||||
func (v *View) DeleteApplicationsByProjectID(ProjectID string) error {
|
||||
return view.DeleteApplicationsByProjectID(v.Db, applicationTable, ProjectID)
|
||||
func (v *View) DeleteApplicationsByProjectID(projectID string) error {
|
||||
return view.DeleteApplicationsByProjectID(v.Db, applicationTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestApplicationSequence() (*repository.CurrentSequence, error) {
|
||||
|
@ -32,7 +32,7 @@ func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence
|
||||
}
|
||||
|
||||
func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64) error {
|
||||
err := view.PutUserMemberships(v.Db, userTable, memberships...)
|
||||
err := view.PutUserMemberships(v.Db, userMembershipTable, memberships...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -8,7 +8,7 @@ import (
|
||||
|
||||
type ProjectRepository interface {
|
||||
ProjectByID(ctx context.Context, id string) (*model.ProjectView, error)
|
||||
CreateProject(ctx context.Context, name string) (*model.Project, error)
|
||||
CreateProject(ctx context.Context, project *model.Project) (*model.Project, error)
|
||||
UpdateProject(ctx context.Context, project *model.Project) (*model.Project, error)
|
||||
DeactivateProject(ctx context.Context, id string) (*model.Project, error)
|
||||
ReactivateProject(ctx context.Context, id string) (*model.Project, error)
|
||||
|
@ -7,12 +7,14 @@ import (
|
||||
)
|
||||
|
||||
type ApplicationView struct {
|
||||
ID string
|
||||
ProjectID string
|
||||
Name string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
State AppState
|
||||
ID string
|
||||
ProjectID string
|
||||
Name string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
State AppState
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
|
||||
IsOIDC bool
|
||||
OIDCVersion OIDCVersion
|
||||
@ -27,6 +29,9 @@ type ApplicationView struct {
|
||||
ComplianceProblems []string
|
||||
DevMode bool
|
||||
OriginAllowList []string
|
||||
AccessTokenType OIDCTokenType
|
||||
IDTokenRoleAssertion bool
|
||||
AccessTokenRoleAssertion bool
|
||||
|
||||
Sequence uint64
|
||||
}
|
||||
|
@ -21,19 +21,22 @@ const (
|
||||
|
||||
type OIDCConfig struct {
|
||||
es_models.ObjectRoot
|
||||
AppID string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
ClientSecretString string
|
||||
RedirectUris []string
|
||||
ResponseTypes []OIDCResponseType
|
||||
GrantTypes []OIDCGrantType
|
||||
ApplicationType OIDCApplicationType
|
||||
AuthMethodType OIDCAuthMethodType
|
||||
PostLogoutRedirectUris []string
|
||||
OIDCVersion OIDCVersion
|
||||
Compliance *Compliance
|
||||
DevMode bool
|
||||
AppID string
|
||||
ClientID string
|
||||
ClientSecret *crypto.CryptoValue
|
||||
ClientSecretString string
|
||||
RedirectUris []string
|
||||
ResponseTypes []OIDCResponseType
|
||||
GrantTypes []OIDCGrantType
|
||||
ApplicationType OIDCApplicationType
|
||||
AuthMethodType OIDCAuthMethodType
|
||||
PostLogoutRedirectUris []string
|
||||
OIDCVersion OIDCVersion
|
||||
Compliance *Compliance
|
||||
DevMode bool
|
||||
AccessTokenType OIDCTokenType
|
||||
AccessTokenRoleAssertion bool
|
||||
IDTokenRoleAssertion bool
|
||||
}
|
||||
|
||||
type OIDCVersion int32
|
||||
@ -79,6 +82,13 @@ type Compliance struct {
|
||||
Problems []string
|
||||
}
|
||||
|
||||
type OIDCTokenType int32
|
||||
|
||||
const (
|
||||
OIDCTokenTypeBearer OIDCTokenType = iota
|
||||
OIDCTokenTypeJWT
|
||||
)
|
||||
|
||||
func (c *OIDCConfig) IsValid() bool {
|
||||
grantTypes := c.getRequiredGrantTypes()
|
||||
for _, grantType := range grantTypes {
|
||||
|
@ -1,19 +1,22 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/golang/protobuf/ptypes/timestamp"
|
||||
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
)
|
||||
|
||||
type Project struct {
|
||||
es_models.ObjectRoot
|
||||
|
||||
State ProjectState
|
||||
Name string
|
||||
Members []*ProjectMember
|
||||
Roles []*ProjectRole
|
||||
Applications []*Application
|
||||
Grants []*ProjectGrant
|
||||
State ProjectState
|
||||
Name string
|
||||
Members []*ProjectMember
|
||||
Roles []*ProjectRole
|
||||
Applications []*Application
|
||||
Grants []*ProjectGrant
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
}
|
||||
type ProjectChanges struct {
|
||||
Changes []*ProjectChange
|
||||
|
@ -1,18 +1,21 @@
|
||||
package model
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/model"
|
||||
"time"
|
||||
|
||||
"github.com/caos/zitadel/internal/model"
|
||||
)
|
||||
|
||||
type ProjectView struct {
|
||||
ProjectID string
|
||||
Name string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
State ProjectState
|
||||
ResourceOwner string
|
||||
Sequence uint64
|
||||
ProjectID string
|
||||
Name string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
State ProjectState
|
||||
ResourceOwner string
|
||||
ProjectRoleAssertion bool
|
||||
ProjectRoleCheck bool
|
||||
Sequence uint64
|
||||
}
|
||||
|
||||
type ProjectViewSearchRequest struct {
|
||||
|
@ -321,7 +321,7 @@ func (es *ProjectEventstore) AddProjectRoles(ctx context.Context, roles ...*proj
|
||||
}
|
||||
for _, role := range roles {
|
||||
if !role.IsValid() {
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-idue3", "Errors.Project.MemberInvalid")
|
||||
return nil, caos_errs.ThrowPreconditionFailed(nil, "EVENT-iduG4", "Errors.Project.RoleInvalid")
|
||||
}
|
||||
}
|
||||
existingProject, err := es.ProjectByID(ctx, roles[0].AggregateID)
|
||||
|
@ -2,26 +2,31 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"reflect"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
"github.com/caos/zitadel/internal/crypto"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
type OIDCConfig struct {
|
||||
es_models.ObjectRoot
|
||||
Version int32 `json:"oidcVersion,omitempty"`
|
||||
AppID string `json:"appId"`
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"`
|
||||
RedirectUris []string `json:"redirectUris,omitempty"`
|
||||
ResponseTypes []int32 `json:"responseTypes,omitempty"`
|
||||
GrantTypes []int32 `json:"grantTypes,omitempty"`
|
||||
ApplicationType int32 `json:"applicationType,omitempty"`
|
||||
AuthMethodType int32 `json:"authMethodType,omitempty"`
|
||||
PostLogoutRedirectUris []string `json:"postLogoutRedirectUris,omitempty"`
|
||||
DevMode bool `json:"devMode,omitempty"`
|
||||
Version int32 `json:"oidcVersion,omitempty"`
|
||||
AppID string `json:"appId"`
|
||||
ClientID string `json:"clientId,omitempty"`
|
||||
ClientSecret *crypto.CryptoValue `json:"clientSecret,omitempty"`
|
||||
RedirectUris []string `json:"redirectUris,omitempty"`
|
||||
ResponseTypes []int32 `json:"responseTypes,omitempty"`
|
||||
GrantTypes []int32 `json:"grantTypes,omitempty"`
|
||||
ApplicationType int32 `json:"applicationType,omitempty"`
|
||||
AuthMethodType int32 `json:"authMethodType,omitempty"`
|
||||
PostLogoutRedirectUris []string `json:"postLogoutRedirectUris,omitempty"`
|
||||
DevMode bool `json:"devMode,omitempty"`
|
||||
AccessTokenType int32 `json:"accessTokenType,omitempty"`
|
||||
AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion,omitempty"`
|
||||
IDTokenRoleAssertion bool `json:"idTokenRoleAssertion,omitempty"`
|
||||
}
|
||||
|
||||
func (c *OIDCConfig) Changes(changed *OIDCConfig) map[string]interface{} {
|
||||
@ -51,6 +56,15 @@ func (c *OIDCConfig) Changes(changed *OIDCConfig) map[string]interface{} {
|
||||
if c.DevMode != changed.DevMode {
|
||||
changes["devMode"] = changed.DevMode
|
||||
}
|
||||
if c.AccessTokenType != changed.AccessTokenType {
|
||||
changes["accessTokenType"] = changed.AccessTokenType
|
||||
}
|
||||
if c.AccessTokenRoleAssertion != changed.AccessTokenRoleAssertion {
|
||||
changes["accessTokenRoleAssertion"] = changed.AccessTokenRoleAssertion
|
||||
}
|
||||
if c.IDTokenRoleAssertion != changed.IDTokenRoleAssertion {
|
||||
changes["idTokenRoleAssertion"] = changed.IDTokenRoleAssertion
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
@ -64,18 +78,21 @@ func OIDCConfigFromModel(config *model.OIDCConfig) *OIDCConfig {
|
||||
grantTypes[i] = int32(rt)
|
||||
}
|
||||
return &OIDCConfig{
|
||||
ObjectRoot: config.ObjectRoot,
|
||||
AppID: config.AppID,
|
||||
Version: int32(config.OIDCVersion),
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
RedirectUris: config.RedirectUris,
|
||||
ResponseTypes: responseTypes,
|
||||
GrantTypes: grantTypes,
|
||||
ApplicationType: int32(config.ApplicationType),
|
||||
AuthMethodType: int32(config.AuthMethodType),
|
||||
PostLogoutRedirectUris: config.PostLogoutRedirectUris,
|
||||
DevMode: config.DevMode,
|
||||
ObjectRoot: config.ObjectRoot,
|
||||
AppID: config.AppID,
|
||||
Version: int32(config.OIDCVersion),
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
RedirectUris: config.RedirectUris,
|
||||
ResponseTypes: responseTypes,
|
||||
GrantTypes: grantTypes,
|
||||
ApplicationType: int32(config.ApplicationType),
|
||||
AuthMethodType: int32(config.AuthMethodType),
|
||||
PostLogoutRedirectUris: config.PostLogoutRedirectUris,
|
||||
DevMode: config.DevMode,
|
||||
AccessTokenType: int32(config.AccessTokenType),
|
||||
AccessTokenRoleAssertion: config.AccessTokenRoleAssertion,
|
||||
IDTokenRoleAssertion: config.IDTokenRoleAssertion,
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,18 +106,21 @@ func OIDCConfigToModel(config *OIDCConfig) *model.OIDCConfig {
|
||||
grantTypes[i] = model.OIDCGrantType(rt)
|
||||
}
|
||||
oidcConfig := &model.OIDCConfig{
|
||||
ObjectRoot: config.ObjectRoot,
|
||||
AppID: config.AppID,
|
||||
OIDCVersion: model.OIDCVersion(config.Version),
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
RedirectUris: config.RedirectUris,
|
||||
ResponseTypes: responseTypes,
|
||||
GrantTypes: grantTypes,
|
||||
ApplicationType: model.OIDCApplicationType(config.ApplicationType),
|
||||
AuthMethodType: model.OIDCAuthMethodType(config.AuthMethodType),
|
||||
PostLogoutRedirectUris: config.PostLogoutRedirectUris,
|
||||
DevMode: config.DevMode,
|
||||
ObjectRoot: config.ObjectRoot,
|
||||
AppID: config.AppID,
|
||||
OIDCVersion: model.OIDCVersion(config.Version),
|
||||
ClientID: config.ClientID,
|
||||
ClientSecret: config.ClientSecret,
|
||||
RedirectUris: config.RedirectUris,
|
||||
ResponseTypes: responseTypes,
|
||||
GrantTypes: grantTypes,
|
||||
ApplicationType: model.OIDCApplicationType(config.ApplicationType),
|
||||
AuthMethodType: model.OIDCAuthMethodType(config.AuthMethodType),
|
||||
PostLogoutRedirectUris: config.PostLogoutRedirectUris,
|
||||
DevMode: config.DevMode,
|
||||
AccessTokenType: model.OIDCTokenType(config.AccessTokenType),
|
||||
AccessTokenRoleAssertion: config.AccessTokenRoleAssertion,
|
||||
IDTokenRoleAssertion: config.IDTokenRoleAssertion,
|
||||
}
|
||||
oidcConfig.FillCompliance()
|
||||
return oidcConfig
|
||||
|
@ -2,7 +2,9 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
)
|
||||
@ -13,12 +15,14 @@ const (
|
||||
|
||||
type Project struct {
|
||||
es_models.ObjectRoot
|
||||
Name string `json:"name,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
Members []*ProjectMember `json:"-"`
|
||||
Roles []*ProjectRole `json:"-"`
|
||||
Applications []*Application `json:"-"`
|
||||
Grants []*ProjectGrant `json:"-"`
|
||||
Name string `json:"name,omitempty"`
|
||||
ProjectRoleAssertion bool `json:"projectRoleAssertion,omitempty"`
|
||||
ProjectRoleCheck bool `json:"projectRoleCheck,omitempty"`
|
||||
State int32 `json:"-"`
|
||||
Members []*ProjectMember `json:"-"`
|
||||
Roles []*ProjectRole `json:"-"`
|
||||
Applications []*Application `json:"-"`
|
||||
Grants []*ProjectGrant `json:"-"`
|
||||
}
|
||||
|
||||
func GetProject(projects []*Project, id string) (int, *Project) {
|
||||
@ -35,6 +39,12 @@ func (p *Project) Changes(changed *Project) map[string]interface{} {
|
||||
if changed.Name != "" && p.Name != changed.Name {
|
||||
changes["name"] = changed.Name
|
||||
}
|
||||
if p.ProjectRoleAssertion != changed.ProjectRoleAssertion {
|
||||
changes["projectRoleAssertion"] = changed.ProjectRoleAssertion
|
||||
}
|
||||
if p.ProjectRoleCheck != changed.ProjectRoleCheck {
|
||||
changes["projectRoleCheck"] = changed.ProjectRoleCheck
|
||||
}
|
||||
return changes
|
||||
}
|
||||
|
||||
@ -44,13 +54,15 @@ func ProjectFromModel(project *model.Project) *Project {
|
||||
apps := AppsFromModel(project.Applications)
|
||||
grants := GrantsFromModel(project.Grants)
|
||||
return &Project{
|
||||
ObjectRoot: project.ObjectRoot,
|
||||
Name: project.Name,
|
||||
State: int32(project.State),
|
||||
Members: members,
|
||||
Roles: roles,
|
||||
Applications: apps,
|
||||
Grants: grants,
|
||||
ObjectRoot: project.ObjectRoot,
|
||||
Name: project.Name,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
State: int32(project.State),
|
||||
Members: members,
|
||||
Roles: roles,
|
||||
Applications: apps,
|
||||
Grants: grants,
|
||||
}
|
||||
}
|
||||
|
||||
@ -60,13 +72,15 @@ func ProjectToModel(project *Project) *model.Project {
|
||||
apps := AppsToModel(project.Applications)
|
||||
grants := GrantsToModel(project.Grants)
|
||||
return &model.Project{
|
||||
ObjectRoot: project.ObjectRoot,
|
||||
Name: project.Name,
|
||||
State: model.ProjectState(project.State),
|
||||
Members: members,
|
||||
Roles: roles,
|
||||
Applications: apps,
|
||||
Grants: grants,
|
||||
ObjectRoot: project.ObjectRoot,
|
||||
Name: project.Name,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
State: model.ProjectState(project.State),
|
||||
Members: members,
|
||||
Roles: roles,
|
||||
Applications: apps,
|
||||
Grants: grants,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,6 +69,15 @@ func PutApplication(db *gorm.DB, table string, app *model.ApplicationView) error
|
||||
return save(db, app)
|
||||
}
|
||||
|
||||
func PutApplications(db *gorm.DB, table string, apps ...*model.ApplicationView) error {
|
||||
save := repository.PrepareBulkSave(table)
|
||||
s := make([]interface{}, len(apps))
|
||||
for i, app := range apps {
|
||||
s[i] = app
|
||||
}
|
||||
return save(db, s...)
|
||||
}
|
||||
|
||||
func DeleteApplication(db *gorm.DB, table, appID string) error {
|
||||
delete := repository.PrepareDeleteByKey(table, model.ApplicationSearchKey(proj_model.AppSearchKeyAppID), appID)
|
||||
return delete(db)
|
||||
|
@ -23,12 +23,14 @@ const (
|
||||
)
|
||||
|
||||
type ApplicationView struct {
|
||||
ID string `json:"appId" gorm:"column:id;primary_key"`
|
||||
ProjectID string `json:"-" gorm:"column:project_id"`
|
||||
Name string `json:"name" gorm:"column:app_name"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
State int32 `json:"-" gorm:"column:app_state"`
|
||||
ID string `json:"appId" gorm:"column:id;primary_key"`
|
||||
ProjectID string `json:"-" gorm:"column:project_id"`
|
||||
Name string `json:"name" gorm:"column:app_name"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
State int32 `json:"-" gorm:"column:app_state"`
|
||||
ProjectRoleAssertion bool `json:"projectRoleAssertion" gorm:"column:project_role_assertion"`
|
||||
ProjectRoleCheck bool `json:"projectRoleCheck" gorm:"column:project_role_check"`
|
||||
|
||||
IsOIDC bool `json:"-" gorm:"column:is_oidc"`
|
||||
OIDCVersion int32 `json:"oidcVersion" gorm:"column:oidc_version"`
|
||||
@ -43,58 +45,24 @@ type ApplicationView struct {
|
||||
ComplianceProblems pq.StringArray `json:"-" gorm:"column:compliance_problems"`
|
||||
DevMode bool `json:"devMode" gorm:"column:dev_mode"`
|
||||
OriginAllowList pq.StringArray `json:"-" gorm:"column:origin_allow_list"`
|
||||
AccessTokenType int32 `json:"accessTokenType" gorm:"column:access_token_type"`
|
||||
AccessTokenRoleAssertion bool `json:"accessTokenRoleAssertion" gorm:"column:access_token_role_assertion"`
|
||||
IDTokenRoleAssertion bool `json:"idTokenRoleAssertion" gorm:"column:id_token_role_assertion"`
|
||||
|
||||
Sequence uint64 `json:"-" gorm:"sequence"`
|
||||
}
|
||||
|
||||
func ApplicationViewFromModel(app *model.ApplicationView) *ApplicationView {
|
||||
return &ApplicationView{
|
||||
ID: app.ID,
|
||||
ProjectID: app.ProjectID,
|
||||
Name: app.Name,
|
||||
State: int32(app.State),
|
||||
Sequence: app.Sequence,
|
||||
CreationDate: app.CreationDate,
|
||||
ChangeDate: app.ChangeDate,
|
||||
|
||||
IsOIDC: app.IsOIDC,
|
||||
OIDCClientID: app.OIDCClientID,
|
||||
OIDCRedirectUris: app.OIDCRedirectUris,
|
||||
OIDCResponseTypes: OIDCResponseTypesFromModel(app.OIDCResponseTypes),
|
||||
OIDCGrantTypes: OIDCGrantTypesFromModel(app.OIDCGrantTypes),
|
||||
OIDCApplicationType: int32(app.OIDCApplicationType),
|
||||
OIDCAuthMethodType: int32(app.OIDCAuthMethodType),
|
||||
OIDCPostLogoutRedirectUris: app.OIDCPostLogoutRedirectUris,
|
||||
DevMode: app.DevMode,
|
||||
OriginAllowList: app.OriginAllowList,
|
||||
}
|
||||
}
|
||||
|
||||
func OIDCResponseTypesFromModel(oidctypes []model.OIDCResponseType) []int64 {
|
||||
result := make([]int64, len(oidctypes))
|
||||
for i, t := range oidctypes {
|
||||
result[i] = int64(t)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func OIDCGrantTypesFromModel(granttypes []model.OIDCGrantType) []int64 {
|
||||
result := make([]int64, len(granttypes))
|
||||
for i, t := range granttypes {
|
||||
result[i] = int64(t)
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func ApplicationViewToModel(app *ApplicationView) *model.ApplicationView {
|
||||
return &model.ApplicationView{
|
||||
ID: app.ID,
|
||||
ProjectID: app.ProjectID,
|
||||
Name: app.Name,
|
||||
State: model.AppState(app.State),
|
||||
Sequence: app.Sequence,
|
||||
CreationDate: app.CreationDate,
|
||||
ChangeDate: app.ChangeDate,
|
||||
ID: app.ID,
|
||||
ProjectID: app.ProjectID,
|
||||
Name: app.Name,
|
||||
State: model.AppState(app.State),
|
||||
Sequence: app.Sequence,
|
||||
CreationDate: app.CreationDate,
|
||||
ChangeDate: app.ChangeDate,
|
||||
ProjectRoleAssertion: app.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: app.ProjectRoleCheck,
|
||||
|
||||
IsOIDC: app.IsOIDC,
|
||||
OIDCVersion: model.OIDCVersion(app.OIDCVersion),
|
||||
@ -109,6 +77,9 @@ func ApplicationViewToModel(app *ApplicationView) *model.ApplicationView {
|
||||
ComplianceProblems: app.ComplianceProblems,
|
||||
DevMode: app.DevMode,
|
||||
OriginAllowList: app.OriginAllowList,
|
||||
AccessTokenType: model.OIDCTokenType(app.AccessTokenType),
|
||||
AccessTokenRoleAssertion: app.AccessTokenRoleAssertion,
|
||||
IDTokenRoleAssertion: app.IDTokenRoleAssertion,
|
||||
}
|
||||
}
|
||||
|
||||
@ -152,6 +123,8 @@ func (a *ApplicationView) AppendEventIfMyApp(event *models.Event) (err error) {
|
||||
}
|
||||
case es_model.ApplicationRemoved:
|
||||
return view.SetData(event)
|
||||
case es_model.ProjectChanged:
|
||||
return a.AppendEvent(event)
|
||||
case es_model.ProjectRemoved:
|
||||
return a.AppendEvent(event)
|
||||
default:
|
||||
@ -187,6 +160,8 @@ func (a *ApplicationView) AppendEvent(event *models.Event) (err error) {
|
||||
}
|
||||
a.setCompliance()
|
||||
return a.setOriginAllowList()
|
||||
case es_model.ProjectChanged:
|
||||
return a.SetData(event)
|
||||
case es_model.ApplicationDeactivated:
|
||||
a.State = int32(model.AppStateInactive)
|
||||
case es_model.ApplicationReactivated:
|
||||
|
@ -2,12 +2,14 @@ package model
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/project/model"
|
||||
es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -17,36 +19,42 @@ const (
|
||||
)
|
||||
|
||||
type ProjectView struct {
|
||||
ProjectID string `json:"-" gorm:"column:project_id;primary_key"`
|
||||
Name string `json:"name" gorm:"column:project_name"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
State int32 `json:"-" gorm:"column:project_state"`
|
||||
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
ProjectID string `json:"-" gorm:"column:project_id;primary_key"`
|
||||
Name string `json:"name" gorm:"column:project_name"`
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
State int32 `json:"-" gorm:"column:project_state"`
|
||||
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
|
||||
ProjectRoleAssertion bool `json:"projectRoleAssertion" gorm:"column:project_role_assertion"`
|
||||
ProjectRoleCheck bool `json:"projectRoleCheck" gorm:"column:project_role_check"`
|
||||
Sequence uint64 `json:"-" gorm:"column:sequence"`
|
||||
}
|
||||
|
||||
func ProjectFromModel(project *model.ProjectView) *ProjectView {
|
||||
return &ProjectView{
|
||||
ProjectID: project.ProjectID,
|
||||
Name: project.Name,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
State: int32(project.State),
|
||||
ResourceOwner: project.ResourceOwner,
|
||||
Sequence: project.Sequence,
|
||||
ProjectID: project.ProjectID,
|
||||
Name: project.Name,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
State: int32(project.State),
|
||||
ResourceOwner: project.ResourceOwner,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
Sequence: project.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
func ProjectToModel(project *ProjectView) *model.ProjectView {
|
||||
return &model.ProjectView{
|
||||
ProjectID: project.ProjectID,
|
||||
Name: project.Name,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
State: model.ProjectState(project.State),
|
||||
ResourceOwner: project.ResourceOwner,
|
||||
Sequence: project.Sequence,
|
||||
ProjectID: project.ProjectID,
|
||||
Name: project.Name,
|
||||
ChangeDate: project.ChangeDate,
|
||||
CreationDate: project.CreationDate,
|
||||
State: model.ProjectState(project.State),
|
||||
ResourceOwner: project.ResourceOwner,
|
||||
ProjectRoleAssertion: project.ProjectRoleAssertion,
|
||||
ProjectRoleCheck: project.ProjectRoleCheck,
|
||||
Sequence: project.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -222,32 +222,32 @@ func (l *Login) handleAutoRegister(w http.ResponseWriter, r *http.Request, authR
|
||||
}
|
||||
|
||||
func (l *Login) mapTokenToLoginUser(tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) *model.ExternalUser {
|
||||
displayName := tokens.IDTokenClaims.PreferredUsername
|
||||
if displayName == "" && tokens.IDTokenClaims.Email != "" {
|
||||
displayName = tokens.IDTokenClaims.Email
|
||||
displayName := tokens.IDTokenClaims.GetPreferredUsername()
|
||||
if displayName == "" && tokens.IDTokenClaims.GetEmail() != "" {
|
||||
displayName = tokens.IDTokenClaims.GetEmail()
|
||||
}
|
||||
switch idpConfig.OIDCIDPDisplayNameMapping {
|
||||
case iam_model.OIDCMappingFieldEmail:
|
||||
if tokens.IDTokenClaims.EmailVerified && tokens.IDTokenClaims.Email != "" {
|
||||
displayName = tokens.IDTokenClaims.Email
|
||||
if tokens.IDTokenClaims.IsEmailVerified() && tokens.IDTokenClaims.GetEmail() != "" {
|
||||
displayName = tokens.IDTokenClaims.GetEmail()
|
||||
}
|
||||
}
|
||||
|
||||
externalUser := &model.ExternalUser{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
ExternalUserID: tokens.IDTokenClaims.Subject,
|
||||
PreferredUsername: tokens.IDTokenClaims.PreferredUsername,
|
||||
ExternalUserID: tokens.IDTokenClaims.GetSubject(),
|
||||
PreferredUsername: tokens.IDTokenClaims.GetPreferredUsername(),
|
||||
DisplayName: displayName,
|
||||
FirstName: tokens.IDTokenClaims.GivenName,
|
||||
LastName: tokens.IDTokenClaims.FamilyName,
|
||||
NickName: tokens.IDTokenClaims.Nickname,
|
||||
Email: tokens.IDTokenClaims.Email,
|
||||
IsEmailVerified: tokens.IDTokenClaims.EmailVerified,
|
||||
FirstName: tokens.IDTokenClaims.GetGivenName(),
|
||||
LastName: tokens.IDTokenClaims.GetFamilyName(),
|
||||
NickName: tokens.IDTokenClaims.GetNickname(),
|
||||
Email: tokens.IDTokenClaims.GetEmail(),
|
||||
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
|
||||
}
|
||||
|
||||
if tokens.IDTokenClaims.PhoneNumber != "" {
|
||||
externalUser.Phone = tokens.IDTokenClaims.PhoneNumber
|
||||
externalUser.IsPhoneVerified = tokens.IDTokenClaims.PhoneNumberVerified
|
||||
if tokens.IDTokenClaims.GetPhoneNumber() != "" {
|
||||
externalUser.Phone = tokens.IDTokenClaims.GetPhoneNumber()
|
||||
externalUser.IsPhoneVerified = tokens.IDTokenClaims.IsPhoneNumberVerified()
|
||||
}
|
||||
return externalUser
|
||||
}
|
||||
|
@ -1,8 +1,13 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/caos/oidc/pkg/oidc"
|
||||
"github.com/caos/oidc/pkg/rp"
|
||||
"golang.org/x/text/language"
|
||||
|
||||
http_mw "github.com/caos/zitadel/internal/api/http/middleware"
|
||||
"github.com/caos/zitadel/internal/auth_request/model"
|
||||
caos_errors "github.com/caos/zitadel/internal/errors"
|
||||
@ -10,8 +15,6 @@ import (
|
||||
iam_model "github.com/caos/zitadel/internal/iam/model"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
"net/http"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func (l *Login) handleExternalRegister(w http.ResponseWriter, r *http.Request) {
|
||||
@ -109,11 +112,11 @@ func (l *Login) handleExternalUserRegister(w http.ResponseWriter, r *http.Reques
|
||||
}
|
||||
|
||||
func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAMPolicyView, tokens *oidc.Tokens, idpConfig *iam_model.IDPConfigView) (*usr_model.User, *usr_model.ExternalIDP) {
|
||||
username := tokens.IDTokenClaims.PreferredUsername
|
||||
username := tokens.IDTokenClaims.GetPreferredUsername()
|
||||
switch idpConfig.OIDCUsernameMapping {
|
||||
case iam_model.OIDCMappingFieldEmail:
|
||||
if tokens.IDTokenClaims.EmailVerified && tokens.IDTokenClaims.Email != "" {
|
||||
username = tokens.IDTokenClaims.Email
|
||||
if tokens.IDTokenClaims.IsEmailVerified() && tokens.IDTokenClaims.GetEmail() != "" {
|
||||
username = tokens.IDTokenClaims.GetEmail()
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,35 +131,35 @@ func (l *Login) mapTokenToLoginUserAndExternalIDP(orgIamPolicy *iam_model.OrgIAM
|
||||
UserName: username,
|
||||
Human: &usr_model.Human{
|
||||
Profile: &usr_model.Profile{
|
||||
FirstName: tokens.IDTokenClaims.GivenName,
|
||||
LastName: tokens.IDTokenClaims.FamilyName,
|
||||
PreferredLanguage: tokens.IDTokenClaims.Locale,
|
||||
NickName: tokens.IDTokenClaims.Nickname,
|
||||
FirstName: tokens.IDTokenClaims.GetGivenName(),
|
||||
LastName: tokens.IDTokenClaims.GetFamilyName(),
|
||||
PreferredLanguage: language.Tag(tokens.IDTokenClaims.GetLocale()),
|
||||
NickName: tokens.IDTokenClaims.GetNickname(),
|
||||
},
|
||||
Email: &usr_model.Email{
|
||||
EmailAddress: tokens.IDTokenClaims.Email,
|
||||
IsEmailVerified: tokens.IDTokenClaims.EmailVerified,
|
||||
EmailAddress: tokens.IDTokenClaims.GetEmail(),
|
||||
IsEmailVerified: tokens.IDTokenClaims.IsEmailVerified(),
|
||||
},
|
||||
},
|
||||
}
|
||||
if tokens.IDTokenClaims.PhoneNumber != "" {
|
||||
if tokens.IDTokenClaims.GetPhoneNumber() != "" {
|
||||
user.Phone = &usr_model.Phone{
|
||||
PhoneNumber: tokens.IDTokenClaims.PhoneNumber,
|
||||
IsPhoneVerified: tokens.IDTokenClaims.PhoneNumberVerified,
|
||||
PhoneNumber: tokens.IDTokenClaims.GetPhoneNumber(),
|
||||
IsPhoneVerified: tokens.IDTokenClaims.IsPhoneNumberVerified(),
|
||||
}
|
||||
}
|
||||
|
||||
displayName := tokens.IDTokenClaims.PreferredUsername
|
||||
displayName := tokens.IDTokenClaims.GetPreferredUsername()
|
||||
switch idpConfig.OIDCIDPDisplayNameMapping {
|
||||
case iam_model.OIDCMappingFieldEmail:
|
||||
if tokens.IDTokenClaims.EmailVerified && tokens.IDTokenClaims.Email != "" {
|
||||
displayName = tokens.IDTokenClaims.Email
|
||||
if tokens.IDTokenClaims.IsEmailVerified() && tokens.IDTokenClaims.GetEmail() != "" {
|
||||
displayName = tokens.IDTokenClaims.GetEmail()
|
||||
}
|
||||
}
|
||||
|
||||
externalIDP := &usr_model.ExternalIDP{
|
||||
IDPConfigID: idpConfig.IDPConfigID,
|
||||
UserID: tokens.IDTokenClaims.Subject,
|
||||
UserID: tokens.IDTokenClaims.GetSubject(),
|
||||
DisplayName: displayName,
|
||||
}
|
||||
return user, externalIDP
|
||||
|
@ -211,6 +211,8 @@ func (l *Login) chooseNextStep(w http.ResponseWriter, r *http.Request, authReq *
|
||||
l.renderExternalNotFoundOption(w, r, authReq, err)
|
||||
case *model.ExternalLoginStep:
|
||||
l.handleExternalLoginStep(w, r, authReq, step.SelectedIDPConfigID)
|
||||
case *model.GrantRequiredStep:
|
||||
l.renderInternalError(w, r, authReq, caos_errs.ThrowPreconditionFailed(nil, "APP-asb43", "Errors.User.GrantRequired"))
|
||||
default:
|
||||
l.renderInternalError(w, r, authReq, caos_errs.ThrowInternal(nil, "APP-ds3QF", "step no possible"))
|
||||
}
|
||||
|
@ -187,6 +187,7 @@ Errors:
|
||||
AuthRequest:
|
||||
NotFound: AuthRequest konnte nicht gefunden werden
|
||||
UserAgentNotCorresponding: User Agent stimmt nicht überein
|
||||
RequestTypeNotSupported: Requesttyp wird nicht unterstürzt
|
||||
User:
|
||||
NotFound: Benutzer konnte nicht gefunden werden
|
||||
NotMatchingUserID: User stimm nicht mit User in Auth Request überein
|
||||
@ -226,5 +227,6 @@ Errors:
|
||||
ExternalIDP:
|
||||
IDPTypeNotImplemented: IDP Typ ist nicht implementiert
|
||||
NotAllowed: Externer Login Provider ist nicht erlaubt
|
||||
GrantRequired: Der Login an diese Applikation ist nicht möglich. Der Benutzer benötigt mindestens eine Berechtigung an der Applikation. Bitte melde dich bei deinem Administrator.
|
||||
|
||||
optional: (optional)
|
||||
|
@ -187,6 +187,7 @@ Errors:
|
||||
AuthRequest:
|
||||
NotFound: Could not find authrequest
|
||||
UserAgentNotCorresponding: User Agent does not correspond
|
||||
RequestTypeNotSupported: Request type is not supported
|
||||
User:
|
||||
NotFound: User could not be found
|
||||
NotMatchingUserID: User and user in authrequest don't match
|
||||
@ -226,6 +227,7 @@ Errors:
|
||||
ExternalIDP:
|
||||
IDPTypeNotImplemented: IDP Type is not implemented
|
||||
NotAllowed: External Login Provider not allowed
|
||||
GrantRequired: Login not possible. The user is required to have at least one grant on the application. Please contact your administrator.
|
||||
|
||||
|
||||
optional: (optional)
|
||||
|
@ -1,6 +1,6 @@
|
||||
{{template "main-top" .}}
|
||||
|
||||
<div>
|
||||
<div class="head">
|
||||
{{ .ErrType }}
|
||||
{{ .ErrMessage }}
|
||||
</div>
|
||||
|
@ -44,7 +44,7 @@ const (
|
||||
type TokenSearchQuery struct {
|
||||
Key TokenSearchKey
|
||||
Method model.SearchMethod
|
||||
Value string
|
||||
Value interface{}
|
||||
}
|
||||
|
||||
type TokenSearchResponse struct {
|
||||
|
@ -7,20 +7,20 @@ import (
|
||||
)
|
||||
|
||||
type UserGrantView struct {
|
||||
ID string
|
||||
ResourceOwner string
|
||||
UserID string
|
||||
ProjectID string
|
||||
GrantID string
|
||||
UserName string
|
||||
FirstName string
|
||||
LastName string
|
||||
DisplayName string
|
||||
Email string
|
||||
ProjectName string
|
||||
OrgName string
|
||||
OrgDomain string
|
||||
RoleKeys []string
|
||||
ID string
|
||||
ResourceOwner string
|
||||
UserID string
|
||||
ProjectID string
|
||||
GrantID string
|
||||
UserName string
|
||||
FirstName string
|
||||
LastName string
|
||||
DisplayName string
|
||||
Email string
|
||||
ProjectName string
|
||||
OrgName string
|
||||
OrgPrimaryDomain string
|
||||
RoleKeys []string
|
||||
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
|
@ -5,11 +5,12 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/lib/pq"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/models"
|
||||
"github.com/caos/zitadel/internal/usergrant/model"
|
||||
es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model"
|
||||
"github.com/lib/pq"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -24,19 +25,20 @@ const (
|
||||
)
|
||||
|
||||
type UserGrantView struct {
|
||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||
ResourceOwner string `json:"-" gorm:"resource_owner"`
|
||||
UserID string `json:"userId" gorm:"user_id"`
|
||||
ProjectID string `json:"projectId" gorm:"column:project_id"`
|
||||
GrantID string `json:"grantId" gorm:"column:grant_id"`
|
||||
UserName string `json:"-" gorm:"column:user_name"`
|
||||
FirstName string `json:"-" gorm:"column:first_name"`
|
||||
LastName string `json:"-" gorm:"column:last_name"`
|
||||
DisplayName string `json:"-" gorm:"column:display_name"`
|
||||
Email string `json:"-" gorm:"column:email"`
|
||||
ProjectName string `json:"-" gorm:"column:project_name"`
|
||||
OrgName string `json:"-" gorm:"column:org_name"`
|
||||
RoleKeys pq.StringArray `json:"roleKeys" gorm:"column:role_keys"`
|
||||
ID string `json:"-" gorm:"column:id;primary_key"`
|
||||
ResourceOwner string `json:"-" gorm:"resource_owner"`
|
||||
UserID string `json:"userId" gorm:"user_id"`
|
||||
ProjectID string `json:"projectId" gorm:"column:project_id"`
|
||||
GrantID string `json:"grantId" gorm:"column:grant_id"`
|
||||
UserName string `json:"-" gorm:"column:user_name"`
|
||||
FirstName string `json:"-" gorm:"column:first_name"`
|
||||
LastName string `json:"-" gorm:"column:last_name"`
|
||||
DisplayName string `json:"-" gorm:"column:display_name"`
|
||||
Email string `json:"-" gorm:"column:email"`
|
||||
ProjectName string `json:"-" gorm:"column:project_name"`
|
||||
OrgName string `json:"-" gorm:"column:org_name"`
|
||||
OrgPrimaryDomain string `json:"-" gorm:"column:org_primary_domain"`
|
||||
RoleKeys pq.StringArray `json:"roleKeys" gorm:"column:role_keys"`
|
||||
|
||||
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
|
||||
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
|
||||
@ -47,22 +49,23 @@ type UserGrantView struct {
|
||||
|
||||
func UserGrantToModel(grant *UserGrantView) *model.UserGrantView {
|
||||
return &model.UserGrantView{
|
||||
ID: grant.ID,
|
||||
ResourceOwner: grant.ResourceOwner,
|
||||
UserID: grant.UserID,
|
||||
ProjectID: grant.ProjectID,
|
||||
ChangeDate: grant.ChangeDate,
|
||||
CreationDate: grant.CreationDate,
|
||||
State: model.UserGrantState(grant.State),
|
||||
UserName: grant.UserName,
|
||||
FirstName: grant.FirstName,
|
||||
LastName: grant.LastName,
|
||||
DisplayName: grant.DisplayName,
|
||||
Email: grant.Email,
|
||||
ProjectName: grant.ProjectName,
|
||||
OrgName: grant.OrgName,
|
||||
RoleKeys: grant.RoleKeys,
|
||||
Sequence: grant.Sequence,
|
||||
ID: grant.ID,
|
||||
ResourceOwner: grant.ResourceOwner,
|
||||
UserID: grant.UserID,
|
||||
ProjectID: grant.ProjectID,
|
||||
ChangeDate: grant.ChangeDate,
|
||||
CreationDate: grant.CreationDate,
|
||||
State: model.UserGrantState(grant.State),
|
||||
UserName: grant.UserName,
|
||||
FirstName: grant.FirstName,
|
||||
LastName: grant.LastName,
|
||||
DisplayName: grant.DisplayName,
|
||||
Email: grant.Email,
|
||||
ProjectName: grant.ProjectName,
|
||||
OrgName: grant.OrgName,
|
||||
OrgPrimaryDomain: grant.OrgPrimaryDomain,
|
||||
RoleKeys: grant.RoleKeys,
|
||||
Sequence: grant.Sequence,
|
||||
}
|
||||
}
|
||||
|
||||
|
40
migrations/cockroach/V1.18__scope_projectroles.sql
Normal file
40
migrations/cockroach/V1.18__scope_projectroles.sql
Normal file
@ -0,0 +1,40 @@
|
||||
CREATE TABLE auth.project_roles (
|
||||
project_id TEXT,
|
||||
role_key TEXT,
|
||||
display_name TEXT,
|
||||
resource_owner TEXT,
|
||||
org_id TEXT,
|
||||
group_name TEXT,
|
||||
|
||||
creation_date TIMESTAMPTZ,
|
||||
sequence BIGINT,
|
||||
|
||||
PRIMARY KEY (org_id, project_id, role_key)
|
||||
);
|
||||
|
||||
ALTER TABLE authz.user_grants ADD COLUMN org_primary_domain TEXT;
|
||||
ALTER TABLE auth.user_grants ADD COLUMN org_primary_domain TEXT;
|
||||
ALTER TABLE management.user_grants ADD COLUMN org_primary_domain TEXT;
|
||||
|
||||
ALTER TABLE authz.applications ADD COLUMN access_token_type SMALLINT;
|
||||
ALTER TABLE auth.applications ADD COLUMN access_token_type SMALLINT;
|
||||
ALTER TABLE management.applications ADD COLUMN access_token_type SMALLINT;
|
||||
|
||||
ALTER TABLE management.projects ADD COLUMN project_role_assertion BOOLEAN;
|
||||
ALTER TABLE management.projects ADD COLUMN project_role_check BOOLEAN;
|
||||
|
||||
ALTER TABLE authz.applications ADD COLUMN project_role_assertion BOOLEAN;
|
||||
ALTER TABLE auth.applications ADD COLUMN project_role_assertion BOOLEAN;
|
||||
ALTER TABLE management.applications ADD COLUMN project_role_assertion BOOLEAN;
|
||||
|
||||
ALTER TABLE authz.applications ADD COLUMN project_role_check BOOLEAN;
|
||||
ALTER TABLE auth.applications ADD COLUMN project_role_check BOOLEAN;
|
||||
ALTER TABLE management.applications ADD COLUMN project_role_check BOOLEAN;
|
||||
|
||||
ALTER TABLE authz.applications ADD COLUMN access_token_role_assertion BOOLEAN;
|
||||
ALTER TABLE auth.applications ADD COLUMN access_token_role_assertion BOOLEAN;
|
||||
ALTER TABLE management.applications ADD COLUMN access_token_role_assertion BOOLEAN;
|
||||
|
||||
ALTER TABLE authz.applications ADD COLUMN id_token_role_assertion BOOLEAN;
|
||||
ALTER TABLE auth.applications ADD COLUMN id_token_role_assertion BOOLEAN;
|
||||
ALTER TABLE management.applications ADD COLUMN id_token_role_assertion BOOLEAN;
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -6381,6 +6381,10 @@ func (m *ProjectCreateRequest) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
// no validation rules for ProjectRoleAssertion
|
||||
|
||||
// no validation rules for ProjectRoleCheck
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6462,6 +6466,10 @@ func (m *ProjectUpdateRequest) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
// no validation rules for ProjectRoleAssertion
|
||||
|
||||
// no validation rules for ProjectRoleCheck
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6659,6 +6667,10 @@ func (m *ProjectView) Validate() error {
|
||||
|
||||
// no validation rules for Sequence
|
||||
|
||||
// no validation rules for ProjectRoleAssertion
|
||||
|
||||
// no validation rules for ProjectRoleCheck
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -6998,6 +7010,10 @@ func (m *Project) Validate() error {
|
||||
|
||||
// no validation rules for Sequence
|
||||
|
||||
// no validation rules for ProjectRoleAssertion
|
||||
|
||||
// no validation rules for ProjectRoleCheck
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -8850,6 +8866,12 @@ func (m *OIDCConfig) Validate() error {
|
||||
|
||||
// no validation rules for DevMode
|
||||
|
||||
// no validation rules for AccessTokenType
|
||||
|
||||
// no validation rules for AccessTokenRoleAssertion
|
||||
|
||||
// no validation rules for IdTokenRoleAssertion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -8937,6 +8959,12 @@ func (m *OIDCApplicationCreate) Validate() error {
|
||||
|
||||
// no validation rules for DevMode
|
||||
|
||||
// no validation rules for AccessTokenType
|
||||
|
||||
// no validation rules for AccessTokenRoleAssertion
|
||||
|
||||
// no validation rules for IdTokenRoleAssertion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -9024,6 +9052,12 @@ func (m *OIDCConfigUpdate) Validate() error {
|
||||
|
||||
// no validation rules for DevMode
|
||||
|
||||
// no validation rules for AccessTokenType
|
||||
|
||||
// no validation rules for AccessTokenRoleAssertion
|
||||
|
||||
// no validation rules for IdTokenRoleAssertion
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -2180,11 +2180,15 @@ enum OrgMemberSearchKey {
|
||||
|
||||
message ProjectCreateRequest {
|
||||
string name = 1 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
bool project_role_assertion = 2;
|
||||
bool project_role_check = 3;
|
||||
}
|
||||
|
||||
message ProjectUpdateRequest {
|
||||
string id = 1 [(validate.rules).string = {min_len: 1}];
|
||||
string name = 2 [(validate.rules).string = {min_len: 1, max_len: 200}];
|
||||
bool project_role_assertion = 3;
|
||||
bool project_role_check = 4;
|
||||
}
|
||||
|
||||
message ProjectSearchResponse {
|
||||
@ -2204,6 +2208,8 @@ message ProjectView {
|
||||
google.protobuf.Timestamp creation_date = 5;
|
||||
string resource_owner = 6;
|
||||
uint64 sequence = 7;
|
||||
bool project_role_assertion = 8;
|
||||
bool project_role_check = 9;
|
||||
}
|
||||
|
||||
message ProjectSearchRequest {
|
||||
@ -2234,6 +2240,8 @@ message Project {
|
||||
google.protobuf.Timestamp change_date = 4;
|
||||
google.protobuf.Timestamp creation_date = 5;
|
||||
uint64 sequence = 6;
|
||||
bool project_role_assertion = 7;
|
||||
bool project_role_check = 8;
|
||||
}
|
||||
|
||||
enum ProjectState {
|
||||
@ -2423,6 +2431,9 @@ message OIDCConfig {
|
||||
bool none_compliant = 10;
|
||||
repeated caos.zitadel.api.v1.LocalizedMessage compliance_problems = 11;
|
||||
bool dev_mode = 12;
|
||||
OIDCTokenType access_token_type = 13;
|
||||
bool access_token_role_assertion = 14;
|
||||
bool id_token_role_assertion = 15;
|
||||
}
|
||||
|
||||
message OIDCApplicationCreate {
|
||||
@ -2436,12 +2447,20 @@ message OIDCApplicationCreate {
|
||||
repeated string post_logout_redirect_uris = 8;
|
||||
OIDCVersion version = 9;
|
||||
bool dev_mode = 10;
|
||||
OIDCTokenType access_token_type = 11;
|
||||
bool access_token_role_assertion = 12;
|
||||
bool id_token_role_assertion = 13;
|
||||
}
|
||||
|
||||
enum OIDCVersion {
|
||||
OIDCV1_0 = 0;
|
||||
}
|
||||
|
||||
enum OIDCTokenType {
|
||||
OIDCTokenType_Bearer = 0;
|
||||
OIDCTokenType_JWT = 1;
|
||||
}
|
||||
|
||||
message OIDCConfigUpdate {
|
||||
string project_id = 1 [(validate.rules).string = {min_len: 1}];
|
||||
string application_id = 2 [(validate.rules).string = {min_len: 1}];
|
||||
@ -2452,6 +2471,9 @@ message OIDCConfigUpdate {
|
||||
OIDCAuthMethodType auth_method_type = 7;
|
||||
repeated string post_logout_redirect_uris = 8;
|
||||
bool dev_mode = 9;
|
||||
OIDCTokenType access_token_type = 10;
|
||||
bool access_token_role_assertion = 11;
|
||||
bool id_token_role_assertion = 12;
|
||||
}
|
||||
|
||||
enum OIDCResponseType {
|
||||
|
Loading…
x
Reference in New Issue
Block a user