mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 19:07:30 +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:
@@ -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,
|
||||
}
|
||||
}
|
||||
|
||||
|
Reference in New Issue
Block a user