fix: permissions (#698)

* get my zitadel permissions from memberships (not grants)

* change SearchMyProjectOrgs to user user_membership
This commit is contained in:
Livio Amstutz 2020-09-04 15:20:39 +02:00 committed by GitHub
parent a2aabe577a
commit cca92874ab
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 454 additions and 62 deletions

View File

@ -2,6 +2,7 @@ package eventstore
import (
"context"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/api/authz"
@ -11,6 +12,8 @@ import (
global_model "github.com/caos/zitadel/internal/model"
org_model "github.com/caos/zitadel/internal/org/model"
org_view_model "github.com/caos/zitadel/internal/org/repository/view/model"
user_model "github.com/caos/zitadel/internal/user/model"
user_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
grant_model "github.com/caos/zitadel/internal/usergrant/model"
"github.com/caos/zitadel/internal/usergrant/repository/view/model"
)
@ -63,6 +66,7 @@ func (repo *UserGrantRepo) SearchMyProjectOrgs(ctx context.Context, request *gra
if isAdmin {
return repo.SearchAdminOrgs(request)
}
return repo.searchZitadelOrgs(ctxData, request)
}
request.Queries = append(request.Queries, &grant_model.UserGrantSearchQuery{Key: grant_model.UserGrantSearchKeyProjectID, Method: global_model.SearchMethodEquals, Value: ctxData.ProjectID})
@ -73,35 +77,48 @@ func (repo *UserGrantRepo) SearchMyProjectOrgs(ctx context.Context, request *gra
if len(grants.Result) > 0 {
return grantRespToOrgResp(grants), nil
}
user, err := repo.View.UserByID(ctxData.UserID)
if err != nil {
return nil, err
return repo.userOrg(ctxData)
}
func membershipsToOrgResp(memberships []*user_view_model.UserMembershipView, count uint64) *grant_model.ProjectOrgSearchResponse {
orgs := make([]*grant_model.Org, 0, len(memberships))
for _, m := range memberships {
if !containsOrg(orgs, m.ResourceOwner) {
orgs = append(orgs, &grant_model.Org{OrgID: m.ResourceOwner, OrgName: m.ResourceOwnerName})
}
}
org, err := repo.View.OrgByID(user.ResourceOwner)
if err != nil {
return nil, err
return &grant_model.ProjectOrgSearchResponse{
TotalResult: count,
Result: orgs,
}
return &grant_model.ProjectOrgSearchResponse{Result: []*grant_model.Org{&grant_model.Org{
OrgID: org.ID,
OrgName: org.Name,
}}}, nil
}
func (repo *UserGrantRepo) SearchMyZitadelPermissions(ctx context.Context) ([]string, error) {
grant, err := repo.AuthZRepo.ResolveGrants(ctx)
ctxData := authz.GetCtxData(ctx)
memberships, count, err := repo.View.SearchUserMemberships(&user_model.UserMembershipSearchRequest{
Queries: []*user_model.UserMembershipSearchQuery{
{
Key: user_model.UserMembershipSearchKeyUserID,
Method: global_model.SearchMethodEquals,
Value: ctxData.UserID,
},
{
Key: user_model.UserMembershipSearchKeyResourceOwner,
Method: global_model.SearchMethodEquals,
Value: ctxData.OrgID,
},
},
})
if err != nil {
return nil, err
}
if grant == nil {
if count == 0 {
return []string{}, nil
}
permissions := &grant_model.Permissions{Permissions: []string{}}
for _, role := range grant.Roles {
roleName, ctxID := authz.SplitPermission(role)
for _, mapping := range repo.Auth.RolePermissionMappings {
if mapping.Role == roleName {
permissions.AppendPermissions(ctxID, mapping.Permissions...)
}
for _, membership := range memberships {
for _, role := range membership.Roles {
permissions = repo.mapRoleToPermission(permissions, membership, role)
}
}
return permissions.Permissions, nil
@ -159,6 +176,56 @@ func (repo *UserGrantRepo) UserGrantsByProjectAndUserID(projectID, userID string
return model.UserGrantsToModel(grants), nil
}
func (repo *UserGrantRepo) userOrg(ctxData authz.CtxData) (*grant_model.ProjectOrgSearchResponse, error) {
user, err := repo.View.UserByID(ctxData.UserID)
if err != nil {
return nil, err
}
org, err := repo.View.OrgByID(user.ResourceOwner)
if err != nil {
return nil, err
}
return &grant_model.ProjectOrgSearchResponse{Result: []*grant_model.Org{&grant_model.Org{
OrgID: org.ID,
OrgName: org.Name,
}}}, nil
}
func (repo *UserGrantRepo) searchZitadelOrgs(ctxData authz.CtxData, request *grant_model.UserGrantSearchRequest) (*grant_model.ProjectOrgSearchResponse, error) {
memberships, count, err := repo.View.SearchUserMemberships(&user_model.UserMembershipSearchRequest{
Offset: request.Offset,
Limit: request.Limit,
Asc: request.Asc,
Queries: []*user_model.UserMembershipSearchQuery{
{
Key: user_model.UserMembershipSearchKeyUserID,
Method: global_model.SearchMethodEquals,
Value: ctxData.UserID,
},
},
})
if err != nil {
return nil, err
}
if len(memberships) > 0 {
return membershipsToOrgResp(memberships, count), nil
}
return repo.userOrg(ctxData)
}
func (repo *UserGrantRepo) mapRoleToPermission(permissions *grant_model.Permissions, membership *user_view_model.UserMembershipView, role string) *grant_model.Permissions {
for _, mapping := range repo.Auth.RolePermissionMappings {
if mapping.Role == role {
ctxID := ""
if membership.MemberType == int32(user_model.MemberTypeProject) || membership.MemberType == int32(user_model.MemberTypeProjectGrant) {
ctxID = membership.ObjectID
}
permissions.AppendPermissions(ctxID, mapping.Permissions...)
}
}
return permissions
}
func grantRespToOrgResp(grants *grant_model.UserGrantSearchResponse) *grant_model.ProjectOrgSearchResponse {
resp := &grant_model.ProjectOrgSearchResponse{
TotalResult: grants.TotalResult,
@ -207,3 +274,12 @@ func addIamAdminRoles(orgRoles, iamAdminRoles []string) []string {
}
return result
}
func containsOrg(orgs []*grant_model.Org, resourceOwner string) bool {
for _, org := range orgs {
if org.OrgID == resourceOwner {
return true
}
}
return false
}

View File

@ -39,6 +39,7 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, ev
return []query.Handler{
&User{handler: handler{view, bulkLimit, configs.cycleDuration("User"), errorCount}, orgEvents: repos.OrgEvents},
&UserSession{handler: handler{view, bulkLimit, configs.cycleDuration("UserSession"), errorCount}, userEvents: repos.UserEvents},
&UserMembership{handler: handler{view, bulkLimit, configs.cycleDuration("UserMembership"), errorCount}, orgEvents: repos.OrgEvents, projectEvents: repos.ProjectEvents},
&Token{handler: handler{view, bulkLimit, configs.cycleDuration("Token"), errorCount}, ProjectEvents: repos.ProjectEvents},
&Key{handler: handler{view, bulkLimit, configs.cycleDuration("Key"), errorCount}},
&Application{handler: handler{view, bulkLimit, configs.cycleDuration("Application"), errorCount}},

View File

@ -260,10 +260,20 @@ func (u *UserGrant) processMember(event *models.Event, rolePrefix, roleSuffix st
proj_es_model.ProjectGrantMemberRemoved:
grant, err := u.view.UserGrantByIDs(event.ResourceOwner, u.iamProjectID, userID)
if err != nil {
if err != nil && !errors.IsNotFound(err) {
return err
}
return u.view.DeleteUserGrant(grant.ID, event.Sequence)
if errors.IsNotFound(err) {
return u.view.ProcessedUserGrantSequence(event.Sequence)
}
if roleSuffix != "" {
roleKeys = suffixRoles(roleSuffix, roleKeys)
}
if grant.RoleKeys == nil {
return u.view.ProcessedUserGrantSequence(event.Sequence)
}
grant.RoleKeys = mergeExistingRoles(rolePrefix, roleSuffix, grant.RoleKeys, nil)
return u.view.PutUserGrant(grant, event.Sequence)
default:
return u.view.ProcessedUserGrantSequence(event.Sequence)
}

View File

@ -0,0 +1,216 @@
package handler
import (
"context"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
org_model "github.com/caos/zitadel/internal/org/model"
org_event "github.com/caos/zitadel/internal/org/repository/eventsourcing"
proj_event "github.com/caos/zitadel/internal/project/repository/eventsourcing"
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/caos/logging"
"github.com/caos/zitadel/internal/eventstore/models"
es_models "github.com/caos/zitadel/internal/eventstore/models"
"github.com/caos/zitadel/internal/eventstore/spooler"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
usr_model "github.com/caos/zitadel/internal/user/model"
usr_es_model "github.com/caos/zitadel/internal/user/repository/view/model"
)
type UserMembership struct {
handler
orgEvents *org_event.OrgEventstore
projectEvents *proj_event.ProjectEventstore
}
const (
userMembershipTable = "auth.user_memberships"
)
func (m *UserMembership) ViewModel() string {
return userMembershipTable
}
func (m *UserMembership) EventQuery() (*models.SearchQuery, error) {
sequence, err := m.view.GetLatestUserMembershipSequence()
if err != nil {
return nil, err
}
return es_models.NewSearchQuery().
AggregateTypeFilter(iam_es_model.IAMAggregate, org_es_model.OrgAggregate, proj_es_model.ProjectAggregate).
LatestSequenceFilter(sequence.CurrentSequence), nil
}
func (m *UserMembership) Reduce(event *models.Event) (err error) {
switch event.AggregateType {
case iam_es_model.IAMAggregate:
err = m.processIam(event)
case org_es_model.OrgAggregate:
err = m.processOrg(event)
case proj_es_model.ProjectAggregate:
err = m.processProject(event)
}
return err
}
func (m *UserMembership) processIam(event *models.Event) (err error) {
member := new(usr_es_model.UserMembershipView)
err = member.AppendEvent(event)
if err != nil {
return err
}
switch event.Type {
case iam_es_model.IAMMemberAdded:
m.fillIamDisplayName(member)
case iam_es_model.IAMMemberChanged:
member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam)
if err != nil {
return err
}
err = member.AppendEvent(event)
case iam_es_model.IAMMemberRemoved:
return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeIam, event.Sequence)
default:
return m.view.ProcessedUserMembershipSequence(event.Sequence)
}
if err != nil {
return err
}
return m.view.PutUserMembership(member, event.Sequence)
}
func (m *UserMembership) fillIamDisplayName(member *usr_es_model.UserMembershipView) {
member.DisplayName = member.AggregateID
member.ResourceOwnerName = member.ResourceOwner
}
func (m *UserMembership) processOrg(event *models.Event) (err error) {
member := new(usr_es_model.UserMembershipView)
err = member.AppendEvent(event)
if err != nil {
return err
}
switch event.Type {
case org_es_model.OrgMemberAdded:
err = m.fillOrgName(member)
case org_es_model.OrgMemberChanged:
member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeOrganisation)
if err != nil {
return err
}
err = member.AppendEvent(event)
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)
default:
return m.view.ProcessedUserMembershipSequence(event.Sequence)
}
if err != nil {
return err
}
return m.view.PutUserMembership(member, event.Sequence)
}
func (m *UserMembership) fillOrgName(member *usr_es_model.UserMembershipView) (err error) {
org, err := m.orgEvents.OrgByID(context.Background(), org_model.NewOrg(member.ResourceOwner))
if err != nil {
return err
}
member.ResourceOwnerName = org.Name
if member.AggregateID == org.AggregateID {
member.DisplayName = org.Name
}
return nil
}
func (m *UserMembership) updateOrgName(event *models.Event) error {
org, err := m.orgEvents.OrgByID(context.Background(), org_model.NewOrg(event.AggregateID))
if err != nil {
return err
}
memberships, err := m.view.UserMembershipsByResourceOwner(event.ResourceOwner)
if err != nil {
return err
}
for _, membership := range memberships {
membership.ResourceOwnerName = org.Name
if membership.AggregateID == event.AggregateID {
membership.DisplayName = org.Name
}
}
return m.view.BulkPutUserMemberships(memberships, event.Sequence)
}
func (m *UserMembership) processProject(event *models.Event) (err error) {
member := new(usr_es_model.UserMembershipView)
err = member.AppendEvent(event)
if err != nil {
return err
}
switch event.Type {
case proj_es_model.ProjectMemberAdded, proj_es_model.ProjectGrantMemberAdded:
err = m.fillProjectDisplayName(member)
if err != nil {
return err
}
err = m.fillOrgName(member)
case proj_es_model.ProjectMemberChanged:
member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject)
if err != nil {
return err
}
err = member.AppendEvent(event)
case proj_es_model.ProjectMemberRemoved:
return m.view.DeleteUserMembership(member.UserID, event.AggregateID, event.AggregateID, usr_model.MemberTypeProject, event.Sequence)
case proj_es_model.ProjectGrantMemberChanged:
member, err = m.view.UserMembershipByIDs(member.UserID, event.AggregateID, member.ObjectID, usr_model.MemberTypeProjectGrant)
if err != nil {
return err
}
err = member.AppendEvent(event)
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)
default:
return m.view.ProcessedUserMembershipSequence(event.Sequence)
}
if err != nil {
return err
}
return m.view.PutUserMembership(member, event.Sequence)
}
func (m *UserMembership) fillProjectDisplayName(member *usr_es_model.UserMembershipView) (err error) {
project, err := m.projectEvents.ProjectByID(context.Background(), member.AggregateID)
if err != nil {
return err
}
member.DisplayName = project.Name
return nil
}
func (m *UserMembership) updateProjectDisplayName(event *models.Event) error {
project, err := m.projectEvents.ProjectByID(context.Background(), event.AggregateID)
if err != nil {
return err
}
memberships, err := m.view.UserMembershipsByAggregateID(event.AggregateID)
if err != nil {
return err
}
for _, membership := range memberships {
membership.DisplayName = project.Name
}
return m.view.BulkPutUserMemberships(memberships, event.Sequence)
}
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")
return spooler.HandleError(event, err, m.view.GetLatestUserMembershipFailedEvent, m.view.ProcessedUserMembershipFailedEvent, m.view.ProcessedUserMembershipSequence, m.errorCountUntilSkip)
}

View File

@ -0,0 +1,68 @@
package view
import (
usr_model "github.com/caos/zitadel/internal/user/model"
"github.com/caos/zitadel/internal/user/repository/view"
"github.com/caos/zitadel/internal/user/repository/view/model"
"github.com/caos/zitadel/internal/view/repository"
)
const (
userMembershipTable = "auth.user_memberships"
)
func (v *View) UserMembershipByIDs(userID, aggregateID, objectID string, memberType usr_model.MemberType) (*model.UserMembershipView, error) {
return view.UserMembershipByIDs(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType)
}
func (v *View) UserMembershipsByAggregateID(aggregateID string) ([]*model.UserMembershipView, error) {
return view.UserMembershipsByAggregateID(v.Db, userMembershipTable, aggregateID)
}
func (v *View) UserMembershipsByResourceOwner(resourceOwner string) ([]*model.UserMembershipView, error) {
return view.UserMembershipsByResourceOwner(v.Db, userMembershipTable, resourceOwner)
}
func (v *View) SearchUserMemberships(request *usr_model.UserMembershipSearchRequest) ([]*model.UserMembershipView, uint64, error) {
return view.SearchUserMemberships(v.Db, userMembershipTable, request)
}
func (v *View) PutUserMembership(membership *model.UserMembershipView, sequence uint64) error {
err := view.PutUserMembership(v.Db, userMembershipTable, membership)
if err != nil {
return err
}
return v.ProcessedUserMembershipSequence(sequence)
}
func (v *View) BulkPutUserMemberships(memberships []*model.UserMembershipView, sequence uint64) error {
err := view.PutUserMemberships(v.Db, userTable, memberships...)
if err != nil {
return err
}
return v.ProcessedUserMembershipSequence(sequence)
}
func (v *View) DeleteUserMembership(userID, aggregateID, objectID string, memberType usr_model.MemberType, eventSequence uint64) error {
err := view.DeleteUserMembership(v.Db, userMembershipTable, userID, aggregateID, objectID, memberType)
if err != nil {
return nil
}
return v.ProcessedUserMembershipSequence(eventSequence)
}
func (v *View) GetLatestUserMembershipSequence() (*repository.CurrentSequence, error) {
return v.latestSequence(userMembershipTable)
}
func (v *View) ProcessedUserMembershipSequence(eventSequence uint64) error {
return v.saveCurrentSequence(userMembershipTable, eventSequence)
}
func (v *View) GetLatestUserMembershipFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
return v.latestFailedEvent(userMembershipTable, sequence)
}
func (v *View) ProcessedUserMembershipFailedEvent(failedEvent *repository.FailedEvent) error {
return v.saveFailedEvent(failedEvent)
}

View File

@ -13,12 +13,13 @@ type UserMembershipView struct {
//ObjectID differs from aggregate id if obejct is sub of an aggregate
ObjectID string
Roles []string
DisplayName string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
Sequence uint64
Roles []string
DisplayName string
CreationDate time.Time
ChangeDate time.Time
ResourceOwner string
ResourceOwnerName string
Sequence uint64
}
type MemberType int32

View File

@ -5,13 +5,14 @@ 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"
iam_es_model "github.com/caos/zitadel/internal/iam/repository/eventsourcing/model"
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
"github.com/caos/zitadel/internal/user/model"
"github.com/lib/pq"
)
const (
@ -28,41 +29,28 @@ type UserMembershipView struct {
AggregateID string `json:"-" gorm:"column:aggregate_id;primary_key"`
ObjectID string `json:"-" gorm:"column:object_id;primary_key"`
Roles pq.StringArray `json:"-" gorm:"column:roles"`
DisplayName string `json:"-" gorm:"column:display_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
func UserMembershipFromModel(membership *model.UserMembershipView) *UserMembershipView {
return &UserMembershipView{
UserID: membership.UserID,
MemberType: int32(membership.MemberType),
AggregateID: membership.AggregateID,
ObjectID: membership.ObjectID,
Roles: membership.Roles,
DisplayName: membership.DisplayName,
ChangeDate: membership.ChangeDate,
CreationDate: membership.CreationDate,
ResourceOwner: membership.ResourceOwner,
Sequence: membership.Sequence,
}
Roles pq.StringArray `json:"-" gorm:"column:roles"`
DisplayName string `json:"-" gorm:"column:display_name"`
CreationDate time.Time `json:"-" gorm:"column:creation_date"`
ChangeDate time.Time `json:"-" gorm:"column:change_date"`
ResourceOwner string `json:"-" gorm:"column:resource_owner"`
ResourceOwnerName string `json:"-" gorm:"column:resource_owner_name"`
Sequence uint64 `json:"-" gorm:"column:sequence"`
}
func UserMembershipToModel(membership *UserMembershipView) *model.UserMembershipView {
return &model.UserMembershipView{
UserID: membership.UserID,
MemberType: model.MemberType(membership.MemberType),
AggregateID: membership.AggregateID,
ObjectID: membership.ObjectID,
Roles: membership.Roles,
DisplayName: membership.DisplayName,
ChangeDate: membership.ChangeDate,
CreationDate: membership.CreationDate,
ResourceOwner: membership.ResourceOwner,
Sequence: membership.Sequence,
UserID: membership.UserID,
MemberType: model.MemberType(membership.MemberType),
AggregateID: membership.AggregateID,
ObjectID: membership.ObjectID,
Roles: membership.Roles,
DisplayName: membership.DisplayName,
ChangeDate: membership.ChangeDate,
CreationDate: membership.CreationDate,
ResourceOwner: membership.ResourceOwner,
ResourceOwnerName: membership.ResourceOwnerName,
Sequence: membership.Sequence,
}
}
@ -82,22 +70,26 @@ func (u *UserMembershipView) AppendEvent(event *models.Event) (err error) {
case iam_es_model.IAMMemberAdded:
u.setRootData(event, model.MemberTypeIam)
err = u.setIamMemberData(event)
case iam_es_model.IAMMemberChanged:
case iam_es_model.IAMMemberChanged,
iam_es_model.IAMMemberRemoved:
err = u.setIamMemberData(event)
case org_es_model.OrgMemberAdded:
u.setRootData(event, model.MemberTypeOrganisation)
err = u.setOrgMemberData(event)
case org_es_model.OrgMemberChanged:
case org_es_model.OrgMemberChanged,
org_es_model.OrgMemberRemoved:
err = u.setOrgMemberData(event)
case proj_es_model.ProjectMemberAdded:
u.setRootData(event, model.MemberTypeProject)
err = u.setProjectMemberData(event)
case proj_es_model.ProjectMemberChanged:
case proj_es_model.ProjectMemberChanged,
proj_es_model.ProjectMemberRemoved:
err = u.setProjectMemberData(event)
case proj_es_model.ProjectGrantMemberAdded:
u.setRootData(event, model.MemberTypeProjectGrant)
err = u.setProjectMemberData(event)
case proj_es_model.ProjectGrantMemberChanged:
case proj_es_model.ProjectGrantMemberChanged,
proj_es_model.ProjectGrantMemberRemoved:
err = u.setProjectMemberData(event)
}
return err

View File

@ -35,6 +35,16 @@ func UserMembershipsByAggregateID(db *gorm.DB, table, aggregateID string) ([]*mo
return memberships, err
}
func UserMembershipsByResourceOwner(db *gorm.DB, table, resourceOwner string) ([]*model.UserMembershipView, error) {
memberships := make([]*model.UserMembershipView, 0)
aggregateIDQuery := &usr_model.UserMembershipSearchQuery{Key: usr_model.UserMembershipSearchKeyResourceOwner, Value: resourceOwner, Method: global_model.SearchMethodEquals}
query := repository.PrepareSearchQuery(table, model.UserMembershipSearchRequest{
Queries: []*usr_model.UserMembershipSearchQuery{aggregateIDQuery},
})
_, err := query(db, &memberships)
return memberships, err
}
func SearchUserMemberships(db *gorm.DB, table string, req *usr_model.UserMembershipSearchRequest) ([]*model.UserMembershipView, uint64, error) {
users := make([]*model.UserMembershipView, 0)
query := repository.PrepareSearchQuery(table, model.UserMembershipSearchRequest{Limit: req.Limit, Offset: req.Offset, Queries: req.Queries})

View File

@ -0,0 +1,18 @@
CREATE TABLE auth.user_memberships (
user_id TEXT,
member_type SMALLINT,
aggregate_id TEXT,
object_id TEXT,
roles TEXT ARRAY,
display_name TEXT,
resource_owner TEXT,
resource_owner_name TEXT,
creation_date TIMESTAMPTZ,
change_date TIMESTAMPTZ,
sequence BIGINT,
PRIMARY KEY (user_id, member_type, aggregate_id, object_id)
);
ALTER TABLE management.user_memberships ADD COLUMN resource_owner_name TEXT;