mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-23 11:38:19 +00:00
feat(queries): user membership (#2768)
* refactor(domain): add user type * fix(projections): start with login names * fix(login_policy): correct handling of user domain claimed event * fix(projections): add members * refactor: simplify member projections * add migration for members * add metadata to member projections * refactor: login name projection * fix: set correct suffixes on login name projections * test(projections): login name reduces * fix: correct cols in reduce member * test(projections): org, iam, project members * member additional cols and conds as opt, add project grant members * fix(migration): members * fix(migration): correct database name * migration version * migs * better naming for member cond and col * split project and project grant members * prepare member columns * feat(queries): membership query * test(queries): membership prepare * fix(queries): multiple projections for latest sequence * fix(api): use query for membership queries in auth and management * fix(query): member queries and user avatar column * member cols * fix(queries): membership stmt * fix user test * fix user test
This commit is contained in:
parent
2cdb297138
commit
2265fffd8e
@ -29,18 +29,18 @@ func (s *Server) ListMyProjectPermissions(ctx context.Context, _ *auth_pb.ListMy
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ListMyMemberships(ctx context.Context, req *auth_pb.ListMyMembershipsRequest) (*auth_pb.ListMyMembershipsResponse, error) {
|
func (s *Server) ListMyMemberships(ctx context.Context, req *auth_pb.ListMyMembershipsRequest) (*auth_pb.ListMyMembershipsResponse, error) {
|
||||||
request, err := ListMyMembershipsRequestToModel(req)
|
request, err := ListMyMembershipsRequestToModel(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
response, err := s.repo.SearchMyUserMemberships(ctx, request)
|
response, err := s.query.Memberships(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &auth_pb.ListMyMembershipsResponse{
|
return &auth_pb.ListMyMembershipsResponse{
|
||||||
Result: user_grpc.MembershipsToMembershipsPb(response.Result),
|
Result: user_grpc.MembershipsToMembershipsPb(response.Memberships),
|
||||||
Details: obj_grpc.ToListDetails(
|
Details: obj_grpc.ToListDetails(
|
||||||
response.TotalResult,
|
response.Count,
|
||||||
response.Sequence,
|
response.Sequence,
|
||||||
response.Timestamp,
|
response.Timestamp,
|
||||||
),
|
),
|
||||||
|
@ -1,23 +1,33 @@
|
|||||||
package auth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||||
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
||||||
user_model "github.com/caos/zitadel/internal/user/model"
|
"github.com/caos/zitadel/internal/query"
|
||||||
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
|
auth_pb "github.com/caos/zitadel/pkg/grpc/auth"
|
||||||
)
|
)
|
||||||
|
|
||||||
func ListMyMembershipsRequestToModel(req *auth_pb.ListMyMembershipsRequest) (*user_model.UserMembershipSearchRequest, error) {
|
func ListMyMembershipsRequestToModel(ctx context.Context, req *auth_pb.ListMyMembershipsRequest) (*query.MembershipSearchQuery, error) {
|
||||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||||
queries, err := user_grpc.MembershipQueriesToModel(req.Queries)
|
queries, err := user_grpc.MembershipQueriesToQuery(req.Queries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &user_model.UserMembershipSearchRequest{
|
userQuery, err := query.NewMembershipUserIDQuery(authz.GetCtxData(ctx).UserID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
queries = append(queries, userQuery)
|
||||||
|
return &query.MembershipSearchQuery{
|
||||||
|
SearchRequest: query.SearchRequest{
|
||||||
Offset: offset,
|
Offset: offset,
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
Asc: asc,
|
Asc: asc,
|
||||||
//SortingColumn: //TODO: sorting
|
//SortingColumn: //TODO: sorting
|
||||||
|
},
|
||||||
Queries: queries,
|
Queries: queries,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/api/authz"
|
"github.com/caos/zitadel/internal/api/authz"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/change"
|
"github.com/caos/zitadel/internal/api/grpc/change"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/metadata"
|
"github.com/caos/zitadel/internal/api/grpc/metadata"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
|
||||||
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
||||||
"github.com/caos/zitadel/internal/api/grpc/org"
|
"github.com/caos/zitadel/internal/api/grpc/org"
|
||||||
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
||||||
@ -102,7 +101,7 @@ func (s *Server) UpdateMyUserName(ctx context.Context, req *auth_pb.UpdateMyUser
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &auth_pb.UpdateMyUserNameResponse{
|
return &auth_pb.UpdateMyUserNameResponse{
|
||||||
Details: object.DomainToChangeDetailsPb(objectDetails),
|
Details: obj_grpc.DomainToChangeDetailsPb(objectDetails),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -121,7 +120,7 @@ func (s *Server) ListMyUserGrants(ctx context.Context, req *auth_pb.ListMyUserGr
|
|||||||
}
|
}
|
||||||
return &auth_pb.ListMyUserGrantsResponse{
|
return &auth_pb.ListMyUserGrantsResponse{
|
||||||
Result: UserGrantsToPb(res.Result),
|
Result: UserGrantsToPb(res.Result),
|
||||||
Details: object.ToListDetails(
|
Details: obj_grpc.ToListDetails(
|
||||||
res.TotalResult,
|
res.TotalResult,
|
||||||
res.Sequence,
|
res.Sequence,
|
||||||
res.Timestamp,
|
res.Timestamp,
|
||||||
@ -140,13 +139,13 @@ func (s *Server) ListMyProjectOrgs(ctx context.Context, req *auth_pb.ListMyProje
|
|||||||
}
|
}
|
||||||
return &auth_pb.ListMyProjectOrgsResponse{
|
return &auth_pb.ListMyProjectOrgsResponse{
|
||||||
//TODO: not all details
|
//TODO: not all details
|
||||||
Details: object.ToListDetails(res.TotalResult, 0, time.Time{}),
|
Details: obj_grpc.ToListDetails(res.TotalResult, 0, time.Time{}),
|
||||||
Result: org.OrgsToPb(res.Result),
|
Result: org.OrgsToPb(res.Result),
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListMyProjectOrgsRequestToModel(req *auth_pb.ListMyProjectOrgsRequest) (*grant_model.UserGrantSearchRequest, error) {
|
func ListMyProjectOrgsRequestToModel(req *auth_pb.ListMyProjectOrgsRequest) (*grant_model.UserGrantSearchRequest, error) {
|
||||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
offset, limit, asc := obj_grpc.ListQueryToModel(req.Query)
|
||||||
queries, err := org.OrgQueriesToUserGrantModel(req.Queries)
|
queries, err := org.OrgQueriesToUserGrantModel(req.Queries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@ -634,18 +634,18 @@ func (s *Server) RemoveHumanLinkedIDP(ctx context.Context, req *mgmt_pb.RemoveHu
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (s *Server) ListUserMemberships(ctx context.Context, req *mgmt_pb.ListUserMembershipsRequest) (*mgmt_pb.ListUserMembershipsResponse, error) {
|
func (s *Server) ListUserMemberships(ctx context.Context, req *mgmt_pb.ListUserMembershipsRequest) (*mgmt_pb.ListUserMembershipsResponse, error) {
|
||||||
request, err := ListUserMembershipsRequestToModel(req)
|
request, err := ListUserMembershipsRequestToModel(ctx, req)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
response, err := s.user.SearchUserMemberships(ctx, request)
|
response, err := s.query.Memberships(ctx, request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return &mgmt_pb.ListUserMembershipsResponse{
|
return &mgmt_pb.ListUserMembershipsResponse{
|
||||||
Result: user_grpc.MembershipsToMembershipsPb(response.Result),
|
Result: user_grpc.MembershipsToMembershipsPb(response.Memberships),
|
||||||
Details: obj_grpc.ToListDetails(
|
Details: obj_grpc.ToListDetails(
|
||||||
response.TotalResult,
|
response.Count,
|
||||||
response.Sequence,
|
response.Sequence,
|
||||||
response.Timestamp,
|
response.Timestamp,
|
||||||
),
|
),
|
||||||
|
@ -255,21 +255,27 @@ func ListHumanLinkedIDPsRequestToQuery(ctx context.Context, req *mgmt_pb.ListHum
|
|||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func ListUserMembershipsRequestToModel(req *mgmt_pb.ListUserMembershipsRequest) (*user_model.UserMembershipSearchRequest, error) {
|
func ListUserMembershipsRequestToModel(ctx context.Context, req *mgmt_pb.ListUserMembershipsRequest) (*query.MembershipSearchQuery, error) {
|
||||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||||
queries, err := user_grpc.MembershipQueriesToModel(req.Queries)
|
queries, err := user_grpc.MembershipQueriesToQuery(req.Queries)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
queries = append(queries, &user_model.UserMembershipSearchQuery{
|
userQuery, err := query.NewMembershipUserIDQuery(req.UserId)
|
||||||
Key: user_model.UserMembershipSearchKeyUserID,
|
if err != nil {
|
||||||
Method: domain.SearchMethodEquals,
|
return nil, err
|
||||||
Value: req.UserId,
|
}
|
||||||
})
|
ownerQuery, err := query.NewMembershipResourceOwnerQuery(authz.GetCtxData(ctx).OrgID)
|
||||||
return &user_model.UserMembershipSearchRequest{
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
queries = append(queries, userQuery, ownerQuery)
|
||||||
|
return &query.MembershipSearchQuery{
|
||||||
|
SearchRequest: query.SearchRequest{
|
||||||
Offset: offset,
|
Offset: offset,
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
Asc: asc,
|
Asc: asc,
|
||||||
|
},
|
||||||
//SortingColumn: //TODO: sorting
|
//SortingColumn: //TODO: sorting
|
||||||
Queries: queries,
|
Queries: queries,
|
||||||
}, nil
|
}, nil
|
||||||
|
@ -124,7 +124,7 @@ func TextMethodToQuery(method object_pb.TextQueryMethod) query.TextComparison {
|
|||||||
|
|
||||||
func ListQueryToModel(query *object_pb.ListQuery) (offset, limit uint64, asc bool) {
|
func ListQueryToModel(query *object_pb.ListQuery) (offset, limit uint64, asc bool) {
|
||||||
if query == nil {
|
if query == nil {
|
||||||
return
|
return 0, 0, false
|
||||||
}
|
}
|
||||||
return query.Offset, uint64(query.Limit), query.Asc
|
return query.Offset, uint64(query.Limit), query.Asc
|
||||||
}
|
}
|
||||||
|
@ -4,34 +4,35 @@ import (
|
|||||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||||
"github.com/caos/zitadel/internal/domain"
|
"github.com/caos/zitadel/internal/domain"
|
||||||
"github.com/caos/zitadel/internal/errors"
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/query"
|
||||||
user_model "github.com/caos/zitadel/internal/user/model"
|
user_model "github.com/caos/zitadel/internal/user/model"
|
||||||
user_pb "github.com/caos/zitadel/pkg/grpc/user"
|
user_pb "github.com/caos/zitadel/pkg/grpc/user"
|
||||||
)
|
)
|
||||||
|
|
||||||
func MembershipQueriesToModel(queries []*user_pb.MembershipQuery) (_ []*user_model.UserMembershipSearchQuery, err error) {
|
func MembershipQueriesToQuery(queries []*user_pb.MembershipQuery) (_ []query.SearchQuery, err error) {
|
||||||
q := make([]*user_model.UserMembershipSearchQuery, 0)
|
q := make([]query.SearchQuery, 0)
|
||||||
for _, query := range queries {
|
for _, query := range queries {
|
||||||
qs, err := MembershipQueryToModel(query)
|
qs, err := MembershipQueryToQuery(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
q = append(q, qs...)
|
q = append(q, qs)
|
||||||
}
|
}
|
||||||
return q, nil
|
return q, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func MembershipQueryToModel(query *user_pb.MembershipQuery) ([]*user_model.UserMembershipSearchQuery, error) {
|
func MembershipQueryToQuery(req *user_pb.MembershipQuery) (query.SearchQuery, error) {
|
||||||
switch q := query.Query.(type) {
|
switch q := req.Query.(type) {
|
||||||
case *user_pb.MembershipQuery_OrgQuery:
|
case *user_pb.MembershipQuery_OrgQuery:
|
||||||
return MembershipOrgQueryToModel(q.OrgQuery), nil
|
return query.NewMembershipOrgIDQuery(q.OrgQuery.OrgId)
|
||||||
case *user_pb.MembershipQuery_ProjectQuery:
|
case *user_pb.MembershipQuery_ProjectQuery:
|
||||||
return MembershipProjectQueryToModel(q.ProjectQuery), nil
|
return query.NewMembershipProjectIDQuery(q.ProjectQuery.ProjectId)
|
||||||
case *user_pb.MembershipQuery_ProjectGrantQuery:
|
case *user_pb.MembershipQuery_ProjectGrantQuery:
|
||||||
return MembershipProjectGrantQueryToModel(q.ProjectGrantQuery), nil
|
return query.NewMembershipProjectGrantIDQuery(q.ProjectGrantQuery.ProjectGrantId)
|
||||||
case *user_pb.MembershipQuery_IamQuery:
|
case *user_pb.MembershipQuery_IamQuery:
|
||||||
return MembershipIAMQueryToModel(q.IamQuery), nil
|
return query.NewMembershipIsIAMQuery()
|
||||||
default:
|
default:
|
||||||
return nil, errors.ThrowInvalidArgument(nil, "USER-dsg3z", "List.Query.Invalid")
|
return nil, errors.ThrowInvalidArgument(nil, "USER-dsg3z", "Errors.List.Query.Invalid")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +92,7 @@ func MembershipProjectGrantQueryToModel(q *user_pb.MembershipProjectGrantQuery)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func MembershipsToMembershipsPb(memberships []*user_model.UserMembershipView) []*user_pb.Membership {
|
func MembershipsToMembershipsPb(memberships []*query.Membership) []*user_pb.Membership {
|
||||||
converted := make([]*user_pb.Membership, len(memberships))
|
converted := make([]*user_pb.Membership, len(memberships))
|
||||||
for i, membership := range memberships {
|
for i, membership := range memberships {
|
||||||
converted[i] = MembershipToMembershipPb(membership)
|
converted[i] = MembershipToMembershipPb(membership)
|
||||||
@ -99,7 +100,7 @@ func MembershipsToMembershipsPb(memberships []*user_model.UserMembershipView) []
|
|||||||
return converted
|
return converted
|
||||||
}
|
}
|
||||||
|
|
||||||
func MembershipToMembershipPb(membership *user_model.UserMembershipView) *user_pb.Membership {
|
func MembershipToMembershipPb(membership *query.Membership) *user_pb.Membership {
|
||||||
return &user_pb.Membership{
|
return &user_pb.Membership{
|
||||||
UserId: membership.UserID,
|
UserId: membership.UserID,
|
||||||
Type: memberTypeToPb(membership),
|
Type: memberTypeToPb(membership),
|
||||||
@ -114,25 +115,23 @@ func MembershipToMembershipPb(membership *user_model.UserMembershipView) *user_p
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func memberTypeToPb(membership *user_model.UserMembershipView) user_pb.MembershipType {
|
func memberTypeToPb(membership *query.Membership) user_pb.MembershipType {
|
||||||
switch membership.MemberType {
|
if membership.Org != nil {
|
||||||
case user_model.MemberTypeOrganisation:
|
|
||||||
return &user_pb.Membership_OrgId{
|
return &user_pb.Membership_OrgId{
|
||||||
OrgId: membership.AggregateID,
|
OrgId: membership.Org.OrgID,
|
||||||
}
|
}
|
||||||
case user_model.MemberTypeProject:
|
} else if membership.Project != nil {
|
||||||
return &user_pb.Membership_ProjectId{
|
return &user_pb.Membership_ProjectId{
|
||||||
ProjectId: membership.AggregateID,
|
ProjectId: membership.Project.ProjectID,
|
||||||
}
|
}
|
||||||
case user_model.MemberTypeProjectGrant:
|
} else if membership.ProjectGrant != nil {
|
||||||
return &user_pb.Membership_ProjectGrantId{
|
return &user_pb.Membership_ProjectGrantId{
|
||||||
ProjectGrantId: membership.ObjectID,
|
ProjectGrantId: membership.ProjectGrant.GrantID,
|
||||||
}
|
}
|
||||||
case user_model.MemberTypeIam:
|
} else if membership.IAM != nil {
|
||||||
return &user_pb.Membership_Iam{
|
return &user_pb.Membership_Iam{
|
||||||
Iam: true, //TODO: ?
|
Iam: true,
|
||||||
}
|
}
|
||||||
default:
|
|
||||||
return nil //TODO: ?
|
|
||||||
}
|
}
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
@ -38,11 +38,16 @@ func prepareLatestSequence() (sq.SelectBuilder, func(*sql.Row) (*LatestSequence,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) latestSequence(ctx context.Context, projection table) (*LatestSequence, error) {
|
func (q *Queries) latestSequence(ctx context.Context, projections ...table) (*LatestSequence, error) {
|
||||||
query, scan := prepareLatestSequence()
|
query, scan := prepareLatestSequence()
|
||||||
stmt, args, err := query.Where(sq.Eq{
|
or := make(sq.Or, len(projections))
|
||||||
CurrentSequenceColProjectionName.identifier(): projection.name,
|
for i, projection := range projections {
|
||||||
}).ToSql()
|
or[i] = sq.Eq{CurrentSequenceColProjectionName.identifier(): projection.name}
|
||||||
|
}
|
||||||
|
stmt, args, err := query.
|
||||||
|
Where(or).
|
||||||
|
OrderBy(CurrentSequenceColCurrentSequence.identifier()).
|
||||||
|
ToSql()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.ThrowInternal(err, "QUERY-5CfX9", "Errors.Query.SQLStatement")
|
return nil, errors.ThrowInternal(err, "QUERY-5CfX9", "Errors.Query.SQLStatement")
|
||||||
}
|
}
|
||||||
|
38
internal/query/iam_member.go
Normal file
38
internal/query/iam_member.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import "github.com/caos/zitadel/internal/query/projection"
|
||||||
|
|
||||||
|
var (
|
||||||
|
iamMemberTable = table{
|
||||||
|
name: projection.IAMMemberProjectionTable,
|
||||||
|
alias: "members",
|
||||||
|
}
|
||||||
|
IAMMemberUserID = Column{
|
||||||
|
name: projection.MemberUserIDCol,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
IAMMemberRoles = Column{
|
||||||
|
name: projection.MemberRolesCol,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
IAMMemberCreationDate = Column{
|
||||||
|
name: projection.MemberCreationDate,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
IAMMemberChangeDate = Column{
|
||||||
|
name: projection.MemberChangeDate,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
IAMMemberSequence = Column{
|
||||||
|
name: projection.MemberSequence,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
IAMMemberResourceOwner = Column{
|
||||||
|
name: projection.MemberResourceOwner,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
IAMMemberIAMID = Column{
|
||||||
|
name: projection.IAMMemberIAMIDCol,
|
||||||
|
table: iamMemberTable,
|
||||||
|
}
|
||||||
|
)
|
@ -2,9 +2,67 @@ package query
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
|
||||||
|
"github.com/caos/zitadel/internal/query/projection"
|
||||||
"github.com/caos/zitadel/internal/telemetry/tracing"
|
"github.com/caos/zitadel/internal/telemetry/tracing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
type MembersQuery struct {
|
||||||
|
SearchRequest
|
||||||
|
Queries []SearchQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *MembersQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||||
|
query = q.SearchRequest.toQuery(query)
|
||||||
|
for _, q := range q.Queries {
|
||||||
|
query = q.toQuery(query)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemberEmailSearchQuery(method TextComparison, value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(HumanEmailCol, value, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemberFirstNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(HumanFirstNameCol, value, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemberLastNameSearchQuery(method TextComparison, value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(HumanLastNameCol, value, method)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMemberUserIDSearchQuery(value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(memberUserID, value, TextEquals)
|
||||||
|
}
|
||||||
|
func NewMemberResourceOwnerSearchQuery(value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(memberResourceOwner, value, TextEquals)
|
||||||
|
}
|
||||||
|
|
||||||
|
type Members struct {
|
||||||
|
SearchResponse
|
||||||
|
Members []*Member
|
||||||
|
}
|
||||||
|
|
||||||
|
type Member struct {
|
||||||
|
CreationDate time.Time
|
||||||
|
ChangeDate time.Time
|
||||||
|
Sequence uint64
|
||||||
|
ResourceOwner string
|
||||||
|
|
||||||
|
UserID string
|
||||||
|
Roles []string
|
||||||
|
PreferredLoginName string
|
||||||
|
Email string
|
||||||
|
FirstName string
|
||||||
|
LastName string
|
||||||
|
DisplayName string
|
||||||
|
AvatarURL string
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Queries) IAMMemberByID(ctx context.Context, iamID, userID string) (member *IAMMemberReadModel, err error) {
|
func (r *Queries) IAMMemberByID(ctx context.Context, iamID, userID string) (member *IAMMemberReadModel, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
@ -17,3 +75,18 @@ func (r *Queries) IAMMemberByID(ctx context.Context, iamID, userID string) (memb
|
|||||||
|
|
||||||
return member, nil
|
return member, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
memberTableAlias = table{
|
||||||
|
name: "members",
|
||||||
|
alias: "members",
|
||||||
|
}
|
||||||
|
memberUserID = Column{
|
||||||
|
name: projection.MemberUserIDCol,
|
||||||
|
table: memberTableAlias,
|
||||||
|
}
|
||||||
|
memberResourceOwner = Column{
|
||||||
|
name: projection.MemberResourceOwner,
|
||||||
|
table: memberTableAlias,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
38
internal/query/org_member.go
Normal file
38
internal/query/org_member.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import "github.com/caos/zitadel/internal/query/projection"
|
||||||
|
|
||||||
|
var (
|
||||||
|
orgMemberTable = table{
|
||||||
|
name: projection.OrgMemberProjectionTable,
|
||||||
|
alias: "members",
|
||||||
|
}
|
||||||
|
OrgMemberUserID = Column{
|
||||||
|
name: projection.MemberUserIDCol,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
OrgMemberRoles = Column{
|
||||||
|
name: projection.MemberRolesCol,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
OrgMemberCreationDate = Column{
|
||||||
|
name: projection.MemberCreationDate,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
OrgMemberChangeDate = Column{
|
||||||
|
name: projection.MemberChangeDate,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
OrgMemberSequence = Column{
|
||||||
|
name: projection.MemberSequence,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
OrgMemberResourceOwner = Column{
|
||||||
|
name: projection.MemberResourceOwner,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
OrgMemberOrgID = Column{
|
||||||
|
name: projection.OrgMemberOrgIDCol,
|
||||||
|
table: orgMemberTable,
|
||||||
|
}
|
||||||
|
)
|
42
internal/query/project_grant_member.go
Normal file
42
internal/query/project_grant_member.go
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import "github.com/caos/zitadel/internal/query/projection"
|
||||||
|
|
||||||
|
var (
|
||||||
|
projectGrantMemberTable = table{
|
||||||
|
name: projection.ProjectGrantMemberProjectionTable,
|
||||||
|
alias: "members",
|
||||||
|
}
|
||||||
|
ProjectGrantMemberUserID = Column{
|
||||||
|
name: projection.MemberUserIDCol,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberRoles = Column{
|
||||||
|
name: projection.MemberRolesCol,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberCreationDate = Column{
|
||||||
|
name: projection.MemberCreationDate,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberChangeDate = Column{
|
||||||
|
name: projection.MemberChangeDate,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberSequence = Column{
|
||||||
|
name: projection.MemberSequence,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberResourceOwner = Column{
|
||||||
|
name: projection.MemberResourceOwner,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberProjectID = Column{
|
||||||
|
name: projection.ProjectGrantMemberProjectIDCol,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
ProjectGrantMemberGrantID = Column{
|
||||||
|
name: projection.ProjectGrantMemberGrantIDCol,
|
||||||
|
table: projectGrantMemberTable,
|
||||||
|
}
|
||||||
|
)
|
38
internal/query/project_member.go
Normal file
38
internal/query/project_member.go
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import "github.com/caos/zitadel/internal/query/projection"
|
||||||
|
|
||||||
|
var (
|
||||||
|
projectMemberTable = table{
|
||||||
|
name: projection.ProjectMemberProjectionTable,
|
||||||
|
alias: "members",
|
||||||
|
}
|
||||||
|
ProjectMemberUserID = Column{
|
||||||
|
name: projection.MemberUserIDCol,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
ProjectMemberRoles = Column{
|
||||||
|
name: projection.MemberRolesCol,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
ProjectMemberCreationDate = Column{
|
||||||
|
name: projection.MemberCreationDate,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
ProjectMemberChangeDate = Column{
|
||||||
|
name: projection.MemberChangeDate,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
ProjectMemberSequence = Column{
|
||||||
|
name: projection.MemberSequence,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
ProjectMemberResourceOwner = Column{
|
||||||
|
name: projection.MemberResourceOwner,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
ProjectMemberProjectID = Column{
|
||||||
|
name: projection.ProjectMemberProjectIDCol,
|
||||||
|
table: projectMemberTable,
|
||||||
|
}
|
||||||
|
)
|
@ -47,7 +47,7 @@ const (
|
|||||||
HumanUserIDCol = "user_id"
|
HumanUserIDCol = "user_id"
|
||||||
|
|
||||||
// profile
|
// profile
|
||||||
HumanFistNameCol = "first_name"
|
HumanFirstNameCol = "first_name"
|
||||||
HumanLastNameCol = "last_name"
|
HumanLastNameCol = "last_name"
|
||||||
HumanNickNameCol = "nick_name"
|
HumanNickNameCol = "nick_name"
|
||||||
HumanDisplayNameCol = "display_name"
|
HumanDisplayNameCol = "display_name"
|
||||||
@ -208,7 +208,7 @@ func (p *UserProjection) reduceHumanAdded(event eventstore.EventReader) (*handle
|
|||||||
crdb.AddCreateStatement(
|
crdb.AddCreateStatement(
|
||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol(HumanUserIDCol, e.Aggregate().ID),
|
handler.NewCol(HumanUserIDCol, e.Aggregate().ID),
|
||||||
handler.NewCol(HumanFistNameCol, e.FirstName),
|
handler.NewCol(HumanFirstNameCol, e.FirstName),
|
||||||
handler.NewCol(HumanLastNameCol, e.LastName),
|
handler.NewCol(HumanLastNameCol, e.LastName),
|
||||||
handler.NewCol(HumanNickNameCol, &sql.NullString{String: e.NickName, Valid: e.NickName != ""}),
|
handler.NewCol(HumanNickNameCol, &sql.NullString{String: e.NickName, Valid: e.NickName != ""}),
|
||||||
handler.NewCol(HumanDisplayNameCol, &sql.NullString{String: e.DisplayName, Valid: e.DisplayName != ""}),
|
handler.NewCol(HumanDisplayNameCol, &sql.NullString{String: e.DisplayName, Valid: e.DisplayName != ""}),
|
||||||
@ -244,7 +244,7 @@ func (p *UserProjection) reduceHumanRegistered(event eventstore.EventReader) (*h
|
|||||||
crdb.AddCreateStatement(
|
crdb.AddCreateStatement(
|
||||||
[]handler.Column{
|
[]handler.Column{
|
||||||
handler.NewCol(HumanUserIDCol, e.Aggregate().ID),
|
handler.NewCol(HumanUserIDCol, e.Aggregate().ID),
|
||||||
handler.NewCol(HumanFistNameCol, e.FirstName),
|
handler.NewCol(HumanFirstNameCol, e.FirstName),
|
||||||
handler.NewCol(HumanLastNameCol, e.LastName),
|
handler.NewCol(HumanLastNameCol, e.LastName),
|
||||||
handler.NewCol(HumanNickNameCol, &sql.NullString{String: e.NickName, Valid: e.NickName != ""}),
|
handler.NewCol(HumanNickNameCol, &sql.NullString{String: e.NickName, Valid: e.NickName != ""}),
|
||||||
handler.NewCol(HumanDisplayNameCol, &sql.NullString{String: e.DisplayName, Valid: e.DisplayName != ""}),
|
handler.NewCol(HumanDisplayNameCol, &sql.NullString{String: e.DisplayName, Valid: e.DisplayName != ""}),
|
||||||
@ -381,7 +381,7 @@ func (p *UserProjection) reduceHumanProfileChanged(event eventstore.EventReader)
|
|||||||
}
|
}
|
||||||
cols := make([]handler.Column, 0, 6)
|
cols := make([]handler.Column, 0, 6)
|
||||||
if e.FirstName != "" {
|
if e.FirstName != "" {
|
||||||
cols = append(cols, handler.NewCol(HumanFistNameCol, e.FirstName))
|
cols = append(cols, handler.NewCol(HumanFirstNameCol, e.FirstName))
|
||||||
}
|
}
|
||||||
|
|
||||||
if e.LastName != "" {
|
if e.LastName != "" {
|
||||||
|
@ -46,6 +46,23 @@ type SearchQuery interface {
|
|||||||
toQuery(sq.SelectBuilder) sq.SelectBuilder
|
toQuery(sq.SelectBuilder) sq.SelectBuilder
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type NotNullQuery struct {
|
||||||
|
Column Column
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewNotNullQuery(col Column) (*NotNullQuery, error) {
|
||||||
|
if col.isZero() {
|
||||||
|
return nil, ErrMissingColumn
|
||||||
|
}
|
||||||
|
return &NotNullQuery{
|
||||||
|
Column: col,
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *NotNullQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||||
|
return query.Where(sq.NotEq{q.Column.identifier(): nil})
|
||||||
|
}
|
||||||
|
|
||||||
type TextQuery struct {
|
type TextQuery struct {
|
||||||
Column Column
|
Column Column
|
||||||
Text string
|
Text string
|
||||||
|
@ -2,7 +2,120 @@ package query
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"github.com/caos/zitadel/internal/eventstore"
|
"github.com/caos/zitadel/internal/eventstore"
|
||||||
|
"github.com/caos/zitadel/internal/query/projection"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
userTable = table{
|
||||||
|
name: projection.UserTable,
|
||||||
|
}
|
||||||
|
UserIDCol = Column{
|
||||||
|
name: projection.UserIDCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
UserCreationDateCol = Column{
|
||||||
|
name: projection.UserCreationDateCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
UserChangeDateCol = Column{
|
||||||
|
name: projection.UserChangeDateCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
UserResourceOwnerCol = Column{
|
||||||
|
name: projection.UserResourceOwnerCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
UserStateCol = Column{
|
||||||
|
name: projection.UserStateCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
UserSequenceCol = Column{
|
||||||
|
name: projection.UserSequenceCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
UserUsernameCol = Column{
|
||||||
|
name: projection.UserUsernameCol,
|
||||||
|
table: userTable,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
humanTable = table{
|
||||||
|
name: projection.UserHumanTable,
|
||||||
|
}
|
||||||
|
// profile
|
||||||
|
HumanUserIDCol = Column{
|
||||||
|
name: projection.HumanUserIDCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanFirstNameCol = Column{
|
||||||
|
name: projection.HumanFirstNameCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanLastNameCol = Column{
|
||||||
|
name: projection.HumanLastNameCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanNickNameCol = Column{
|
||||||
|
name: projection.HumanNickNameCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanDisplayNameCol = Column{
|
||||||
|
name: projection.HumanDisplayNameCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanPreferredLanguageCol = Column{
|
||||||
|
name: projection.HumanPreferredLanguageCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanGenderCol = Column{
|
||||||
|
name: projection.HumanGenderCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanAvaterURLCol = Column{
|
||||||
|
name: projection.HumanAvaterURLCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
// email
|
||||||
|
HumanEmailCol = Column{
|
||||||
|
name: projection.HumanEmailCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanIsEmailVerifiedCol = Column{
|
||||||
|
name: projection.HumanIsEmailVerifiedCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
|
||||||
|
// phone
|
||||||
|
HumanPhoneCol = Column{
|
||||||
|
name: projection.HumanPhoneCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
HumanIsPhoneVerifiedCol = Column{
|
||||||
|
name: projection.HumanIsPhoneVerifiedCol,
|
||||||
|
table: humanTable,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
machineTable = table{
|
||||||
|
name: projection.UserMachineTable,
|
||||||
|
}
|
||||||
|
MachineUserIDCol = Column{
|
||||||
|
name: projection.MachineUserIDCol,
|
||||||
|
table: machineTable,
|
||||||
|
}
|
||||||
|
MachineNameCol = Column{
|
||||||
|
name: projection.MachineNameCol,
|
||||||
|
table: machineTable,
|
||||||
|
}
|
||||||
|
MachineDescriptionCol = Column{
|
||||||
|
name: projection.MachineDescriptionCol,
|
||||||
|
table: machineTable,
|
||||||
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (q *Queries) UserEvents(ctx context.Context, orgID, userID string, sequence uint64) ([]eventstore.EventReader, error) {
|
func (q *Queries) UserEvents(ctx context.Context, orgID, userID string, sequence uint64) ([]eventstore.EventReader, error) {
|
||||||
|
330
internal/query/user_membership.go
Normal file
330
internal/query/user_membership.go
Normal file
@ -0,0 +1,330 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"database/sql"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
"github.com/caos/zitadel/internal/errors"
|
||||||
|
"github.com/caos/zitadel/internal/query/projection"
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Memberships struct {
|
||||||
|
SearchResponse
|
||||||
|
Memberships []*Membership
|
||||||
|
}
|
||||||
|
|
||||||
|
type Membership struct {
|
||||||
|
UserID string
|
||||||
|
Roles []string
|
||||||
|
CreationDate time.Time
|
||||||
|
ChangeDate time.Time
|
||||||
|
Sequence uint64
|
||||||
|
ResourceOwner string
|
||||||
|
DisplayName string
|
||||||
|
|
||||||
|
Org *OrgMembership
|
||||||
|
IAM *IAMMembership
|
||||||
|
Project *ProjectMembership
|
||||||
|
ProjectGrant *ProjectGrantMembership
|
||||||
|
}
|
||||||
|
|
||||||
|
type OrgMembership struct {
|
||||||
|
OrgID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type IAMMembership struct {
|
||||||
|
IAMID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectMembership struct {
|
||||||
|
ProjectID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type ProjectGrantMembership struct {
|
||||||
|
ProjectID string
|
||||||
|
GrantID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MembershipSearchQuery struct {
|
||||||
|
SearchRequest
|
||||||
|
Queries []SearchQuery
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMembershipUserIDQuery(userID string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(membershipUserID.setTable(membershipAlias), userID, TextEquals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMembershipResourceOwnerQuery(value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(membershipResourceOwner.setTable(membershipAlias), value, TextEquals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMembershipOrgIDQuery(value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(membershipOrgID, value, TextEquals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMembershipProjectIDQuery(value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(membershipProjectID, value, TextEquals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMembershipProjectGrantIDQuery(value string) (SearchQuery, error) {
|
||||||
|
return NewTextQuery(membershipGrantID, value, TextEquals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewMembershipIsIAMQuery() (SearchQuery, error) {
|
||||||
|
return NewNotNullQuery(membershipIAMID)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *MembershipSearchQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||||
|
query = q.SearchRequest.toQuery(query)
|
||||||
|
for _, q := range q.Queries {
|
||||||
|
query = q.toQuery(query)
|
||||||
|
}
|
||||||
|
return query
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) Memberships(ctx context.Context, queries *MembershipSearchQuery) (*Memberships, error) {
|
||||||
|
query, scan := prepareMembershipsQuery()
|
||||||
|
stmt, args, err := queries.toQuery(query).ToSql()
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.ThrowInvalidArgument(err, "QUERY-T84X9", "Errors.Query.InvalidRequest")
|
||||||
|
}
|
||||||
|
latestSequence, err := q.latestSequence(ctx, orgMemberTable, iamMemberTable, projectMemberTable, projectGrantMemberTable)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, errors.ThrowInternal(err, "QUERY-eAV2x", "Errors.Internal")
|
||||||
|
}
|
||||||
|
memberships, err := scan(rows)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
memberships.LatestSequence = latestSequence
|
||||||
|
return memberships, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//membershipAlias is a hack to satisfy checks in the queries
|
||||||
|
membershipAlias = table{
|
||||||
|
name: "memberships",
|
||||||
|
}
|
||||||
|
membershipUserID = Column{
|
||||||
|
name: projection.MemberUserIDCol,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipRoles = Column{
|
||||||
|
name: projection.MemberRolesCol,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipCreationDate = Column{
|
||||||
|
name: projection.MemberCreationDate,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipChangeDate = Column{
|
||||||
|
name: projection.MemberChangeDate,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipSequence = Column{
|
||||||
|
name: projection.MemberSequence,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipResourceOwner = Column{
|
||||||
|
name: projection.MemberResourceOwner,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipOrgID = Column{
|
||||||
|
name: projection.OrgMemberOrgIDCol,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipIAMID = Column{
|
||||||
|
name: projection.IAMMemberIAMIDCol,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipProjectID = Column{
|
||||||
|
name: projection.ProjectMemberProjectIDCol,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
membershipGrantID = Column{
|
||||||
|
name: projection.ProjectGrantMemberGrantIDCol,
|
||||||
|
table: membershipAlias,
|
||||||
|
}
|
||||||
|
|
||||||
|
membershipFrom = "(" +
|
||||||
|
prepareOrgMember() +
|
||||||
|
" UNION ALL " +
|
||||||
|
prepareIAMMember() +
|
||||||
|
" UNION ALL " +
|
||||||
|
prepareProjectMember() +
|
||||||
|
" UNION ALL " +
|
||||||
|
prepareProjectGrantMember() +
|
||||||
|
") AS " + membershipAlias.identifier()
|
||||||
|
)
|
||||||
|
|
||||||
|
func prepareMembershipsQuery() (sq.SelectBuilder, func(*sql.Rows) (*Memberships, error)) {
|
||||||
|
return sq.Select(
|
||||||
|
membershipUserID.identifier(),
|
||||||
|
membershipRoles.identifier(),
|
||||||
|
membershipCreationDate.identifier(),
|
||||||
|
membershipChangeDate.identifier(),
|
||||||
|
membershipSequence.identifier(),
|
||||||
|
membershipResourceOwner.identifier(),
|
||||||
|
membershipOrgID.identifier(),
|
||||||
|
membershipIAMID.identifier(),
|
||||||
|
membershipProjectID.identifier(),
|
||||||
|
membershipGrantID.identifier(),
|
||||||
|
HumanDisplayNameCol.identifier(),
|
||||||
|
MachineNameCol.identifier(),
|
||||||
|
countColumn.identifier(),
|
||||||
|
).From(membershipFrom).
|
||||||
|
LeftJoin(join(HumanUserIDCol, membershipUserID)).
|
||||||
|
LeftJoin(join(MachineUserIDCol, membershipUserID)).
|
||||||
|
PlaceholderFormat(sq.Dollar),
|
||||||
|
func(rows *sql.Rows) (*Memberships, error) {
|
||||||
|
memberships := make([]*Membership, 0)
|
||||||
|
var count uint64
|
||||||
|
for rows.Next() {
|
||||||
|
|
||||||
|
var (
|
||||||
|
membership = new(Membership)
|
||||||
|
orgID = sql.NullString{}
|
||||||
|
iamID = sql.NullString{}
|
||||||
|
projectID = sql.NullString{}
|
||||||
|
grantID = sql.NullString{}
|
||||||
|
roles = pq.StringArray{}
|
||||||
|
displayName = sql.NullString{}
|
||||||
|
machineName = sql.NullString{}
|
||||||
|
)
|
||||||
|
|
||||||
|
err := rows.Scan(
|
||||||
|
&membership.UserID,
|
||||||
|
&roles,
|
||||||
|
&membership.CreationDate,
|
||||||
|
&membership.ChangeDate,
|
||||||
|
&membership.Sequence,
|
||||||
|
&membership.ResourceOwner,
|
||||||
|
&orgID,
|
||||||
|
&iamID,
|
||||||
|
&projectID,
|
||||||
|
&grantID,
|
||||||
|
&displayName,
|
||||||
|
&machineName,
|
||||||
|
&count,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
membership.Roles = roles
|
||||||
|
|
||||||
|
if displayName.Valid {
|
||||||
|
membership.DisplayName = displayName.String
|
||||||
|
} else if machineName.Valid {
|
||||||
|
membership.DisplayName = machineName.String
|
||||||
|
}
|
||||||
|
|
||||||
|
if orgID.Valid {
|
||||||
|
membership.Org = &OrgMembership{
|
||||||
|
OrgID: orgID.String,
|
||||||
|
}
|
||||||
|
} else if iamID.Valid {
|
||||||
|
membership.IAM = &IAMMembership{
|
||||||
|
IAMID: iamID.String,
|
||||||
|
}
|
||||||
|
} else if projectID.Valid && grantID.Valid {
|
||||||
|
membership.ProjectGrant = &ProjectGrantMembership{
|
||||||
|
ProjectID: projectID.String,
|
||||||
|
GrantID: grantID.String,
|
||||||
|
}
|
||||||
|
} else if projectID.Valid {
|
||||||
|
membership.Project = &ProjectMembership{
|
||||||
|
ProjectID: projectID.String,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
memberships = append(memberships, membership)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := rows.Close(); err != nil {
|
||||||
|
return nil, errors.ThrowInternal(err, "QUERY-N34NV", "Errors.Query.CloseRows")
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Memberships{
|
||||||
|
Memberships: memberships,
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: count,
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareOrgMember() string {
|
||||||
|
stmt, _ := sq.Select(
|
||||||
|
OrgMemberUserID.identifier(),
|
||||||
|
OrgMemberRoles.identifier(),
|
||||||
|
OrgMemberCreationDate.identifier(),
|
||||||
|
OrgMemberChangeDate.identifier(),
|
||||||
|
OrgMemberSequence.identifier(),
|
||||||
|
OrgMemberResourceOwner.identifier(),
|
||||||
|
OrgMemberOrgID.identifier(),
|
||||||
|
"NULL::STRING AS "+membershipIAMID.name,
|
||||||
|
"NULL::STRING AS "+membershipProjectID.name,
|
||||||
|
"NULL::STRING AS "+membershipGrantID.name,
|
||||||
|
).From(orgMemberTable.identifier()).MustSql()
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareIAMMember() string {
|
||||||
|
stmt, _ := sq.Select(
|
||||||
|
IAMMemberUserID.identifier(),
|
||||||
|
IAMMemberRoles.identifier(),
|
||||||
|
IAMMemberCreationDate.identifier(),
|
||||||
|
IAMMemberChangeDate.identifier(),
|
||||||
|
IAMMemberSequence.identifier(),
|
||||||
|
IAMMemberResourceOwner.identifier(),
|
||||||
|
"NULL::STRING AS "+membershipOrgID.name,
|
||||||
|
IAMMemberIAMID.identifier(),
|
||||||
|
"NULL::STRING AS "+membershipProjectID.name,
|
||||||
|
"NULL::STRING AS "+membershipGrantID.name,
|
||||||
|
).From(iamMemberTable.identifier()).MustSql()
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareProjectMember() string {
|
||||||
|
stmt, _ := sq.Select(
|
||||||
|
ProjectMemberUserID.identifier(),
|
||||||
|
ProjectMemberRoles.identifier(),
|
||||||
|
ProjectMemberCreationDate.identifier(),
|
||||||
|
ProjectMemberChangeDate.identifier(),
|
||||||
|
ProjectMemberSequence.identifier(),
|
||||||
|
ProjectMemberResourceOwner.identifier(),
|
||||||
|
"NULL::STRING AS "+membershipOrgID.name,
|
||||||
|
"NULL::STRING AS "+membershipIAMID.name,
|
||||||
|
ProjectMemberProjectID.identifier(),
|
||||||
|
"NULL::STRING AS "+membershipGrantID.name,
|
||||||
|
).From(projectMemberTable.identifier()).MustSql()
|
||||||
|
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
|
||||||
|
func prepareProjectGrantMember() string {
|
||||||
|
stmt, _ := sq.Select(
|
||||||
|
ProjectGrantMemberUserID.identifier(),
|
||||||
|
ProjectGrantMemberRoles.identifier(),
|
||||||
|
ProjectGrantMemberCreationDate.identifier(),
|
||||||
|
ProjectGrantMemberChangeDate.identifier(),
|
||||||
|
ProjectGrantMemberSequence.identifier(),
|
||||||
|
ProjectGrantMemberResourceOwner.identifier(),
|
||||||
|
"NULL::STRING AS "+membershipOrgID.name,
|
||||||
|
"NULL::STRING AS "+membershipIAMID.name,
|
||||||
|
ProjectGrantMemberProjectID.identifier(),
|
||||||
|
ProjectGrantMemberGrantID.identifier(),
|
||||||
|
).From(projectGrantMemberTable.identifier()).MustSql()
|
||||||
|
|
||||||
|
return stmt
|
||||||
|
}
|
611
internal/query/user_membership_test.go
Normal file
611
internal/query/user_membership_test.go
Normal file
@ -0,0 +1,611 @@
|
|||||||
|
package query
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"database/sql/driver"
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"regexp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
membershipsStmt = regexp.QuoteMeta(
|
||||||
|
"SELECT memberships.user_id" +
|
||||||
|
", memberships.roles" +
|
||||||
|
", memberships.creation_date" +
|
||||||
|
", memberships.change_date" +
|
||||||
|
", memberships.sequence" +
|
||||||
|
", memberships.resource_owner" +
|
||||||
|
", memberships.org_id" +
|
||||||
|
", memberships.iam_id" +
|
||||||
|
", memberships.project_id" +
|
||||||
|
", memberships.grant_id" +
|
||||||
|
", zitadel.projections.users_humans.display_name" +
|
||||||
|
", zitadel.projections.users_machines.name" +
|
||||||
|
", COUNT(*) OVER ()" +
|
||||||
|
" FROM (" +
|
||||||
|
"SELECT members.user_id" +
|
||||||
|
", members.roles" +
|
||||||
|
", members.creation_date" +
|
||||||
|
", members.change_date" +
|
||||||
|
", members.sequence" +
|
||||||
|
", members.resource_owner" +
|
||||||
|
", members.org_id" +
|
||||||
|
", NULL::STRING AS iam_id" +
|
||||||
|
", NULL::STRING AS project_id" +
|
||||||
|
", NULL::STRING AS grant_id" +
|
||||||
|
" FROM zitadel.projections.org_members as members" +
|
||||||
|
" UNION ALL " +
|
||||||
|
"SELECT members.user_id" +
|
||||||
|
", members.roles" +
|
||||||
|
", members.creation_date" +
|
||||||
|
", members.change_date" +
|
||||||
|
", members.sequence" +
|
||||||
|
", members.resource_owner" +
|
||||||
|
", NULL::STRING AS org_id" +
|
||||||
|
", members.iam_id" +
|
||||||
|
", NULL::STRING AS project_id" +
|
||||||
|
", NULL::STRING AS grant_id" +
|
||||||
|
" FROM zitadel.projections.iam_members as members" +
|
||||||
|
" UNION ALL " +
|
||||||
|
"SELECT members.user_id" +
|
||||||
|
", members.roles" +
|
||||||
|
", members.creation_date" +
|
||||||
|
", members.change_date" +
|
||||||
|
", members.sequence" +
|
||||||
|
", members.resource_owner" +
|
||||||
|
", NULL::STRING AS org_id" +
|
||||||
|
", NULL::STRING AS iam_id" +
|
||||||
|
", members.project_id" +
|
||||||
|
", NULL::STRING AS grant_id" +
|
||||||
|
" FROM zitadel.projections.project_members as members" +
|
||||||
|
" UNION ALL " +
|
||||||
|
"SELECT members.user_id" +
|
||||||
|
", members.roles" +
|
||||||
|
", members.creation_date" +
|
||||||
|
", members.change_date" +
|
||||||
|
", members.sequence" +
|
||||||
|
", members.resource_owner" +
|
||||||
|
", NULL::STRING AS org_id" +
|
||||||
|
", NULL::STRING AS iam_id" +
|
||||||
|
", members.project_id" +
|
||||||
|
", members.grant_id" +
|
||||||
|
" FROM zitadel.projections.project_grant_members as members" +
|
||||||
|
") AS memberships" +
|
||||||
|
" LEFT JOIN zitadel.projections.users_humans ON memberships.user_id = zitadel.projections.users_humans.user_id" +
|
||||||
|
" LEFT JOIN zitadel.projections.users_machines ON memberships.user_id = zitadel.projections.users_machines.user_id")
|
||||||
|
membershipCols = []string{
|
||||||
|
"user_id",
|
||||||
|
"roles",
|
||||||
|
"creation_date",
|
||||||
|
"change_date",
|
||||||
|
"sequence",
|
||||||
|
"resource_owner",
|
||||||
|
"org_id",
|
||||||
|
"iam_id",
|
||||||
|
"project_id",
|
||||||
|
"grant_id",
|
||||||
|
"display_name",
|
||||||
|
"name",
|
||||||
|
"count",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_MembershipPrepares(t *testing.T) {
|
||||||
|
type want struct {
|
||||||
|
sqlExpectations sqlExpectation
|
||||||
|
err checkErr
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
prepare interface{}
|
||||||
|
want want
|
||||||
|
object interface{}
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery no result",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{Memberships: []*Membership{}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one org member human",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
"org-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
Org: &OrgMembership{OrgID: "org-id"},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one org member machine",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
"org-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
Org: &OrgMembership{OrgID: "org-id"},
|
||||||
|
DisplayName: "machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one iam member human",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
"iam-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
IAM: &IAMMembership{IAMID: "iam-id"},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one iam member machine",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
"iam-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
IAM: &IAMMembership{IAMID: "iam-id"},
|
||||||
|
DisplayName: "machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one project member human",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"project-id",
|
||||||
|
nil,
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
Project: &ProjectMembership{ProjectID: "project-id"},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one project member machine",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"project-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
Project: &ProjectMembership{ProjectID: "project-id"},
|
||||||
|
DisplayName: "machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one project grant member human",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"project-id",
|
||||||
|
"grant-id",
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
ProjectGrant: &ProjectGrantMembership{
|
||||||
|
GrantID: "grant-id",
|
||||||
|
ProjectID: "project-id",
|
||||||
|
},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one project grant member machine",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"project-id",
|
||||||
|
"grant-id",
|
||||||
|
nil,
|
||||||
|
"machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 1,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
ProjectGrant: &ProjectGrantMembership{
|
||||||
|
GrantID: "grant-id",
|
||||||
|
ProjectID: "project-id",
|
||||||
|
},
|
||||||
|
DisplayName: "machine-name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery one for each member type",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueries(
|
||||||
|
membershipsStmt,
|
||||||
|
membershipCols,
|
||||||
|
[][]driver.Value{
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
"org-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
"iam-id",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"project-id",
|
||||||
|
nil,
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"user-id",
|
||||||
|
pq.StringArray{"role1", "role2"},
|
||||||
|
testNow,
|
||||||
|
testNow,
|
||||||
|
uint64(20211202),
|
||||||
|
"ro",
|
||||||
|
nil,
|
||||||
|
nil,
|
||||||
|
"project-id",
|
||||||
|
"grant-id",
|
||||||
|
"display name",
|
||||||
|
nil,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
),
|
||||||
|
},
|
||||||
|
object: &Memberships{
|
||||||
|
SearchResponse: SearchResponse{
|
||||||
|
Count: 4,
|
||||||
|
},
|
||||||
|
Memberships: []*Membership{
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
Org: &OrgMembership{OrgID: "org-id"},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
IAM: &IAMMembership{IAMID: "iam-id"},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
Project: &ProjectMembership{ProjectID: "project-id"},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
UserID: "user-id",
|
||||||
|
Roles: []string{"role1", "role2"},
|
||||||
|
CreationDate: testNow,
|
||||||
|
ChangeDate: testNow,
|
||||||
|
Sequence: 20211202,
|
||||||
|
ResourceOwner: "ro",
|
||||||
|
ProjectGrant: &ProjectGrantMembership{
|
||||||
|
ProjectID: "project-id",
|
||||||
|
GrantID: "grant-id",
|
||||||
|
},
|
||||||
|
DisplayName: "display name",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "prepareMembershipsQuery sql err",
|
||||||
|
prepare: prepareMembershipsQuery,
|
||||||
|
want: want{
|
||||||
|
sqlExpectations: mockQueryErr(
|
||||||
|
membershipsStmt,
|
||||||
|
sql.ErrConnDone,
|
||||||
|
),
|
||||||
|
err: func(err error) (error, bool) {
|
||||||
|
if !errors.Is(err, sql.ErrConnDone) {
|
||||||
|
return fmt.Errorf("err should be sql.ErrConnDone got: %w", err), false
|
||||||
|
}
|
||||||
|
return nil, true
|
||||||
|
},
|
||||||
|
},
|
||||||
|
object: nil,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
assertPrepare(t, tt.prepare, tt.object, tt.want.sqlExpectations, tt.want.err)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user