mirror of
https://github.com/zitadel/zitadel.git
synced 2025-12-26 18:46:28 +00:00
# Which Problems Are Solved Project Grant ID would have needed to be unique to be handled properly on the projections, but was defined as the organization ID the project was granted to, so could be non-unique. # How the Problems Are Solved Generate the Project Grant ID even in the v2 APIs, which also includes fixes in the integration tests. Additionally to that, the logic for some functionality had to be extended as the Project Grant ID is not provided anymore in the API, so had to be queried before creating events for Project Grants. # Additional Changes Included fix for authorizations, when an authorization was intended to be created for a project, without providing any organization information, which also showed some faulty integration tests. # Additional Context Partially closes #10745 --------- Co-authored-by: Livio Spring <livio.a@gmail.com> Co-authored-by: Silvan <27845747+adlerhurst@users.noreply.github.com>
207 lines
5.9 KiB
Go
207 lines
5.9 KiB
Go
package command
|
|
|
|
import (
|
|
"slices"
|
|
|
|
"github.com/zitadel/zitadel/internal/domain"
|
|
"github.com/zitadel/zitadel/internal/eventstore"
|
|
"github.com/zitadel/zitadel/internal/repository/org"
|
|
"github.com/zitadel/zitadel/internal/repository/project"
|
|
)
|
|
|
|
type ProjectGrantWriteModel struct {
|
|
eventstore.WriteModel
|
|
|
|
GrantID string
|
|
GrantedOrgID string
|
|
RoleKeys []string
|
|
State domain.ProjectGrantState
|
|
|
|
FoundGrantID string
|
|
}
|
|
|
|
func NewProjectGrantWriteModel(grantID, grantedOrgID, projectID, resourceOwner string) *ProjectGrantWriteModel {
|
|
return &ProjectGrantWriteModel{
|
|
WriteModel: eventstore.WriteModel{
|
|
AggregateID: projectID,
|
|
ResourceOwner: resourceOwner,
|
|
},
|
|
// Always either the grantID or the grantedOrgID is provided
|
|
GrantID: grantID,
|
|
GrantedOrgID: grantedOrgID,
|
|
}
|
|
}
|
|
|
|
func (wm *ProjectGrantWriteModel) AppendEvents(events ...eventstore.Event) {
|
|
for _, event := range events {
|
|
switch e := event.(type) {
|
|
case *project.GrantAddedEvent:
|
|
if projectGrantExists(wm.GrantID, wm.GrantedOrgID, e.GrantID, e.GrantedOrgID) {
|
|
wm.FoundGrantID = e.GrantID
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
case *project.GrantChangedEvent:
|
|
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
case *project.GrantCascadeChangedEvent:
|
|
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
case *project.GrantDeactivateEvent:
|
|
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
case *project.GrantReactivatedEvent:
|
|
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
case *project.GrantRemovedEvent:
|
|
if projectGrantEqual(wm.FoundGrantID, e.GrantID) {
|
|
wm.FoundGrantID = ""
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
case *project.ProjectRemovedEvent:
|
|
wm.WriteModel.AppendEvents(e)
|
|
}
|
|
}
|
|
}
|
|
|
|
func projectGrantExists(requiredGrantID, requiredGrantedOrgID, grantID, grantedOrgID string) bool {
|
|
// either grantID or grantedOrgID is provided and equal
|
|
return projectGrantEqual(requiredGrantID, grantID) ||
|
|
(requiredGrantedOrgID != "" && grantedOrgID == requiredGrantedOrgID)
|
|
}
|
|
|
|
func projectGrantEqual(requiredGrantID, grantID string) bool {
|
|
// grantID is provided and equal
|
|
return requiredGrantID != "" && grantID == requiredGrantID
|
|
}
|
|
|
|
func (wm *ProjectGrantWriteModel) Reduce() error {
|
|
for _, event := range wm.Events {
|
|
switch e := event.(type) {
|
|
case *project.GrantAddedEvent:
|
|
wm.GrantID = e.GrantID
|
|
wm.GrantedOrgID = e.GrantedOrgID
|
|
wm.RoleKeys = e.RoleKeys
|
|
wm.State = domain.ProjectGrantStateActive
|
|
case *project.GrantChangedEvent:
|
|
wm.RoleKeys = e.RoleKeys
|
|
case *project.GrantCascadeChangedEvent:
|
|
wm.RoleKeys = e.RoleKeys
|
|
case *project.GrantDeactivateEvent:
|
|
if wm.State == domain.ProjectGrantStateRemoved {
|
|
continue
|
|
}
|
|
wm.State = domain.ProjectGrantStateInactive
|
|
case *project.GrantReactivatedEvent:
|
|
if wm.State == domain.ProjectGrantStateRemoved {
|
|
continue
|
|
}
|
|
wm.State = domain.ProjectGrantStateActive
|
|
case *project.GrantRemovedEvent:
|
|
wm.State = domain.ProjectGrantStateRemoved
|
|
case *project.ProjectRemovedEvent:
|
|
wm.State = domain.ProjectGrantStateRemoved
|
|
}
|
|
}
|
|
return wm.WriteModel.Reduce()
|
|
}
|
|
|
|
func (wm *ProjectGrantWriteModel) Query() *eventstore.SearchQueryBuilder {
|
|
return eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
|
ResourceOwner(wm.ResourceOwner).
|
|
AddQuery().
|
|
AggregateTypes(project.AggregateType).
|
|
AggregateIDs(wm.AggregateID).
|
|
EventTypes(
|
|
project.GrantAddedType,
|
|
project.GrantChangedType,
|
|
project.GrantCascadeChangedType,
|
|
project.GrantDeactivatedType,
|
|
project.GrantReactivatedType,
|
|
project.GrantRemovedType,
|
|
project.ProjectRemovedType).
|
|
Builder()
|
|
}
|
|
|
|
type ProjectGrantPreConditionReadModel struct {
|
|
eventstore.WriteModel
|
|
|
|
ProjectResourceOwner string
|
|
ProjectID string
|
|
GrantedOrgID string
|
|
ProjectExists bool
|
|
GrantedOrgExists bool
|
|
ExistingRoleKeys []string
|
|
}
|
|
|
|
func NewProjectGrantPreConditionReadModel(projectID, grantedOrgID, resourceOwner string) *ProjectGrantPreConditionReadModel {
|
|
return &ProjectGrantPreConditionReadModel{
|
|
WriteModel: eventstore.WriteModel{},
|
|
ProjectResourceOwner: resourceOwner,
|
|
ProjectID: projectID,
|
|
GrantedOrgID: grantedOrgID,
|
|
}
|
|
}
|
|
|
|
func (wm *ProjectGrantPreConditionReadModel) Reduce() error {
|
|
for _, event := range wm.Events {
|
|
switch e := event.(type) {
|
|
case *project.ProjectAddedEvent:
|
|
if wm.ProjectResourceOwner == "" {
|
|
wm.ProjectResourceOwner = e.Aggregate().ResourceOwner
|
|
}
|
|
if wm.ProjectResourceOwner != e.Aggregate().ResourceOwner {
|
|
continue
|
|
}
|
|
wm.ProjectExists = true
|
|
case *project.ProjectRemovedEvent:
|
|
if wm.ProjectResourceOwner != e.Aggregate().ResourceOwner {
|
|
continue
|
|
}
|
|
wm.ProjectResourceOwner = ""
|
|
wm.ProjectExists = false
|
|
case *project.RoleAddedEvent:
|
|
if e.Aggregate().ResourceOwner != wm.ProjectResourceOwner {
|
|
continue
|
|
}
|
|
wm.ExistingRoleKeys = append(wm.ExistingRoleKeys, e.Key)
|
|
case *project.RoleRemovedEvent:
|
|
if e.Aggregate().ResourceOwner != wm.ProjectResourceOwner {
|
|
continue
|
|
}
|
|
wm.ExistingRoleKeys = slices.DeleteFunc(wm.ExistingRoleKeys, func(key string) bool {
|
|
return key == e.Key
|
|
})
|
|
case *org.OrgAddedEvent:
|
|
wm.GrantedOrgExists = true
|
|
case *org.OrgRemovedEvent:
|
|
wm.GrantedOrgExists = false
|
|
}
|
|
}
|
|
return wm.WriteModel.Reduce()
|
|
}
|
|
|
|
func (wm *ProjectGrantPreConditionReadModel) Query() *eventstore.SearchQueryBuilder {
|
|
query := eventstore.NewSearchQueryBuilder(eventstore.ColumnsEvent).
|
|
AddQuery().
|
|
AggregateTypes(project.AggregateType).
|
|
AggregateIDs(wm.ProjectID).
|
|
EventTypes(
|
|
project.ProjectAddedType,
|
|
project.ProjectRemovedType,
|
|
project.RoleAddedType,
|
|
project.RoleRemovedType).
|
|
Or().
|
|
AggregateTypes(org.AggregateType).
|
|
AggregateIDs(wm.GrantedOrgID).
|
|
EventTypes(
|
|
org.OrgAddedEventType,
|
|
org.OrgRemovedEventType).
|
|
Builder()
|
|
|
|
return query
|
|
}
|