mirror of
https://github.com/zitadel/zitadel.git
synced 2025-05-29 21:08:21 +00:00
feat(queries): user grants (#2838)
* 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 * feat: org member queries * fix(api): use query for iam member calls * fix(queries): org members * fix(queries): project members * fix(queries): project grant members * fix(query): member queries and user avatar column * member cols * fix(queries): membership stmt * fix user test * fix user test * fix(projections): add user grant projection * fix(user_grant): handle state changes * add state to migration * fix(management): use query for user grant requests * merge eventstore-naming into user-grant-projection * feat(queries): user grants * fix(migrations): version * fix(api): user query for user grants * fix(query): event mappers for usergrant aggregate * fix(projection): correct aggregate for user grants * fix(queries): user grant roles as list contains * cleanup reducers * fix avater_key to avatar_key * tests * cleanup * cleanup * add resourceowner query * fix: user grant project name search query Co-authored-by: Livio Amstutz <livio.a@gmail.com> Co-authored-by: fabi <fabienne.gerschwiler@gmail.com>
This commit is contained in:
parent
a63a995269
commit
c542cab4f8
@ -489,6 +489,7 @@ UserTypeQuery is always equals
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.org_domain_query | UserGrantOrgDomainQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.project_name_query | UserGrantProjectNameQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.display_name_query | UserGrantDisplayNameQuery | - | |
|
||||
| [**oneof**](https://developers.google.com/protocol-buffers/docs/proto3#oneof) query.user_type_query | UserGrantUserTypeQuery | - | |
|
||||
|
||||
|
||||
|
||||
@ -528,6 +529,17 @@ UserTypeQuery is always equals
|
||||
|
||||
|
||||
|
||||
### UserGrantUserTypeQuery
|
||||
|
||||
|
||||
|
||||
| Field | Type | Description | Validation |
|
||||
| ----- | ---- | ----------- | ----------- |
|
||||
| type | Type | - | |
|
||||
|
||||
|
||||
|
||||
|
||||
### UserGrantWithGrantedQuery
|
||||
|
||||
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
member_grpc "github.com/caos/zitadel/internal/api/grpc/member"
|
||||
object_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
||||
project_grpc "github.com/caos/zitadel/internal/api/grpc/project"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
@ -174,11 +175,17 @@ func (s *Server) ReactivateProject(ctx context.Context, req *mgmt_pb.ReactivateP
|
||||
}
|
||||
|
||||
func (s *Server) RemoveProject(ctx context.Context, req *mgmt_pb.RemoveProjectRequest) (*mgmt_pb.RemoveProjectResponse, error) {
|
||||
grants, err := s.usergrant.UserGrantsByProjectID(ctx, req.Id)
|
||||
projectQuery, err := query.NewUserGrantProjectIDSearchQuery(req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.RemoveProject(ctx, req.Id, authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants)...)
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.RemoveProject(ctx, req.Id, authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants.UserGrants)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -253,7 +260,18 @@ func (s *Server) UpdateProjectRole(ctx context.Context, req *mgmt_pb.UpdateProje
|
||||
}
|
||||
|
||||
func (s *Server) RemoveProjectRole(ctx context.Context, req *mgmt_pb.RemoveProjectRoleRequest) (*mgmt_pb.RemoveProjectRoleResponse, error) {
|
||||
userGrants, err := s.usergrant.UserGrantsByProjectIDAndRoleKey(ctx, req.ProjectId, req.RoleKey)
|
||||
projectQuery, err := query.NewUserGrantProjectIDSearchQuery(req.ProjectId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
rolesQuery, err := query.NewUserGrantGrantIDSearchQuery(req.RoleKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, rolesQuery},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -261,7 +279,7 @@ func (s *Server) RemoveProjectRole(ctx context.Context, req *mgmt_pb.RemoveProje
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
details, err := s.command.RemoveProjectRole(ctx, req.ProjectId, req.RoleKey, authz.GetCtxData(ctx).OrgID, ProjectGrantsToIDs(projectGrants), userGrantsToIDs(userGrants)...)
|
||||
details, err := s.command.RemoveProjectRole(ctx, req.ProjectId, req.RoleKey, authz.GetCtxData(ctx).OrgID, ProjectGrantsToIDs(projectGrants), userGrantsToIDs(userGrants.UserGrants)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -8,6 +8,7 @@ import (
|
||||
member_grpc "github.com/caos/zitadel/internal/api/grpc/member"
|
||||
object_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
||||
proj_grpc "github.com/caos/zitadel/internal/api/grpc/project"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
@ -78,11 +79,21 @@ func (s *Server) AddProjectGrant(ctx context.Context, req *mgmt_pb.AddProjectGra
|
||||
}
|
||||
|
||||
func (s *Server) UpdateProjectGrant(ctx context.Context, req *mgmt_pb.UpdateProjectGrantRequest) (*mgmt_pb.UpdateProjectGrantResponse, error) {
|
||||
grants, err := s.usergrant.UserGrantsByProjectAndGrantID(ctx, req.ProjectId, req.GrantId)
|
||||
projectQuery, err := query.NewUserGrantProjectIDSearchQuery(req.ProjectId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grant, err := s.command.ChangeProjectGrant(ctx, UpdateProjectGrantRequestToDomain(req), authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants)...)
|
||||
grantQuery, err := query.NewUserGrantGrantIDSearchQuery(req.GrantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, grantQuery},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grant, err := s.command.ChangeProjectGrant(ctx, UpdateProjectGrantRequestToDomain(req), authz.GetCtxData(ctx).OrgID, userGrantsToIDs(grants.UserGrants)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -26,7 +26,6 @@ type Server struct {
|
||||
project repository.ProjectRepository
|
||||
org repository.OrgRepository
|
||||
user repository.UserRepository
|
||||
usergrant repository.UserGrantRepository
|
||||
iam repository.IamRepository
|
||||
authZ authz.Config
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
@ -44,7 +43,6 @@ func CreateServer(command *command.Commands, query *query.Queries, repo reposito
|
||||
project: repo,
|
||||
org: repo,
|
||||
user: repo,
|
||||
usergrant: repo,
|
||||
iam: repo,
|
||||
systemDefaults: sd,
|
||||
assetAPIPrefix: assetAPIPrefix,
|
||||
|
@ -10,13 +10,10 @@ import (
|
||||
change_grpc "github.com/caos/zitadel/internal/api/grpc/change"
|
||||
idp_grpc "github.com/caos/zitadel/internal/api/grpc/idp"
|
||||
"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"
|
||||
"github.com/caos/zitadel/internal/api/grpc/user"
|
||||
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
@ -257,21 +254,27 @@ func (s *Server) UnlockUser(ctx context.Context, req *mgmt_pb.UnlockUserRequest)
|
||||
}
|
||||
|
||||
func (s *Server) RemoveUser(ctx context.Context, req *mgmt_pb.RemoveUserRequest) (*mgmt_pb.RemoveUserResponse, error) {
|
||||
grants, err := s.usergrant.UserGrantsByUserID(ctx, req.Id)
|
||||
userGrantUserQuery, err := query.NewUserGrantUserIDSearchQuery(req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
userQuery, err := query.NewMembershipUserIDQuery(req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memberships, err := s.query.Memberships(ctx, &query.MembershipSearchQuery{
|
||||
Queries: []query.SearchQuery{userQuery},
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objectDetails, err := s.command.RemoveUser(ctx, req.Id, authz.GetCtxData(ctx).OrgID, memberships.Memberships, userGrantsToIDs(grants)...)
|
||||
membershipsUserQuery, err := query.NewMembershipUserIDQuery(req.Id)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
memberships, err := s.query.Memberships(ctx, &query.MembershipSearchQuery{
|
||||
Queries: []query.SearchQuery{membershipsUserQuery},
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
objectDetails, err := s.command.RemoveUser(ctx, req.Id, authz.GetCtxData(ctx).OrgID, memberships.Memberships, userGrantsToIDs(grants.UserGrants)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -280,7 +283,7 @@ func (s *Server) RemoveUser(ctx context.Context, req *mgmt_pb.RemoveUserRequest)
|
||||
}, nil
|
||||
}
|
||||
|
||||
func userGrantsToIDs(userGrants []*grant_model.UserGrantView) []string {
|
||||
func userGrantsToIDs(userGrants []*query.UserGrant) []string {
|
||||
converted := make([]string, len(userGrants))
|
||||
for i, grant := range userGrants {
|
||||
converted[i] = grant.ID
|
||||
@ -435,7 +438,7 @@ func (s *Server) RemoveHumanAvatar(ctx context.Context, req *mgmt_pb.RemoveHuman
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.RemoveHumanAvatarResponse{
|
||||
Details: object.DomainToChangeDetailsPb(objectDetails),
|
||||
Details: obj_grpc.DomainToChangeDetailsPb(objectDetails),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -505,7 +508,7 @@ func (s *Server) ListHumanPasswordless(ctx context.Context, req *mgmt_pb.ListHum
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ListHumanPasswordlessResponse{
|
||||
Result: user.WebAuthNTokensViewToPb(tokens),
|
||||
Result: user_grpc.WebAuthNTokensViewToPb(tokens),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -516,7 +519,7 @@ func (s *Server) AddPasswordlessRegistration(ctx context.Context, req *mgmt_pb.A
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddPasswordlessRegistrationResponse{
|
||||
Details: object.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner),
|
||||
Details: obj_grpc.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner),
|
||||
Link: initCode.Link(s.systemDefaults.Notifications.Endpoints.PasswordlessRegistration),
|
||||
Expiration: durationpb.New(initCode.Expiration),
|
||||
}, nil
|
||||
@ -529,7 +532,7 @@ func (s *Server) SendPasswordlessRegistration(ctx context.Context, req *mgmt_pb.
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.SendPasswordlessRegistrationResponse{
|
||||
Details: object.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner),
|
||||
Details: obj_grpc.AddToDetailsPb(initCode.Sequence, initCode.ChangeDate, initCode.ResourceOwner),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@ -606,7 +609,7 @@ func (s *Server) AddMachineKey(ctx context.Context, req *mgmt_pb.AddMachineKeyRe
|
||||
return &mgmt_pb.AddMachineKeyResponse{
|
||||
KeyId: key.KeyID,
|
||||
KeyDetails: keyDetails,
|
||||
Details: object.AddToDetailsPb(
|
||||
Details: obj_grpc.AddToDetailsPb(
|
||||
key.Sequence,
|
||||
key.ChangeDate,
|
||||
key.ResourceOwner,
|
||||
|
@ -6,31 +6,37 @@ import (
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
obj_grpc "github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/api/grpc/user"
|
||||
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||
)
|
||||
|
||||
func (s *Server) GetUserGrantByID(ctx context.Context, req *mgmt_pb.GetUserGrantByIDRequest) (*mgmt_pb.GetUserGrantByIDResponse, error) {
|
||||
grant, err := s.usergrant.UserGrantByID(ctx, req.GrantId)
|
||||
ownerQuery, err := query.NewUserGrantResourceOwnerSearchQuery(authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grant, err := s.query.UserGrantByID(ctx, req.GrantId, ownerQuery)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.GetUserGrantByIDResponse{
|
||||
UserGrant: user.UserGrantToPb(grant),
|
||||
UserGrant: user.UserGrantToPb(s.assetAPIPrefix, grant),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) ListUserGrants(ctx context.Context, req *mgmt_pb.ListUserGrantRequest) (*mgmt_pb.ListUserGrantResponse, error) {
|
||||
r := ListUserGrantsRequestToModel(ctx, req)
|
||||
r.AppendMyOrgQuery(authz.GetCtxData(ctx).OrgID)
|
||||
res, err := s.usergrant.SearchUserGrants(ctx, r)
|
||||
queries, err := ListUserGrantsRequestToQuery(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.UserGrants(ctx, queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.ListUserGrantResponse{
|
||||
Result: user.UserGrantsToPb(res.Result),
|
||||
Result: user.UserGrantsToPb(s.assetAPIPrefix, res.UserGrants),
|
||||
Details: obj_grpc.ToListDetails(
|
||||
res.TotalResult,
|
||||
res.Count,
|
||||
res.Sequence,
|
||||
res.Timestamp,
|
||||
),
|
||||
|
@ -2,29 +2,51 @@ package management
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
user_grpc "github.com/caos/zitadel/internal/api/grpc/user"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
mgmt_pb "github.com/caos/zitadel/pkg/grpc/management"
|
||||
"github.com/caos/zitadel/pkg/grpc/user"
|
||||
)
|
||||
|
||||
func ListUserGrantsRequestToModel(ctx context.Context, req *mgmt_pb.ListUserGrantRequest) *model.UserGrantSearchRequest {
|
||||
func ListUserGrantsRequestToQuery(ctx context.Context, req *mgmt_pb.ListUserGrantRequest) (*query.UserGrantsQueries, error) {
|
||||
queries, err := user_grpc.UserGrantQueriesToQuery(ctx, req.Queries)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if shouldAppendUserGrantOwnerQuery(req.Queries) {
|
||||
ownerQuery, err := query.NewUserGrantResourceOwnerSearchQuery(authz.GetCtxData(ctx).OrgID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries = append(queries, ownerQuery)
|
||||
}
|
||||
|
||||
offset, limit, asc := object.ListQueryToModel(req.Query)
|
||||
request := &model.UserGrantSearchRequest{
|
||||
request := &query.UserGrantsQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
Queries: user_grpc.UserGrantQueriesToModel(req.Queries),
|
||||
},
|
||||
Queries: queries,
|
||||
}
|
||||
request.Queries = append(request.Queries, &model.UserGrantSearchQuery{
|
||||
Key: model.UserGrantSearchKeyResourceOwner,
|
||||
Method: domain.SearchMethodEquals,
|
||||
Value: authz.GetCtxData(ctx).OrgID,
|
||||
})
|
||||
return request
|
||||
|
||||
return request, nil
|
||||
}
|
||||
|
||||
func shouldAppendUserGrantOwnerQuery(queries []*user.UserGrantQuery) bool {
|
||||
for _, query := range queries {
|
||||
if _, ok := query.Query.(*user.UserGrantQuery_WithGrantedQuery); ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func AddUserGrantRequestToDomain(req *mgmt_pb.AddUserGrantRequest) *domain.UserGrant {
|
||||
|
@ -1,38 +1,42 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/api/grpc/object"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
usr_grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/query"
|
||||
user_pb "github.com/caos/zitadel/pkg/grpc/user"
|
||||
)
|
||||
|
||||
func UserGrantsToPb(grants []*usr_grant_model.UserGrantView) []*user_pb.UserGrant {
|
||||
func UserGrantsToPb(assetPrefix string, grants []*query.UserGrant) []*user_pb.UserGrant {
|
||||
u := make([]*user_pb.UserGrant, len(grants))
|
||||
for i, grant := range grants {
|
||||
u[i] = UserGrantToPb(grant)
|
||||
u[i] = UserGrantToPb(assetPrefix, grant)
|
||||
}
|
||||
return u
|
||||
}
|
||||
|
||||
func UserGrantToPb(grant *usr_grant_model.UserGrantView) *user_pb.UserGrant {
|
||||
func UserGrantToPb(assetPrefix string, grant *query.UserGrant) *user_pb.UserGrant {
|
||||
return &user_pb.UserGrant{
|
||||
Id: grant.ID,
|
||||
UserId: grant.UserID,
|
||||
State: ModelUserGrantStateToPb(grant.State),
|
||||
RoleKeys: grant.RoleKeys,
|
||||
UserName: grant.UserName,
|
||||
State: user_pb.UserGrantState_USER_GRANT_STATE_ACTIVE,
|
||||
RoleKeys: grant.Roles,
|
||||
ProjectId: grant.ProjectID,
|
||||
OrgId: grant.ResourceOwner,
|
||||
ProjectGrantId: grant.GrantID,
|
||||
UserName: grant.Username,
|
||||
FirstName: grant.FirstName,
|
||||
LastName: grant.LastName,
|
||||
Email: grant.Email,
|
||||
DisplayName: grant.DisplayName,
|
||||
OrgId: grant.ResourceOwner,
|
||||
OrgDomain: grant.OrgPrimaryDomain,
|
||||
OrgName: grant.OrgName,
|
||||
ProjectId: grant.ProjectID,
|
||||
ProjectName: grant.ProjectName,
|
||||
ProjectGrantId: grant.GrantID,
|
||||
AvatarUrl: grant.AvatarURL,
|
||||
AvatarUrl: domain.AvatarURL(assetPrefix, grant.UserResourceOwner, grant.AvatarURL),
|
||||
Details: object.ToViewDetailsPb(
|
||||
grant.Sequence,
|
||||
grant.CreationDate,
|
||||
@ -42,15 +46,18 @@ func UserGrantToPb(grant *usr_grant_model.UserGrantView) *user_pb.UserGrant {
|
||||
}
|
||||
}
|
||||
|
||||
func UserGrantQueriesToModel(queries []*user_pb.UserGrantQuery) []*usr_grant_model.UserGrantSearchQuery {
|
||||
q := make([]*usr_grant_model.UserGrantSearchQuery, len(queries))
|
||||
func UserGrantQueriesToQuery(ctx context.Context, queries []*user_pb.UserGrantQuery) (q []query.SearchQuery, err error) {
|
||||
q = make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i] = UserGrantQueryToModel(query)
|
||||
q[i], err = UserGrantQueryToQuery(ctx, query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return q
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func UserGrantQueryToModel(query *user_pb.UserGrantQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
func UserGrantQueryToQuery(ctx context.Context, query *user_pb.UserGrantQuery) (query.SearchQuery, error) {
|
||||
switch q := query.Query.(type) {
|
||||
case *user_pb.UserGrantQuery_DisplayNameQuery:
|
||||
return UserGrantDisplayNameQueryToModel(q.DisplayNameQuery)
|
||||
@ -77,112 +84,77 @@ func UserGrantQueryToModel(query *user_pb.UserGrantQuery) *usr_grant_model.UserG
|
||||
case *user_pb.UserGrantQuery_UserNameQuery:
|
||||
return UserGrantUserNameQueryToModel(q.UserNameQuery)
|
||||
case *user_pb.UserGrantQuery_WithGrantedQuery:
|
||||
return UserGrantWithGrantedQueryToModel(q.WithGrantedQuery)
|
||||
return UserGrantWithGrantedQueryToModel(ctx, q.WithGrantedQuery)
|
||||
case *user_pb.UserGrantQuery_UserTypeQuery:
|
||||
return UserGrantUserTypeQueryToModel(q.UserTypeQuery)
|
||||
default:
|
||||
return nil
|
||||
return nil, errors.New("invalid query")
|
||||
}
|
||||
}
|
||||
|
||||
func UserGrantDisplayNameQueryToModel(q *user_pb.UserGrantDisplayNameQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyDisplayName,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.DisplayName,
|
||||
}
|
||||
func UserGrantDisplayNameQueryToModel(q *user_pb.UserGrantDisplayNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantDisplayNameQuery(q.DisplayName, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantEmailQueryToModel(q *user_pb.UserGrantEmailQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyEmail,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.Email,
|
||||
}
|
||||
func UserGrantEmailQueryToModel(q *user_pb.UserGrantEmailQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantEmailQuery(q.Email, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantFirstNameQueryToModel(q *user_pb.UserGrantFirstNameQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyFirstName,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.FirstName,
|
||||
}
|
||||
func UserGrantFirstNameQueryToModel(q *user_pb.UserGrantFirstNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantFirstNameQuery(q.FirstName, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantLastNameQueryToModel(q *user_pb.UserGrantLastNameQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyLastName,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.LastName,
|
||||
}
|
||||
func UserGrantLastNameQueryToModel(q *user_pb.UserGrantLastNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantLastNameQuery(q.LastName, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantOrgDomainQueryToModel(q *user_pb.UserGrantOrgDomainQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyOrgDomain,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.OrgDomain,
|
||||
}
|
||||
func UserGrantOrgDomainQueryToModel(q *user_pb.UserGrantOrgDomainQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantDomainQuery(q.OrgDomain, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantOrgNameQueryToModel(q *user_pb.UserGrantOrgNameQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyOrgName,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.OrgName,
|
||||
}
|
||||
func UserGrantOrgNameQueryToModel(q *user_pb.UserGrantOrgNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantOrgNameQuery(q.OrgName, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantProjectIDQueryToModel(q *user_pb.UserGrantProjectIDQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyProjectID,
|
||||
Method: domain.SearchMethodEquals,
|
||||
Value: q.ProjectId,
|
||||
}
|
||||
func UserGrantProjectIDQueryToModel(q *user_pb.UserGrantProjectIDQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantProjectIDSearchQuery(q.ProjectId)
|
||||
}
|
||||
|
||||
func UserGrantProjectGrantIDQueryToModel(q *user_pb.UserGrantProjectGrantIDQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyGrantID,
|
||||
Method: domain.SearchMethodEquals,
|
||||
Value: q.ProjectGrantId,
|
||||
}
|
||||
func UserGrantProjectGrantIDQueryToModel(q *user_pb.UserGrantProjectGrantIDQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantGrantIDSearchQuery(q.ProjectGrantId)
|
||||
}
|
||||
|
||||
func UserGrantProjectNameQueryToModel(q *user_pb.UserGrantProjectNameQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyProjectName,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.ProjectName,
|
||||
}
|
||||
func UserGrantProjectNameQueryToModel(q *user_pb.UserGrantProjectNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantProjectNameQuery(q.ProjectName, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantRoleKeyQueryToModel(q *user_pb.UserGrantRoleKeyQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyRoleKey,
|
||||
Method: domain.SearchMethodListContains,
|
||||
Value: q.RoleKey,
|
||||
}
|
||||
func UserGrantRoleKeyQueryToModel(q *user_pb.UserGrantRoleKeyQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantRoleQuery(q.RoleKey)
|
||||
}
|
||||
|
||||
func UserGrantUserIDQueryToModel(q *user_pb.UserGrantUserIDQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyUserID,
|
||||
Method: domain.SearchMethodEquals,
|
||||
Value: q.UserId,
|
||||
}
|
||||
func UserGrantUserIDQueryToModel(q *user_pb.UserGrantUserIDQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantUserIDSearchQuery(q.UserId)
|
||||
}
|
||||
|
||||
func UserGrantUserNameQueryToModel(q *user_pb.UserGrantUserNameQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyUserName,
|
||||
Method: object.TextMethodToModel(q.Method),
|
||||
Value: q.UserName,
|
||||
}
|
||||
func UserGrantUserNameQueryToModel(q *user_pb.UserGrantUserNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantUsernameQuery(q.UserName, object.TextMethodToQuery(q.Method))
|
||||
}
|
||||
|
||||
func UserGrantWithGrantedQueryToModel(q *user_pb.UserGrantWithGrantedQuery) *usr_grant_model.UserGrantSearchQuery {
|
||||
return &usr_grant_model.UserGrantSearchQuery{
|
||||
Key: usr_grant_model.UserGrantSearchKeyWithGranted,
|
||||
Method: domain.SearchMethodEquals,
|
||||
Value: q.WithGranted,
|
||||
func UserGrantWithGrantedQueryToModel(ctx context.Context, q *user_pb.UserGrantWithGrantedQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantWithGrantedQuery(authz.GetCtxData(ctx).OrgID)
|
||||
}
|
||||
|
||||
func UserGrantUserTypeQueryToModel(q *user_pb.UserGrantUserTypeQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantUserTypeQuery(grantTypeToDomain(q.Type))
|
||||
}
|
||||
|
||||
func grantTypeToDomain(typ user_pb.Type) domain.UserType {
|
||||
switch typ {
|
||||
case user_pb.Type_TYPE_HUMAN:
|
||||
return domain.UserTypeHuman
|
||||
case user_pb.Type_TYPE_MACHINE:
|
||||
return domain.UserTypeMachine
|
||||
default:
|
||||
return domain.UserTypeUnspecified
|
||||
}
|
||||
}
|
||||
|
@ -1,138 +0,0 @@
|
||||
package eventstore
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
|
||||
"github.com/caos/logging"
|
||||
"github.com/caos/zitadel/internal/api/authz"
|
||||
"github.com/caos/zitadel/internal/management/repository/eventsourcing/view"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/view/repository"
|
||||
)
|
||||
|
||||
type UserGrantRepo struct {
|
||||
SearchLimit uint64
|
||||
View *view.View
|
||||
PrefixAvatarURL string
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantByID(ctx context.Context, grantID string) (*grant_model.UserGrantView, error) {
|
||||
grant, err := repo.View.UserGrantByID(grantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantToModel(grant, repo.PrefixAvatarURL), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantsByProjectID(ctx context.Context, projectID string) ([]*grant_model.UserGrantView, error) {
|
||||
grants, err := repo.View.UserGrantsByProjectID(projectID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantsByProjectIDAndRoleKey(ctx context.Context, projectID, roleKey string) ([]*grant_model.UserGrantView, error) {
|
||||
grants, err := repo.View.UserGrantsByProjectIDAndRoleKey(projectID, roleKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantsByProjectAndGrantID(ctx context.Context, projectID, grantID string) ([]*grant_model.UserGrantView, error) {
|
||||
grants, err := repo.View.UserGrantsByProjectAndGrantID(projectID, grantID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) UserGrantsByUserID(ctx context.Context, userID string) ([]*grant_model.UserGrantView, error) {
|
||||
grants, err := repo.View.UserGrantsByUserID(userID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return model.UserGrantsToModel(grants, repo.PrefixAvatarURL), nil
|
||||
}
|
||||
|
||||
func (repo *UserGrantRepo) SearchUserGrants(ctx context.Context, request *grant_model.UserGrantSearchRequest) (*grant_model.UserGrantSearchResponse, error) {
|
||||
err := request.EnsureLimit(repo.SearchLimit)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sequence, sequenceErr := repo.View.GetLatestUserGrantSequence()
|
||||
logging.Log("EVENT-5Viwf").OnError(sequenceErr).Warn("could not read latest user grant sequence")
|
||||
|
||||
result := handleSearchUserGrantPermissions(ctx, request, sequence)
|
||||
if result != nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
grants, count, err := repo.View.SearchUserGrants(request)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
result = &grant_model.UserGrantSearchResponse{
|
||||
Offset: request.Offset,
|
||||
Limit: request.Limit,
|
||||
TotalResult: count,
|
||||
Result: model.UserGrantsToModel(grants, repo.PrefixAvatarURL),
|
||||
}
|
||||
if sequenceErr == nil {
|
||||
result.Sequence = sequence.CurrentSequence
|
||||
result.Timestamp = sequence.LastSuccessfulSpoolerRun
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func handleSearchUserGrantPermissions(ctx context.Context, request *grant_model.UserGrantSearchRequest, sequence *repository.CurrentSequence) *grant_model.UserGrantSearchResponse {
|
||||
permissions := authz.GetAllPermissionsFromCtx(ctx)
|
||||
if authz.HasGlobalExplicitPermission(permissions, projectReadPerm) {
|
||||
return nil
|
||||
}
|
||||
|
||||
ids := authz.GetExplicitPermissionCtxIDs(permissions, projectReadPerm)
|
||||
if _, query := request.GetSearchQuery(grant_model.UserGrantSearchKeyGrantID); query != nil {
|
||||
result := checkContainsPermID(ids, query, request, sequence)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
||||
if _, query := request.GetSearchQuery(grant_model.UserGrantSearchKeyProjectID); query != nil {
|
||||
result := checkContainsPermID(ids, query, request, sequence)
|
||||
if result != nil {
|
||||
return result
|
||||
}
|
||||
}
|
||||
request.Queries = append(request.Queries, &grant_model.UserGrantSearchQuery{Key: grant_model.UserGrantSearchKeyProjectID, Method: domain.SearchMethodIsOneOf, Value: ids})
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkContainsPermID(ids []string, query *grant_model.UserGrantSearchQuery, request *grant_model.UserGrantSearchRequest, sequence *repository.CurrentSequence) *grant_model.UserGrantSearchResponse {
|
||||
containsID := false
|
||||
for _, id := range ids {
|
||||
if id == query.Value {
|
||||
containsID = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !containsID {
|
||||
result := &grant_model.UserGrantSearchResponse{
|
||||
Offset: request.Offset,
|
||||
Limit: request.Limit,
|
||||
TotalResult: uint64(0),
|
||||
Result: []*grant_model.UserGrantView{},
|
||||
}
|
||||
if sequence != nil {
|
||||
result.Sequence = sequence.CurrentSequence
|
||||
result.Timestamp = sequence.LastSuccessfulSpoolerRun
|
||||
}
|
||||
return result
|
||||
}
|
||||
return nil
|
||||
}
|
@ -35,7 +35,6 @@ func Register(configs Configs, bulkLimit, errorCount uint64, view *view.View, es
|
||||
return []query.Handler{
|
||||
newUser(handler{view, bulkLimit, configs.cycleDuration("User"), errorCount, es},
|
||||
defaults.IamID),
|
||||
newUserGrant(handler{view, bulkLimit, configs.cycleDuration("UserGrant"), errorCount, es}),
|
||||
newMetadata(
|
||||
handler{view, bulkLimit, configs.cycleDuration("Metadata"), errorCount, es}),
|
||||
}
|
||||
|
@ -1,340 +0,0 @@
|
||||
package handler
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/caos/logging"
|
||||
|
||||
caos_errs "github.com/caos/zitadel/internal/errors"
|
||||
v1 "github.com/caos/zitadel/internal/eventstore/v1"
|
||||
es_models "github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/query"
|
||||
es_sdk "github.com/caos/zitadel/internal/eventstore/v1/sdk"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/spooler"
|
||||
org_model "github.com/caos/zitadel/internal/org/model"
|
||||
org_es_model "github.com/caos/zitadel/internal/org/repository/eventsourcing/model"
|
||||
org_view "github.com/caos/zitadel/internal/org/repository/view"
|
||||
proj_model "github.com/caos/zitadel/internal/project/model"
|
||||
proj_es_model "github.com/caos/zitadel/internal/project/repository/eventsourcing/model"
|
||||
proj_view "github.com/caos/zitadel/internal/project/repository/view"
|
||||
usr_model "github.com/caos/zitadel/internal/user/model"
|
||||
usr_es_model "github.com/caos/zitadel/internal/user/repository/eventsourcing/model"
|
||||
"github.com/caos/zitadel/internal/user/repository/view"
|
||||
usr_view_model "github.com/caos/zitadel/internal/user/repository/view/model"
|
||||
grant_es_model "github.com/caos/zitadel/internal/usergrant/repository/eventsourcing/model"
|
||||
view_model "github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
)
|
||||
|
||||
const (
|
||||
userGrantTable = "management.user_grants"
|
||||
)
|
||||
|
||||
type UserGrant struct {
|
||||
handler
|
||||
subscription *v1.Subscription
|
||||
}
|
||||
|
||||
func newUserGrant(
|
||||
handler handler,
|
||||
) *UserGrant {
|
||||
h := &UserGrant{
|
||||
handler: handler,
|
||||
}
|
||||
|
||||
h.subscribe()
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
func (m *UserGrant) subscribe() {
|
||||
m.subscription = m.es.Subscribe(m.AggregateTypes()...)
|
||||
go func() {
|
||||
for event := range m.subscription.Events {
|
||||
query.ReduceEvent(m, event)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
func (u *UserGrant) ViewModel() string {
|
||||
return userGrantTable
|
||||
}
|
||||
|
||||
func (u *UserGrant) Subscription() *v1.Subscription {
|
||||
return u.subscription
|
||||
}
|
||||
|
||||
func (_ *UserGrant) AggregateTypes() []es_models.AggregateType {
|
||||
return []es_models.AggregateType{grant_es_model.UserGrantAggregate, usr_es_model.UserAggregate, proj_es_model.ProjectAggregate, org_es_model.OrgAggregate}
|
||||
}
|
||||
|
||||
func (u *UserGrant) CurrentSequence() (uint64, error) {
|
||||
sequence, err := u.view.GetLatestUserGrantSequence()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return sequence.CurrentSequence, nil
|
||||
}
|
||||
|
||||
func (u *UserGrant) EventQuery() (*es_models.SearchQuery, error) {
|
||||
sequence, err := u.view.GetLatestUserGrantSequence()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return es_models.NewSearchQuery().
|
||||
AggregateTypeFilter(u.AggregateTypes()...).
|
||||
LatestSequenceFilter(sequence.CurrentSequence), nil
|
||||
}
|
||||
|
||||
func (u *UserGrant) Reduce(event *es_models.Event) (err error) {
|
||||
switch event.AggregateType {
|
||||
case grant_es_model.UserGrantAggregate:
|
||||
err = u.processUserGrant(event)
|
||||
case usr_es_model.UserAggregate:
|
||||
err = u.processUser(event)
|
||||
case proj_es_model.ProjectAggregate:
|
||||
err = u.processProject(event)
|
||||
case org_es_model.OrgAggregate:
|
||||
err = u.processOrg(event)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (u *UserGrant) processUserGrant(event *es_models.Event) (err error) {
|
||||
grant := new(view_model.UserGrantView)
|
||||
switch event.Type {
|
||||
case grant_es_model.UserGrantAdded:
|
||||
err = grant.AppendEvent(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = u.fillData(grant, event.ResourceOwner)
|
||||
case grant_es_model.UserGrantChanged,
|
||||
grant_es_model.UserGrantCascadeChanged,
|
||||
grant_es_model.UserGrantDeactivated,
|
||||
grant_es_model.UserGrantReactivated:
|
||||
grant, err = u.view.UserGrantByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = grant.AppendEvent(event)
|
||||
case grant_es_model.UserGrantRemoved, grant_es_model.UserGrantCascadeRemoved:
|
||||
return u.view.DeleteUserGrant(event.AggregateID, event)
|
||||
default:
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return u.view.PutUserGrant(grant, event)
|
||||
}
|
||||
|
||||
func (u *UserGrant) processUser(event *es_models.Event) (err error) {
|
||||
switch event.Type {
|
||||
case usr_es_model.UserProfileChanged,
|
||||
usr_es_model.UserEmailChanged,
|
||||
usr_es_model.HumanProfileChanged,
|
||||
usr_es_model.HumanEmailChanged,
|
||||
usr_es_model.MachineChanged,
|
||||
usr_es_model.HumanAvatarAdded,
|
||||
usr_es_model.HumanAvatarRemoved:
|
||||
grants, err := u.view.UserGrantsByUserID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(grants) == 0 {
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
user, err := u.getUserByID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, grant := range grants {
|
||||
u.fillUserData(grant, user)
|
||||
}
|
||||
return u.view.PutUserGrants(grants, event)
|
||||
default:
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) processProject(event *es_models.Event) (err error) {
|
||||
switch event.Type {
|
||||
case proj_es_model.ProjectChanged:
|
||||
proj := new(proj_es_model.Project)
|
||||
err := proj.SetData(event)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if proj.Name == "" {
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
grants, err := u.view.UserGrantsByProjectID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, grant := range grants {
|
||||
grant.ProjectName = proj.Name
|
||||
}
|
||||
return u.view.PutUserGrants(grants, event)
|
||||
default:
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) processOrg(event *es_models.Event) (err error) {
|
||||
switch event.Type {
|
||||
case org_es_model.OrgChanged:
|
||||
grants, err := u.view.UserGrantsByOrgID(event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if len(grants) == 0 {
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
org, err := u.getOrgByID(context.Background(), event.AggregateID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
for _, grant := range grants {
|
||||
u.fillOrgData(grant, org)
|
||||
}
|
||||
return u.view.PutUserGrants(grants, event)
|
||||
default:
|
||||
return u.view.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) fillData(grant *view_model.UserGrantView, resourceOwner string) (err error) {
|
||||
user, err := u.getUserByID(grant.UserID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.fillUserData(grant, user)
|
||||
project, err := u.getProjectByID(context.Background(), grant.ProjectID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.fillProjectData(grant, project)
|
||||
|
||||
org, err := u.getOrgByID(context.TODO(), resourceOwner)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
u.fillOrgData(grant, org)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (u *UserGrant) fillUserData(grant *view_model.UserGrantView, user *usr_view_model.UserView) {
|
||||
grant.UserName = user.UserName
|
||||
grant.UserResourceOwner = user.ResourceOwner
|
||||
if user.HumanView != nil {
|
||||
grant.FirstName = user.FirstName
|
||||
grant.LastName = user.LastName
|
||||
grant.DisplayName = user.FirstName + " " + user.LastName
|
||||
grant.Email = user.Email
|
||||
grant.AvatarKey = user.AvatarKey
|
||||
}
|
||||
if user.MachineView != nil {
|
||||
grant.DisplayName = user.MachineView.Name
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) fillProjectData(grant *view_model.UserGrantView, project *proj_model.Project) {
|
||||
grant.ProjectName = project.Name
|
||||
grant.ProjectOwner = project.ResourceOwner
|
||||
}
|
||||
|
||||
func (u *UserGrant) fillOrgData(grant *view_model.UserGrantView, org *org_model.Org) {
|
||||
grant.OrgName = org.Name
|
||||
for _, domain := range org.Domains {
|
||||
if domain.Primary {
|
||||
grant.OrgPrimaryDomain = domain.Domain
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (u *UserGrant) OnError(event *es_models.Event, err error) error {
|
||||
logging.LogWithFields("SPOOL-8is4s", "id", event.AggregateID).WithError(err).Warn("something went wrong in user handler")
|
||||
return spooler.HandleError(event, err, u.view.GetLatestUserGrantFailedEvent, u.view.ProcessedUserGrantFailedEvent, u.view.ProcessedUserGrantSequence, u.errorCountUntilSkip)
|
||||
}
|
||||
|
||||
func (u *UserGrant) OnSuccess() error {
|
||||
return spooler.HandleSuccess(u.view.UpdateUserGrantSpoolerRunTimestamp)
|
||||
}
|
||||
|
||||
func (u *UserGrant) getUserByID(userID string) (*usr_view_model.UserView, error) {
|
||||
user, usrErr := u.view.UserByID(userID)
|
||||
if usrErr != nil && !caos_errs.IsNotFound(usrErr) {
|
||||
return nil, usrErr
|
||||
}
|
||||
if user == nil {
|
||||
user = &usr_view_model.UserView{}
|
||||
}
|
||||
events, err := u.getUserEvents(userID, user.Sequence)
|
||||
if err != nil {
|
||||
return user, usrErr
|
||||
}
|
||||
userCopy := *user
|
||||
for _, event := range events {
|
||||
if err := userCopy.AppendEvent(event); err != nil {
|
||||
return user, nil
|
||||
}
|
||||
}
|
||||
if userCopy.State == int32(usr_model.UserStateDeleted) {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "HANDLER-m9dos", "Errors.User.NotFound")
|
||||
}
|
||||
return &userCopy, nil
|
||||
}
|
||||
|
||||
func (u *UserGrant) getUserEvents(userID string, sequence uint64) ([]*es_models.Event, error) {
|
||||
query, err := view.UserByIDQuery(userID, sequence)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return u.es.FilterEvents(context.Background(), query)
|
||||
}
|
||||
|
||||
func (u *UserGrant) getOrgByID(ctx context.Context, orgID string) (*org_model.Org, error) {
|
||||
query, err := org_view.OrgByIDQuery(orgID, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
esOrg := &org_es_model.Org{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
AggregateID: orgID,
|
||||
},
|
||||
}
|
||||
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esOrg.AppendEvents, query)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if esOrg.Sequence == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-kVLb2", "Errors.Org.NotFound")
|
||||
}
|
||||
|
||||
return org_es_model.OrgToModel(esOrg), nil
|
||||
}
|
||||
|
||||
func (u *UserGrant) getProjectByID(ctx context.Context, projID string) (*proj_model.Project, error) {
|
||||
query, err := proj_view.ProjectByIDQuery(projID, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
esProject := &proj_es_model.Project{
|
||||
ObjectRoot: es_models.ObjectRoot{
|
||||
AggregateID: projID,
|
||||
},
|
||||
}
|
||||
err = es_sdk.Filter(ctx, u.Eventstore().FilterEvents, esProject.AppendEvents, query)
|
||||
if err != nil && !caos_errs.IsNotFound(err) {
|
||||
return nil, err
|
||||
}
|
||||
if esProject.Sequence == 0 {
|
||||
return nil, caos_errs.ThrowNotFound(nil, "EVENT-Dfb42", "Errors.Project.NotFound")
|
||||
}
|
||||
|
||||
return proj_es_model.ProjectToModel(esProject), nil
|
||||
}
|
@ -29,7 +29,6 @@ type EsRepository struct {
|
||||
eventstore.OrgRepository
|
||||
eventstore.ProjectRepo
|
||||
eventstore.UserRepo
|
||||
eventstore.UserGrantRepo
|
||||
eventstore.IAMRepository
|
||||
view *mgmt_view.View
|
||||
}
|
||||
@ -76,7 +75,6 @@ func Start(conf Config, systemDefaults sd.SystemDefaults, roles []string, querie
|
||||
},
|
||||
ProjectRepo: eventstore.ProjectRepo{es, conf.SearchLimit, view, roles, systemDefaults.IamID, assetsAPI, queries},
|
||||
UserRepo: eventstore.UserRepo{es, conf.SearchLimit, view, systemDefaults, assetsAPI},
|
||||
UserGrantRepo: eventstore.UserGrantRepo{conf.SearchLimit, view, assetsAPI},
|
||||
IAMRepository: eventstore.IAMRepository{IAMV2Query: queries},
|
||||
view: view,
|
||||
}, nil
|
||||
|
@ -1,90 +0,0 @@
|
||||
package view
|
||||
|
||||
import (
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/eventstore/v1/models"
|
||||
grant_model "github.com/caos/zitadel/internal/usergrant/model"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view"
|
||||
"github.com/caos/zitadel/internal/usergrant/repository/view/model"
|
||||
"github.com/caos/zitadel/internal/view/repository"
|
||||
)
|
||||
|
||||
const (
|
||||
userGrantTable = "management.user_grants"
|
||||
)
|
||||
|
||||
func (v *View) UserGrantByID(grantID string) (*model.UserGrantView, error) {
|
||||
return view.UserGrantByID(v.Db, userGrantTable, grantID)
|
||||
}
|
||||
|
||||
func (v *View) SearchUserGrants(request *grant_model.UserGrantSearchRequest) ([]*model.UserGrantView, uint64, error) {
|
||||
return view.SearchUserGrants(v.Db, userGrantTable, request)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByUserID(userID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByUserID(v.Db, userGrantTable, userID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByProjectID(projectID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByProjectID(v.Db, userGrantTable, projectID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByProjectAndGrantID(projectID, grantID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByProjectAndGrantID(v.Db, userGrantTable, projectID, grantID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByOrgID(orgID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByOrgID(v.Db, userGrantTable, orgID)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByProjectIDAndRoleKey(projectID, roleKey string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByProjectIDAndRole(v.Db, userGrantTable, projectID, roleKey)
|
||||
}
|
||||
|
||||
func (v *View) UserGrantsByOrgIDAndProjectID(orgID, projectID string) ([]*model.UserGrantView, error) {
|
||||
return view.UserGrantsByOrgIDAndProjectID(v.Db, userGrantTable, orgID, projectID)
|
||||
}
|
||||
|
||||
func (v *View) PutUserGrant(grant *model.UserGrantView, event *models.Event) error {
|
||||
err := view.PutUserGrant(v.Db, userGrantTable, grant)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
|
||||
func (v *View) PutUserGrants(grants []*model.UserGrantView, event *models.Event) error {
|
||||
err := view.PutUserGrants(v.Db, userGrantTable, grants...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
|
||||
func (v *View) DeleteUserGrant(grantID string, event *models.Event) error {
|
||||
err := view.DeleteUserGrant(v.Db, userGrantTable, grantID)
|
||||
if err != nil && !errors.IsNotFound(err) {
|
||||
return err
|
||||
}
|
||||
return v.ProcessedUserGrantSequence(event)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestUserGrantSequence() (*repository.CurrentSequence, error) {
|
||||
return v.latestSequence(userGrantTable)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedUserGrantSequence(event *models.Event) error {
|
||||
return v.saveCurrentSequence(userGrantTable, event)
|
||||
}
|
||||
|
||||
func (v *View) UpdateUserGrantSpoolerRunTimestamp() error {
|
||||
return v.updateSpoolerRunSequence(userGrantTable)
|
||||
}
|
||||
|
||||
func (v *View) GetLatestUserGrantFailedEvent(sequence uint64) (*repository.FailedEvent, error) {
|
||||
return v.latestFailedEvent(userGrantTable, sequence)
|
||||
}
|
||||
|
||||
func (v *View) ProcessedUserGrantFailedEvent(failedEvent *repository.FailedEvent) error {
|
||||
return v.saveFailedEvent(failedEvent)
|
||||
}
|
@ -5,6 +5,5 @@ type Repository interface {
|
||||
ProjectRepository
|
||||
OrgRepository
|
||||
UserRepository
|
||||
UserGrantRepository
|
||||
IamRepository
|
||||
}
|
||||
|
@ -1,15 +0,0 @@
|
||||
package repository
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/caos/zitadel/internal/usergrant/model"
|
||||
)
|
||||
|
||||
type UserGrantRepository interface {
|
||||
UserGrantByID(ctx context.Context, grantID string) (*model.UserGrantView, error)
|
||||
SearchUserGrants(ctx context.Context, request *model.UserGrantSearchRequest) (*model.UserGrantSearchResponse, error)
|
||||
UserGrantsByProjectID(ctx context.Context, projectID string) ([]*model.UserGrantView, error)
|
||||
UserGrantsByProjectAndGrantID(ctx context.Context, projectID, grantID string) ([]*model.UserGrantView, error)
|
||||
UserGrantsByProjectIDAndRoleKey(ctx context.Context, projectID, roleKey string) ([]*model.UserGrantView, error)
|
||||
UserGrantsByUserID(ctx context.Context, userID string) ([]*model.UserGrantView, error)
|
||||
}
|
@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.iam_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.org_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.project_grant_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@ -25,7 +25,7 @@ var (
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_machines.name" +
|
||||
", zitadel.projections.users_humans.avater_key" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", COUNT(*) OVER () " +
|
||||
"FROM zitadel.projections.project_members as members " +
|
||||
"LEFT JOIN zitadel.projections.users_humans " +
|
||||
@ -48,7 +48,7 @@ var (
|
||||
"last_name",
|
||||
"display_name",
|
||||
"name",
|
||||
"avater_key",
|
||||
"avatar_key",
|
||||
"count",
|
||||
}
|
||||
)
|
||||
|
@ -40,6 +40,7 @@ const (
|
||||
UserStateCol = "state"
|
||||
UserSequenceCol = "sequence"
|
||||
UserUsernameCol = "username"
|
||||
UserTypeCol = "type"
|
||||
)
|
||||
|
||||
const (
|
||||
@ -53,7 +54,7 @@ const (
|
||||
HumanDisplayNameCol = "display_name"
|
||||
HumanPreferredLanguageCol = "preferred_language"
|
||||
HumanGenderCol = "gender"
|
||||
HumanAvaterURLCol = "avater_key"
|
||||
HumanAvaterURLCol = "avatar_key"
|
||||
|
||||
// email
|
||||
HumanEmailCol = "email"
|
||||
@ -203,6 +204,7 @@ func (p *UserProjection) reduceHumanAdded(event eventstore.Event) (*handler.Stat
|
||||
handler.NewCol(UserStateCol, domain.UserStateInitial),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
handler.NewCol(UserUsernameCol, e.UserName),
|
||||
handler.NewCol(UserTypeCol, domain.UserTypeHuman),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
@ -239,6 +241,7 @@ func (p *UserProjection) reduceHumanRegistered(event eventstore.Event) (*handler
|
||||
handler.NewCol(UserStateCol, domain.UserStateInitial),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
handler.NewCol(UserUsernameCol, e.UserName),
|
||||
handler.NewCol(UserTypeCol, domain.UserTypeHuman),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
@ -656,6 +659,7 @@ func (p *UserProjection) reduceMachineAdded(event eventstore.Event) (*handler.St
|
||||
handler.NewCol(UserStateCol, domain.UserStateInitial),
|
||||
handler.NewCol(UserSequenceCol, e.Sequence()),
|
||||
handler.NewCol(UserUsernameCol, e.UserName),
|
||||
handler.NewCol(UserTypeCol, domain.UserTypeMachine),
|
||||
},
|
||||
),
|
||||
crdb.AddCreateStatement(
|
||||
|
@ -50,7 +50,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -59,6 +59,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -107,7 +108,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -116,6 +117,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -159,7 +161,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -168,6 +170,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -216,7 +219,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -225,6 +228,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -273,7 +277,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -282,6 +286,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -325,7 +330,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -334,6 +339,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"user-name",
|
||||
domain.UserTypeHuman,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -1031,7 +1037,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avater_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
"users/agg-id/avatar",
|
||||
"agg-id",
|
||||
@ -1067,7 +1073,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avater_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedStmt: "UPDATE zitadel.projections.users_humans SET (avatar_key) = ($1) WHERE (user_id = $2)",
|
||||
expectedArgs: []interface{}{
|
||||
nil,
|
||||
"agg-id",
|
||||
@ -1098,7 +1104,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -1107,6 +1113,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -1143,7 +1150,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
executer: &testExecuter{
|
||||
executions: []execution{
|
||||
{
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username) VALUES ($1, $2, $3, $4, $5, $6, $7)",
|
||||
expectedStmt: "INSERT INTO zitadel.projections.users (id, creation_date, change_date, resource_owner, state, sequence, username, type) VALUES ($1, $2, $3, $4, $5, $6, $7, $8)",
|
||||
expectedArgs: []interface{}{
|
||||
"agg-id",
|
||||
anyArg{},
|
||||
@ -1152,6 +1159,7 @@ func TestUserProjection_reduces(t *testing.T) {
|
||||
domain.UserStateInitial,
|
||||
uint64(15),
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -44,6 +44,7 @@ const sqlPlaceholder = "?"
|
||||
|
||||
type SearchQuery interface {
|
||||
toQuery(sq.SelectBuilder) sq.SelectBuilder
|
||||
comp() sq.Sqlizer
|
||||
}
|
||||
|
||||
type NotNullQuery struct {
|
||||
@ -60,7 +61,34 @@ func NewNotNullQuery(col Column) (*NotNullQuery, error) {
|
||||
}
|
||||
|
||||
func (q *NotNullQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
return query.Where(sq.NotEq{q.Column.identifier(): nil})
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (q *NotNullQuery) comp() sq.Sqlizer {
|
||||
return sq.NotEq{q.Column.identifier(): nil}
|
||||
}
|
||||
|
||||
type orQuery struct {
|
||||
queries []SearchQuery
|
||||
}
|
||||
|
||||
func newOrQuery(queries ...SearchQuery) (*orQuery, error) {
|
||||
if len(queries) == 0 {
|
||||
return nil, ErrMissingColumn
|
||||
}
|
||||
return &orQuery{queries: queries}, nil
|
||||
}
|
||||
|
||||
func (q *orQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (q *orQuery) comp() sq.Sqlizer {
|
||||
or := make(sq.Or, len(q.queries))
|
||||
for i, query := range q.queries {
|
||||
or[i] = query.comp()
|
||||
}
|
||||
return or
|
||||
}
|
||||
|
||||
type TextQuery struct {
|
||||
@ -90,32 +118,31 @@ func NewTextQuery(col Column, value string, compare TextComparison) (*TextQuery,
|
||||
}
|
||||
|
||||
func (q *TextQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *TextQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
func (s *TextQuery) comp() sq.Sqlizer {
|
||||
switch s.Compare {
|
||||
case TextEquals:
|
||||
return sq.Eq{s.Column.identifier(): s.Text}, nil
|
||||
return sq.Eq{s.Column.identifier(): s.Text}
|
||||
case TextEqualsIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): s.Text}, nil
|
||||
return sq.ILike{s.Column.identifier(): s.Text}
|
||||
case TextStartsWith:
|
||||
return sq.Like{s.Column.identifier(): s.Text + "%"}, nil
|
||||
return sq.Like{s.Column.identifier(): s.Text + "%"}
|
||||
case TextStartsWithIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): s.Text + "%"}, nil
|
||||
return sq.ILike{s.Column.identifier(): s.Text + "%"}
|
||||
case TextEndsWith:
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text}, nil
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text}
|
||||
case TextEndsWithIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text}, nil
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text}
|
||||
case TextContains:
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text + "%"}, nil
|
||||
return sq.Like{s.Column.identifier(): "%" + s.Text + "%"}
|
||||
case TextContainsIgnoreCase:
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text + "%"}, nil
|
||||
return sq.ILike{s.Column.identifier(): "%" + s.Text + "%"}
|
||||
case TextListContains:
|
||||
return s.Column.identifier() + " @> ? ", []interface{}{pq.StringArray{s.Text}}
|
||||
return &listContains{col: s.Column, args: []interface{}{pq.StringArray{s.Text}}}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type TextComparison int
|
||||
@ -187,24 +214,23 @@ func NewNumberQuery(c Column, value interface{}, compare NumberComparison) (*Num
|
||||
}
|
||||
|
||||
func (q *NumberQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *NumberQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
func (s *NumberQuery) comp() sq.Sqlizer {
|
||||
switch s.Compare {
|
||||
case NumberEquals:
|
||||
return sq.Eq{s.Column.identifier(): s.Number}, nil
|
||||
return sq.Eq{s.Column.identifier(): s.Number}
|
||||
case NumberNotEquals:
|
||||
return sq.NotEq{s.Column.identifier(): s.Number}, nil
|
||||
return sq.NotEq{s.Column.identifier(): s.Number}
|
||||
case NumberLess:
|
||||
return sq.Lt{s.Column.identifier(): s.Number}, nil
|
||||
return sq.Lt{s.Column.identifier(): s.Number}
|
||||
case NumberGreater:
|
||||
return sq.Gt{s.Column.identifier(): s.Number}, nil
|
||||
return sq.Gt{s.Column.identifier(): s.Number}
|
||||
case NumberListContains:
|
||||
return s.Column.identifier() + " @> ? ", []interface{}{pq.Array(s.Number)}
|
||||
return &listContains{col: s.Column, args: []interface{}{pq.GenericArray{s.Number}}}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type NumberComparison int
|
||||
@ -258,16 +284,15 @@ func NewListQuery(column Column, value []interface{}, compare ListComparison) (*
|
||||
}
|
||||
|
||||
func (q *ListQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *ListQuery) comp() (interface{}, []interface{}) {
|
||||
func (s *ListQuery) comp() sq.Sqlizer {
|
||||
switch s.Compare {
|
||||
case ListIn:
|
||||
return sq.Eq{s.Column.identifier(): s.List}, nil
|
||||
return sq.Eq{s.Column.identifier(): s.List}
|
||||
}
|
||||
return nil, nil
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListComparison int
|
||||
@ -300,12 +325,11 @@ func NewBoolQuery(c Column, value bool) (*BoolQuery, error) {
|
||||
}
|
||||
|
||||
func (q *BoolQuery) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
where, args := q.comp()
|
||||
return query.Where(where, args...)
|
||||
return query.Where(q.comp())
|
||||
}
|
||||
|
||||
func (s *BoolQuery) comp() (comparison interface{}, args []interface{}) {
|
||||
return sq.Eq{s.Column.identifier(): s.Value}, nil
|
||||
func (s *BoolQuery) comp() sq.Sqlizer {
|
||||
return sq.Eq{s.Column.identifier(): s.Value}
|
||||
}
|
||||
|
||||
var (
|
||||
@ -367,3 +391,12 @@ func (c Column) isZero() bool {
|
||||
func join(join, from Column) string {
|
||||
return join.table.identifier() + " ON " + from.identifier() + " = " + join.identifier()
|
||||
}
|
||||
|
||||
type listContains struct {
|
||||
col Column
|
||||
args []interface{}
|
||||
}
|
||||
|
||||
func (q *listContains) ToSql() (string, []interface{}, error) {
|
||||
return q.col.identifier() + " @> ? ", q.args, nil
|
||||
}
|
||||
|
@ -230,7 +230,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
}
|
||||
type want struct {
|
||||
query interface{}
|
||||
args []interface{}
|
||||
isNil bool
|
||||
}
|
||||
tests := []struct {
|
||||
@ -247,7 +246,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Eq{"test_table.test_col": "Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -259,7 +257,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -271,7 +268,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Like{"test_table.test_col": "Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -283,7 +279,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -295,7 +290,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Like{"test_table.test_col": "%Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -307,7 +301,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "%Hurst"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -319,7 +312,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Like{"test_table.test_col": "%Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -331,7 +323,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.ILike{"test_table.test_col": "%Hurst%"},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -342,10 +333,12 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
Compare: TextListContains,
|
||||
},
|
||||
want: want{
|
||||
query: "test_table.test_col @> ? ",
|
||||
query: &listContains{
|
||||
col: testCol,
|
||||
args: []interface{}{pq.StringArray{"Hurst"}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "too high comparison",
|
||||
fields: fields{
|
||||
@ -376,7 +369,7 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
Text: tt.fields.Text,
|
||||
Compare: tt.fields.Compare,
|
||||
}
|
||||
query, args := s.comp()
|
||||
query := s.comp()
|
||||
if query == nil && tt.want.isNil {
|
||||
return
|
||||
} else if tt.want.isNil && query != nil {
|
||||
@ -386,10 +379,6 @@ func TestTextQuery_comp(t *testing.T) {
|
||||
if !reflect.DeepEqual(query, tt.want.query) {
|
||||
t.Errorf("wrong query: want: %v, (%T), got: %v, (%T)", tt.want.query, tt.want.query, query, query)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(args, tt.want.args) {
|
||||
t.Errorf("wrong args: want: %v, (%T), got: %v (%T)", tt.want.args, tt.want.args, args, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -589,7 +578,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
}
|
||||
type want struct {
|
||||
query interface{}
|
||||
args []interface{}
|
||||
isNil bool
|
||||
}
|
||||
tests := []struct {
|
||||
@ -606,7 +594,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Eq{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -618,7 +605,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.NotEq{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -630,7 +616,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Lt{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -642,7 +627,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
},
|
||||
want: want{
|
||||
query: sq.Gt{"test_table.test_col": 42},
|
||||
args: nil,
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -653,8 +637,10 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
Compare: NumberListContains,
|
||||
},
|
||||
want: want{
|
||||
query: "test_table.test_col @> ? ",
|
||||
args: []interface{}{pq.Array(42)},
|
||||
query: &listContains{
|
||||
col: testCol,
|
||||
args: []interface{}{pq.GenericArray{42}},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
@ -687,7 +673,7 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
Number: tt.fields.Number,
|
||||
Compare: tt.fields.Compare,
|
||||
}
|
||||
query, args := s.comp()
|
||||
query := s.comp()
|
||||
if query == nil && tt.want.isNil {
|
||||
return
|
||||
} else if tt.want.isNil && query != nil {
|
||||
@ -697,10 +683,6 @@ func TestNumberQuery_comp(t *testing.T) {
|
||||
if !reflect.DeepEqual(query, tt.want.query) {
|
||||
t.Errorf("wrong query: want: %v, (%T), got: %v, (%T)", tt.want.query, tt.want.query, query, query)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(args, tt.want.args) {
|
||||
t.Errorf("wrong args: want: %v, (%T), got: %v (%T)", tt.want.args, tt.want.args, args, args)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -39,6 +39,10 @@ var (
|
||||
name: projection.UserUsernameCol,
|
||||
table: userTable,
|
||||
}
|
||||
UserTypeCol = Column{
|
||||
name: projection.UserTypeCol,
|
||||
table: userTable,
|
||||
}
|
||||
)
|
||||
|
||||
var (
|
||||
|
449
internal/query/user_grant.go
Normal file
449
internal/query/user_grant.go
Normal file
@ -0,0 +1,449 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"context"
|
||||
"database/sql"
|
||||
errs "errors"
|
||||
"time"
|
||||
|
||||
sq "github.com/Masterminds/squirrel"
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
"github.com/caos/zitadel/internal/errors"
|
||||
"github.com/caos/zitadel/internal/query/projection"
|
||||
)
|
||||
|
||||
type UserGrant struct {
|
||||
ID string
|
||||
CreationDate time.Time
|
||||
ChangeDate time.Time
|
||||
Sequence uint64
|
||||
Roles []string
|
||||
GrantID string
|
||||
State domain.UserGrantState
|
||||
|
||||
UserID string
|
||||
Username string
|
||||
UserType domain.UserType
|
||||
UserResourceOwner string
|
||||
FirstName string
|
||||
LastName string
|
||||
Email string
|
||||
DisplayName string
|
||||
AvatarURL string
|
||||
|
||||
ResourceOwner string
|
||||
OrgName string
|
||||
OrgPrimaryDomain string
|
||||
|
||||
ProjectID string
|
||||
ProjectName string
|
||||
}
|
||||
|
||||
type UserGrants struct {
|
||||
SearchResponse
|
||||
UserGrants []*UserGrant
|
||||
}
|
||||
|
||||
type UserGrantsQueries struct {
|
||||
SearchRequest
|
||||
Queries []SearchQuery
|
||||
}
|
||||
|
||||
func (q *UserGrantsQueries) toQuery(query sq.SelectBuilder) sq.SelectBuilder {
|
||||
query = q.SearchRequest.toQuery(query)
|
||||
for _, q := range q.Queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
return query
|
||||
}
|
||||
|
||||
func NewUserGrantUserIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantUserID, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantProjectIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantProjectID, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantProjectOwnerSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(ProjectColumnResourceOwner, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantResourceOwnerSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantResourceOwner, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantGrantIDSearchQuery(id string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantGrantID, id, TextEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantUserTypeQuery(typ domain.UserType) (SearchQuery, error) {
|
||||
return NewNumberQuery(UserTypeCol, typ, NumberEquals)
|
||||
}
|
||||
|
||||
func NewUserGrantDisplayNameQuery(displayName string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanDisplayNameCol, displayName, method)
|
||||
}
|
||||
|
||||
func NewUserGrantEmailQuery(email string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanEmailCol, email, method)
|
||||
}
|
||||
|
||||
func NewUserGrantFirstNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanFirstNameCol, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantLastNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(HumanLastNameCol, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantUsernameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(UserUsernameCol, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantDomainQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(OrgColumnDomain, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantOrgNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(OrgColumnName, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantProjectNameQuery(value string, method TextComparison) (SearchQuery, error) {
|
||||
return NewTextQuery(ProjectColumnName, value, method)
|
||||
}
|
||||
|
||||
func NewUserGrantRoleQuery(value string) (SearchQuery, error) {
|
||||
return NewTextQuery(UserGrantRoles, value, TextListContains)
|
||||
}
|
||||
|
||||
func NewUserGrantWithGrantedQuery(owner string) (SearchQuery, error) {
|
||||
orgQuery, err := NewUserGrantResourceOwnerSearchQuery(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
projectQuery, err := NewUserGrantProjectOwnerSearchQuery(owner)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newOrQuery(orgQuery, projectQuery)
|
||||
}
|
||||
|
||||
func NewUserGrantContainsRolesSearchQuery(roles ...string) (SearchQuery, error) {
|
||||
r := make([]interface{}, len(roles))
|
||||
for i, role := range roles {
|
||||
r[i] = role
|
||||
}
|
||||
return NewListQuery(UserGrantRoles, r, ListIn)
|
||||
}
|
||||
|
||||
var (
|
||||
userGrantTable = table{
|
||||
name: projection.UserGrantProjectionTable,
|
||||
}
|
||||
UserGrantID = Column{
|
||||
name: projection.UserGrantID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantResourceOwner = Column{
|
||||
name: projection.UserGrantResourceOwner,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantCreationDate = Column{
|
||||
name: projection.UserGrantCreationDate,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantChangeDate = Column{
|
||||
name: projection.UserGrantChangeDate,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantSequence = Column{
|
||||
name: projection.UserGrantSequence,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantUserID = Column{
|
||||
name: projection.UserGrantUserID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantProjectID = Column{
|
||||
name: projection.UserGrantProjectID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantGrantID = Column{
|
||||
name: projection.UserGrantGrantID,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantRoles = Column{
|
||||
name: projection.UserGrantRoles,
|
||||
table: userGrantTable,
|
||||
}
|
||||
UserGrantState = Column{
|
||||
name: projection.UserGrantState,
|
||||
table: userGrantTable,
|
||||
}
|
||||
)
|
||||
|
||||
func (q *Queries) UserGrantByID(ctx context.Context, id string, queries ...SearchQuery) (*UserGrant, error) {
|
||||
query, scan := prepareUserGrantQuery()
|
||||
for _, q := range queries {
|
||||
query = q.toQuery(query)
|
||||
}
|
||||
stmt, args, err := query.Where(sq.Eq{
|
||||
UserGrantID.identifier(): id,
|
||||
}).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-Fa1KW", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
row := q.client.QueryRowContext(ctx, stmt, args...)
|
||||
return scan(row)
|
||||
}
|
||||
|
||||
func (q *Queries) UserGrants(ctx context.Context, queries *UserGrantsQueries) (*UserGrants, error) {
|
||||
query, scan := prepareUserGrantsQuery()
|
||||
stmt, args, err := queries.toQuery(query).ToSql()
|
||||
if err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-wXnQR", "Errors.Query.SQLStatement")
|
||||
}
|
||||
|
||||
latestSequence, err := q.latestSequence(ctx, userGrantTable)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rows, err := q.client.QueryContext(ctx, stmt, args...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grants, err := scan(rows)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grants.LatestSequence = latestSequence
|
||||
return grants, nil
|
||||
}
|
||||
|
||||
func prepareUserGrantQuery() (sq.SelectBuilder, func(*sql.Row) (*UserGrant, error)) {
|
||||
return sq.Select(
|
||||
UserGrantID.identifier(),
|
||||
UserGrantCreationDate.identifier(),
|
||||
UserGrantChangeDate.identifier(),
|
||||
UserGrantSequence.identifier(),
|
||||
UserGrantGrantID.identifier(),
|
||||
UserGrantRoles.identifier(),
|
||||
UserGrantState.identifier(),
|
||||
|
||||
UserGrantUserID.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
UserTypeCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanAvaterURLCol.identifier(),
|
||||
|
||||
UserGrantResourceOwner.identifier(),
|
||||
OrgColumnName.identifier(),
|
||||
OrgColumnDomain.identifier(),
|
||||
|
||||
UserGrantProjectID.identifier(),
|
||||
ProjectColumnName.identifier(),
|
||||
).
|
||||
From(userGrantTable.identifier()).
|
||||
LeftJoin(join(UserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(HumanUserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(OrgColumnID, UserGrantResourceOwner)).
|
||||
LeftJoin(join(ProjectColumnID, UserGrantProjectID)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(row *sql.Row) (*UserGrant, error) {
|
||||
g := new(UserGrant)
|
||||
|
||||
var (
|
||||
roles = pq.StringArray{}
|
||||
username sql.NullString
|
||||
firstName sql.NullString
|
||||
userType sql.NullInt32
|
||||
userOwner sql.NullString
|
||||
lastName sql.NullString
|
||||
email sql.NullString
|
||||
displayName sql.NullString
|
||||
avatarURL sql.NullString
|
||||
|
||||
orgName sql.NullString
|
||||
orgDomain sql.NullString
|
||||
|
||||
projectName sql.NullString
|
||||
)
|
||||
|
||||
err := row.Scan(
|
||||
&g.ID,
|
||||
&g.CreationDate,
|
||||
&g.ChangeDate,
|
||||
&g.Sequence,
|
||||
&g.GrantID,
|
||||
&roles,
|
||||
&g.State,
|
||||
|
||||
&g.UserID,
|
||||
&username,
|
||||
&userType,
|
||||
&userOwner,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&email,
|
||||
&displayName,
|
||||
&avatarURL,
|
||||
|
||||
&g.ResourceOwner,
|
||||
&orgName,
|
||||
&orgDomain,
|
||||
|
||||
&g.ProjectID,
|
||||
&projectName,
|
||||
)
|
||||
if err != nil {
|
||||
if errs.Is(err, sql.ErrNoRows) {
|
||||
return nil, errors.ThrowNotFound(err, "QUERY-wIPkA", "Errors.UserGrant.NotFound")
|
||||
}
|
||||
return nil, errors.ThrowInternal(err, "QUERY-oQPcP", "Errors.Internal")
|
||||
}
|
||||
|
||||
g.Roles = roles
|
||||
g.Username = username.String
|
||||
g.UserType = domain.UserType(userType.Int32)
|
||||
g.UserResourceOwner = userOwner.String
|
||||
g.FirstName = firstName.String
|
||||
g.LastName = lastName.String
|
||||
g.Email = email.String
|
||||
g.DisplayName = displayName.String
|
||||
g.AvatarURL = avatarURL.String
|
||||
g.OrgName = orgName.String
|
||||
g.OrgPrimaryDomain = orgDomain.String
|
||||
g.ProjectName = projectName.String
|
||||
|
||||
return g, nil
|
||||
}
|
||||
}
|
||||
|
||||
func prepareUserGrantsQuery() (sq.SelectBuilder, func(*sql.Rows) (*UserGrants, error)) {
|
||||
return sq.Select(
|
||||
UserGrantID.identifier(),
|
||||
UserGrantCreationDate.identifier(),
|
||||
UserGrantChangeDate.identifier(),
|
||||
UserGrantSequence.identifier(),
|
||||
UserGrantGrantID.identifier(),
|
||||
UserGrantRoles.identifier(),
|
||||
UserGrantState.identifier(),
|
||||
|
||||
UserGrantUserID.identifier(),
|
||||
UserUsernameCol.identifier(),
|
||||
UserTypeCol.identifier(),
|
||||
UserResourceOwnerCol.identifier(),
|
||||
HumanFirstNameCol.identifier(),
|
||||
HumanLastNameCol.identifier(),
|
||||
HumanEmailCol.identifier(),
|
||||
HumanDisplayNameCol.identifier(),
|
||||
HumanAvaterURLCol.identifier(),
|
||||
|
||||
UserGrantResourceOwner.identifier(),
|
||||
OrgColumnName.identifier(),
|
||||
OrgColumnDomain.identifier(),
|
||||
|
||||
UserGrantProjectID.identifier(),
|
||||
ProjectColumnName.identifier(),
|
||||
|
||||
countColumn.identifier(),
|
||||
).
|
||||
From(userGrantTable.identifier()).
|
||||
LeftJoin(join(UserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(HumanUserIDCol, UserGrantUserID)).
|
||||
LeftJoin(join(OrgColumnID, UserGrantResourceOwner)).
|
||||
LeftJoin(join(ProjectColumnID, UserGrantProjectID)).
|
||||
PlaceholderFormat(sq.Dollar),
|
||||
func(rows *sql.Rows) (*UserGrants, error) {
|
||||
userGrants := make([]*UserGrant, 0)
|
||||
var count uint64
|
||||
for rows.Next() {
|
||||
g := new(UserGrant)
|
||||
|
||||
var (
|
||||
roles = pq.StringArray{}
|
||||
username sql.NullString
|
||||
userType sql.NullInt32
|
||||
userOwner sql.NullString
|
||||
firstName sql.NullString
|
||||
lastName sql.NullString
|
||||
email sql.NullString
|
||||
displayName sql.NullString
|
||||
avatarURL sql.NullString
|
||||
|
||||
orgName sql.NullString
|
||||
orgDomain sql.NullString
|
||||
|
||||
projectName sql.NullString
|
||||
)
|
||||
|
||||
err := rows.Scan(
|
||||
&g.ID,
|
||||
&g.CreationDate,
|
||||
&g.ChangeDate,
|
||||
&g.Sequence,
|
||||
&g.GrantID,
|
||||
&roles,
|
||||
&g.State,
|
||||
|
||||
&g.UserID,
|
||||
&username,
|
||||
&userType,
|
||||
&userOwner,
|
||||
&firstName,
|
||||
&lastName,
|
||||
&email,
|
||||
&displayName,
|
||||
&avatarURL,
|
||||
|
||||
&g.ResourceOwner,
|
||||
&orgName,
|
||||
&orgDomain,
|
||||
|
||||
&g.ProjectID,
|
||||
&projectName,
|
||||
|
||||
&count,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g.Roles = roles
|
||||
g.Username = username.String
|
||||
g.UserType = domain.UserType(userType.Int32)
|
||||
g.UserResourceOwner = userOwner.String
|
||||
g.FirstName = firstName.String
|
||||
g.LastName = lastName.String
|
||||
g.Email = email.String
|
||||
g.DisplayName = displayName.String
|
||||
g.AvatarURL = avatarURL.String
|
||||
g.OrgName = orgName.String
|
||||
g.OrgPrimaryDomain = orgDomain.String
|
||||
g.ProjectName = projectName.String
|
||||
|
||||
userGrants = append(userGrants, g)
|
||||
}
|
||||
|
||||
if err := rows.Close(); err != nil {
|
||||
return nil, errors.ThrowInternal(err, "QUERY-iGvmP", "Errors.Query.CloseRows")
|
||||
}
|
||||
|
||||
return &UserGrants{
|
||||
UserGrants: userGrants,
|
||||
SearchResponse: SearchResponse{
|
||||
Count: count,
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
}
|
778
internal/query/user_grant_test.go
Normal file
778
internal/query/user_grant_test.go
Normal file
@ -0,0 +1,778 @@
|
||||
package query
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"database/sql/driver"
|
||||
"errors"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"testing"
|
||||
|
||||
"github.com/lib/pq"
|
||||
|
||||
"github.com/caos/zitadel/internal/domain"
|
||||
errs "github.com/caos/zitadel/internal/errors"
|
||||
)
|
||||
|
||||
var (
|
||||
userGrantStmt = regexp.QuoteMeta(
|
||||
"SELECT zitadel.projections.user_grants.id" +
|
||||
", zitadel.projections.user_grants.creation_date" +
|
||||
", zitadel.projections.user_grants.change_date" +
|
||||
", zitadel.projections.user_grants.sequence" +
|
||||
", zitadel.projections.user_grants.grant_id" +
|
||||
", zitadel.projections.user_grants.roles" +
|
||||
", zitadel.projections.user_grants.state" +
|
||||
", zitadel.projections.user_grants.user_id" +
|
||||
", zitadel.projections.users.username" +
|
||||
", zitadel.projections.users.type" +
|
||||
", zitadel.projections.users.resource_owner" +
|
||||
", zitadel.projections.users_humans.first_name" +
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.email" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", zitadel.projections.user_grants.resource_owner" +
|
||||
", zitadel.projections.orgs.name" +
|
||||
", zitadel.projections.orgs.primary_domain" +
|
||||
", zitadel.projections.user_grants.project_id" +
|
||||
", zitadel.projections.projects.name" +
|
||||
" FROM zitadel.projections.user_grants" +
|
||||
" LEFT JOIN zitadel.projections.users ON zitadel.projections.user_grants.user_id = zitadel.projections.users.id" +
|
||||
" LEFT JOIN zitadel.projections.users_humans ON zitadel.projections.user_grants.user_id = zitadel.projections.users_humans.user_id" +
|
||||
" LEFT JOIN zitadel.projections.orgs ON zitadel.projections.user_grants.resource_owner = zitadel.projections.orgs.id" +
|
||||
" LEFT JOIN zitadel.projections.projects ON zitadel.projections.user_grants.project_id = zitadel.projections.projects.id")
|
||||
userGrantCols = []string{
|
||||
"id",
|
||||
"creation_date",
|
||||
"change_date",
|
||||
"sequence",
|
||||
"grant_id",
|
||||
"roles",
|
||||
"state",
|
||||
"user_id",
|
||||
"username",
|
||||
"type",
|
||||
"resource_owner", //user resource owner
|
||||
"first_name",
|
||||
"last_name",
|
||||
"email",
|
||||
"display_name",
|
||||
"avatar_key",
|
||||
"resource_owner", //user_grant resource owner
|
||||
"name", //org name
|
||||
"primary_domain",
|
||||
"project_id",
|
||||
"name", //project name
|
||||
}
|
||||
userGrantsStmt = regexp.QuoteMeta(
|
||||
"SELECT zitadel.projections.user_grants.id" +
|
||||
", zitadel.projections.user_grants.creation_date" +
|
||||
", zitadel.projections.user_grants.change_date" +
|
||||
", zitadel.projections.user_grants.sequence" +
|
||||
", zitadel.projections.user_grants.grant_id" +
|
||||
", zitadel.projections.user_grants.roles" +
|
||||
", zitadel.projections.user_grants.state" +
|
||||
", zitadel.projections.user_grants.user_id" +
|
||||
", zitadel.projections.users.username" +
|
||||
", zitadel.projections.users.type" +
|
||||
", zitadel.projections.users.resource_owner" +
|
||||
", zitadel.projections.users_humans.first_name" +
|
||||
", zitadel.projections.users_humans.last_name" +
|
||||
", zitadel.projections.users_humans.email" +
|
||||
", zitadel.projections.users_humans.display_name" +
|
||||
", zitadel.projections.users_humans.avatar_key" +
|
||||
", zitadel.projections.user_grants.resource_owner" +
|
||||
", zitadel.projections.orgs.name" +
|
||||
", zitadel.projections.orgs.primary_domain" +
|
||||
", zitadel.projections.user_grants.project_id" +
|
||||
", zitadel.projections.projects.name" +
|
||||
", COUNT(*) OVER ()" +
|
||||
" FROM zitadel.projections.user_grants" +
|
||||
" LEFT JOIN zitadel.projections.users ON zitadel.projections.user_grants.user_id = zitadel.projections.users.id" +
|
||||
" LEFT JOIN zitadel.projections.users_humans ON zitadel.projections.user_grants.user_id = zitadel.projections.users_humans.user_id" +
|
||||
" LEFT JOIN zitadel.projections.orgs ON zitadel.projections.user_grants.resource_owner = zitadel.projections.orgs.id" +
|
||||
" LEFT JOIN zitadel.projections.projects ON zitadel.projections.user_grants.project_id = zitadel.projections.projects.id")
|
||||
userGrantsCols = append(
|
||||
userGrantCols,
|
||||
"count",
|
||||
)
|
||||
)
|
||||
|
||||
func Test_UserGrantPrepares(t *testing.T) {
|
||||
type want struct {
|
||||
sqlExpectations sqlExpectation
|
||||
err checkErr
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
prepare interface{}
|
||||
want want
|
||||
object interface{}
|
||||
}{
|
||||
{
|
||||
name: "prepareUserGrantQuery no result",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantStmt,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
err: func(err error) (error, bool) {
|
||||
if !errs.IsNotFound(err) {
|
||||
return fmt.Errorf("err should be zitadel.NotFoundError got: %w", err), false
|
||||
}
|
||||
return nil, true
|
||||
},
|
||||
},
|
||||
object: (*UserGrant)(nil),
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery machine user found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
"resource-owner",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeMachine,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
Email: "",
|
||||
DisplayName: "",
|
||||
AvatarURL: "",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery (no org) found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
nil,
|
||||
nil,
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "",
|
||||
OrgPrimaryDomain: "",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery (no project) found",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQuery(
|
||||
userGrantStmt,
|
||||
userGrantCols,
|
||||
[]driver.Value{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
nil,
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrant{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "",
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantQuery sql err",
|
||||
prepare: prepareUserGrantQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueryErr(
|
||||
userGrantStmt,
|
||||
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,
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery no result",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
nil,
|
||||
nil,
|
||||
),
|
||||
},
|
||||
object: &UserGrants{UserGrants: []*UserGrant{}},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant (machine user)",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
"resource-owner",
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
nil,
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeMachine,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
Email: "",
|
||||
DisplayName: "",
|
||||
AvatarURL: "",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant (no org)",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeMachine,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
nil,
|
||||
nil,
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeMachine,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "",
|
||||
OrgPrimaryDomain: "",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery one grant (no project)",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
nil,
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 1,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery multiple grants",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueries(
|
||||
userGrantsStmt,
|
||||
userGrantsCols,
|
||||
[][]driver.Value{
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
{
|
||||
"id",
|
||||
testNow,
|
||||
testNow,
|
||||
20211111,
|
||||
"grant-id",
|
||||
pq.StringArray{"role-key"},
|
||||
domain.UserGrantStateActive,
|
||||
"user-id",
|
||||
"username",
|
||||
domain.UserTypeHuman,
|
||||
"resource-owner",
|
||||
"first-name",
|
||||
"last-name",
|
||||
"email",
|
||||
"display-name",
|
||||
"avatar-key",
|
||||
"ro",
|
||||
"org-name",
|
||||
"primary-domain",
|
||||
"project-id",
|
||||
"project-name",
|
||||
},
|
||||
},
|
||||
),
|
||||
},
|
||||
object: &UserGrants{
|
||||
SearchResponse: SearchResponse{
|
||||
Count: 2,
|
||||
},
|
||||
UserGrants: []*UserGrant{
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
{
|
||||
ID: "id",
|
||||
CreationDate: testNow,
|
||||
ChangeDate: testNow,
|
||||
Sequence: 20211111,
|
||||
Roles: []string{"role-key"},
|
||||
GrantID: "grant-id",
|
||||
State: domain.UserGrantStateActive,
|
||||
UserID: "user-id",
|
||||
Username: "username",
|
||||
UserType: domain.UserTypeHuman,
|
||||
UserResourceOwner: "resource-owner",
|
||||
FirstName: "first-name",
|
||||
LastName: "last-name",
|
||||
Email: "email",
|
||||
DisplayName: "display-name",
|
||||
AvatarURL: "avatar-key",
|
||||
ResourceOwner: "ro",
|
||||
OrgName: "org-name",
|
||||
OrgPrimaryDomain: "primary-domain",
|
||||
ProjectID: "project-id",
|
||||
ProjectName: "project-name",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "prepareUserGrantsQuery sql err",
|
||||
prepare: prepareUserGrantsQuery,
|
||||
want: want{
|
||||
sqlExpectations: mockQueryErr(
|
||||
userGrantsStmt,
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
@ -102,11 +102,3 @@ func (r *UserGrantSearchRequest) GetSearchQuery(key UserGrantSearchKey) (int, *U
|
||||
}
|
||||
return -1, nil
|
||||
}
|
||||
|
||||
func (r *UserGrantSearchRequest) AppendMyOrgQuery(orgID string) {
|
||||
r.Queries = append(r.Queries, &UserGrantSearchQuery{Key: UserGrantSearchKeyResourceOwner, Method: domain.SearchMethodEquals, Value: orgID})
|
||||
}
|
||||
|
||||
func (r *UserGrantSearchRequest) AppendProjectIDQuery(projectID string) {
|
||||
r.Queries = append(r.Queries, &UserGrantSearchQuery{Key: UserGrantSearchKeyProjectID, Method: domain.SearchMethodEquals, Value: projectID})
|
||||
}
|
||||
|
15
migrations/cockroach/V1.105__user_type.sql
Normal file
15
migrations/cockroach/V1.105__user_type.sql
Normal file
@ -0,0 +1,15 @@
|
||||
ALTER TABLE zitadel.projections.users ADD COLUMN type INT2;
|
||||
|
||||
-- human is 1
|
||||
-- machine is 2
|
||||
WITH doa AS (
|
||||
SELECT u.id, IF(h.user_id IS NULL, 2, 1) as type
|
||||
FROM zitadel.projections.users u
|
||||
LEFT JOIN zitadel.projections.users_humans h
|
||||
ON h.user_id = u.id
|
||||
LEFT JOIN zitadel.projections.users_machines m
|
||||
ON m.user_id = u.id
|
||||
)
|
||||
UPDATE zitadel.projections.users SET type = doa.type FROM doa WHERE doa.id = zitadel.projections.users.id;
|
||||
|
||||
ALTER TABLE zitadel.projections.users_humans RENAME COLUMN avater_key to avatar_key;
|
@ -684,6 +684,7 @@ message UserGrantQuery {
|
||||
UserGrantOrgDomainQuery org_domain_query = 11;
|
||||
UserGrantProjectNameQuery project_name_query = 12;
|
||||
UserGrantDisplayNameQuery display_name_query = 13;
|
||||
UserGrantUserTypeQuery user_type_query = 14;
|
||||
}
|
||||
}
|
||||
|
||||
@ -853,4 +854,13 @@ message UserGrantDisplayNameQuery {
|
||||
];
|
||||
}
|
||||
|
||||
message UserGrantUserTypeQuery {
|
||||
Type type = 1 [
|
||||
(grpc.gateway.protoc_gen_openapiv2.options.openapiv2_field) = {
|
||||
description: "type of user"
|
||||
example: "\"TYPE_HUMAN\"";
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
//PLANNED: login name query
|
Loading…
x
Reference in New Issue
Block a user