mirror of
https://github.com/andwati/zitadel.git
synced 2026-11-14 09:55:12 +00:00
feat: user profile requests in resource APIs (#10151)
# Which Problems Are Solved The commands for the resource based v2beta AuthorizationService API are added. Authorizations, previously knows as user grants, give a user in a specific organization and project context roles. The project can be owned or granted. The given roles can be used to restrict access within the projects applications. The commands for the resource based v2beta InteralPermissionService API are added. Administrators, previously knows as memberships, give a user in a specific organization and project context roles. The project can be owned or granted. The give roles give the user permissions to manage different resources in Zitadel. API definitions from https://github.com/zitadel/zitadel/issues/9165 are implemented. Contains endpoints for user metadata. # How the Problems Are Solved ### New Methods - CreateAuthorization - UpdateAuthorization - DeleteAuthorization - ActivateAuthorization - DeactivateAuthorization - ListAuthorizations - CreateAdministrator - UpdateAdministrator - DeleteAdministrator - ListAdministrators - SetUserMetadata to set metadata on a user - DeleteUserMetadata to delete metadata on a user - ListUserMetadata to query for metadata of a user ## Deprecated Methods ### v1.ManagementService - GetUserGrantByID - ListUserGrants - AddUserGrant - UpdateUserGrant - DeactivateUserGrant - ReactivateUserGrant - RemoveUserGrant - BulkRemoveUserGrant ### v1.AuthService - ListMyUserGrants - ListMyProjectPermissions # Additional Changes - Permission checks for metadata functionality on query and command side - correct existence checks for resources, for example you can only be an administrator on an existing project - combined all member tables to singular query for the administrators - add permission checks for command an query side functionality - combined functions on command side where necessary for easier maintainability # Additional Context Closes #9165 --------- Co-authored-by: Elio Bischof <[email protected]> Co-authored-by: Copilot <[email protected]> Co-authored-by: Livio Spring <[email protected]>
This commit is contained in:
co-authored by
Elio Bischof
Copilot
Livio Spring
parent
9ebf2316c6
commit
5403be7c4b
@@ -782,12 +782,3 @@ func TestServer_ListExecutions(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func containExecution(t *assert.CollectT, executionList []*action.Execution, execution *action.Execution) bool {
|
||||
for _, exec := range executionList {
|
||||
if assert.EqualExportedValues(t, execution, exec) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -659,7 +659,7 @@ func (s *Server) getUsers(ctx context.Context, org string, withPasswords bool, w
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
}
|
||||
metadataList, err := s.query.SearchUserMetadata(ctx, false, user.ID, &query.UserMetadataSearchQueries{Queries: []query.SearchQuery{metadataOrgSearch}}, false)
|
||||
metadataList, err := s.query.SearchUserMetadata(ctx, false, user.ID, &query.UserMetadataSearchQueries{Queries: []query.SearchQuery{metadataOrgSearch}}, nil)
|
||||
metaspan.EndWithError(err)
|
||||
if err != nil {
|
||||
return nil, nil, nil, nil, err
|
||||
@@ -984,7 +984,7 @@ func (s *Server) getNecessaryUserGrantsForOrg(ctx context.Context, org string, p
|
||||
return nil, err
|
||||
}
|
||||
|
||||
queriedUserGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantSearchOrg}}, true)
|
||||
queriedUserGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantSearchOrg}}, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/member"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
@@ -33,35 +34,35 @@ func (s *Server) ListIAMMembers(ctx context.Context, req *admin_pb.ListIAMMember
|
||||
}
|
||||
|
||||
func (s *Server) AddIAMMember(ctx context.Context, req *admin_pb.AddIAMMemberRequest) (*admin_pb.AddIAMMemberResponse, error) {
|
||||
member, err := s.command.AddInstanceMember(ctx, req.UserId, req.Roles...)
|
||||
member, err := s.command.AddInstanceMember(ctx, AddIAMMemberToCommand(req, authz.GetInstance(ctx).InstanceID()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.AddIAMMemberResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
member.Sequence,
|
||||
member.ChangeDate,
|
||||
member.EventDate,
|
||||
member.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateIAMMember(ctx context.Context, req *admin_pb.UpdateIAMMemberRequest) (*admin_pb.UpdateIAMMemberResponse, error) {
|
||||
member, err := s.command.ChangeInstanceMember(ctx, UpdateIAMMemberToDomain(req))
|
||||
member, err := s.command.ChangeInstanceMember(ctx, UpdateIAMMemberToCommand(req, authz.GetInstance(ctx).InstanceID()))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &admin_pb.UpdateIAMMemberResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
member.Sequence,
|
||||
member.ChangeDate,
|
||||
member.EventDate,
|
||||
member.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) RemoveIAMMember(ctx context.Context, req *admin_pb.RemoveIAMMemberRequest) (*admin_pb.RemoveIAMMemberResponse, error) {
|
||||
objectDetails, err := s.command.RemoveInstanceMember(ctx, req.UserId)
|
||||
objectDetails, err := s.command.RemoveInstanceMember(ctx, authz.GetInstance(ctx).InstanceID(), req.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -3,23 +3,25 @@ package admin
|
||||
import (
|
||||
member_grpc "github.com/zitadel/zitadel/internal/api/grpc/member"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
admin_pb "github.com/zitadel/zitadel/pkg/grpc/admin"
|
||||
member_pb "github.com/zitadel/zitadel/pkg/grpc/member"
|
||||
)
|
||||
|
||||
func AddIAMMemberToDomain(req *admin_pb.AddIAMMemberRequest) *domain.Member {
|
||||
return &domain.Member{
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
func AddIAMMemberToCommand(req *admin_pb.AddIAMMemberRequest, instanceID string) *command.AddInstanceMember {
|
||||
return &command.AddInstanceMember{
|
||||
InstanceID: instanceID,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateIAMMemberToDomain(req *admin_pb.UpdateIAMMemberRequest) *domain.Member {
|
||||
return &domain.Member{
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
func UpdateIAMMemberToCommand(req *admin_pb.UpdateIAMMemberRequest, instanceID string) *command.ChangeInstanceMember {
|
||||
return &command.ChangeInstanceMember{
|
||||
InstanceID: instanceID,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ func TestAddIAMMemberToDomain(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := AddIAMMemberToDomain(tt.args.req)
|
||||
got := AddIAMMemberToCommand(tt.args.req, "INSTANCE")
|
||||
test.AssertFieldsMapped(t, got, "ObjectRoot")
|
||||
})
|
||||
}
|
||||
@@ -53,7 +53,7 @@ func TestUpdateIAMMemberToDomain(t *testing.T) {
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := UpdateIAMMemberToDomain(tt.args.req)
|
||||
got := UpdateIAMMemberToCommand(tt.args.req, "INSTANCE")
|
||||
test.AssertFieldsMapped(t, got, "ObjectRoot")
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1046,7 +1046,7 @@ func importOrg2(ctx context.Context, s *Server, errors *[]*admin_pb.ImportDataEr
|
||||
if org.UserGrants != nil {
|
||||
for _, grant := range org.GetUserGrants() {
|
||||
logging.Debugf("import usergrant: %s", grant.GetProjectId()+"_"+grant.GetUserId())
|
||||
_, err := s.command.AddUserGrant(ctx, management.AddUserGrantRequestToDomain(grant), org.GetOrgId())
|
||||
_, err := s.command.AddUserGrant(ctx, management.AddUserGrantRequestToDomain(grant, org.GetOrgId()), nil)
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "user_grant", Id: org.GetOrgId() + "_" + grant.GetProjectId() + "_" + grant.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
@@ -1088,7 +1088,7 @@ func importOrgMembers(ctx context.Context, s *Server, errors *[]*admin_pb.Import
|
||||
}
|
||||
for _, member := range org.GetOrgMembers() {
|
||||
logging.Debugf("import orgmember: %s", member.GetUserId())
|
||||
_, err := s.command.AddOrgMember(ctx, org.GetOrgId(), member.GetUserId(), member.GetRoles()...)
|
||||
_, err := s.command.AddOrgMember(ctx, management.AddOrgMemberRequestToCommand(member, org.GetOrgId()))
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "org_member", Id: org.GetOrgId() + "_" + member.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
@@ -1112,7 +1112,7 @@ func importProjectGrantMembers(ctx context.Context, s *Server, errors *[]*admin_
|
||||
}
|
||||
for _, member := range org.GetProjectGrantMembers() {
|
||||
logging.Debugf("import projectgrantmember: %s", member.GetProjectId()+"_"+member.GetGrantId()+"_"+member.GetUserId())
|
||||
_, err := s.command.AddProjectGrantMember(ctx, management.AddProjectGrantMemberRequestToDomain(member))
|
||||
_, err := s.command.AddProjectGrantMember(ctx, management.AddProjectGrantMemberRequestToCommand(member, org.GetOrgId()))
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "project_grant_member", Id: org.GetOrgId() + "_" + member.GetProjectId() + "_" + member.GetGrantId() + "_" + member.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
@@ -1136,7 +1136,7 @@ func importProjectMembers(ctx context.Context, s *Server, errors *[]*admin_pb.Im
|
||||
}
|
||||
for _, member := range org.GetProjectMembers() {
|
||||
logging.Debugf("import orgmember: %s", member.GetProjectId()+"_"+member.GetUserId())
|
||||
_, err := s.command.AddProjectMember(ctx, management.AddProjectMemberRequestToDomain(member), org.GetOrgId())
|
||||
_, err := s.command.AddProjectMember(ctx, management.AddProjectMemberRequestToCommand(member, org.GetOrgId()))
|
||||
if err != nil {
|
||||
*errors = append(*errors, &admin_pb.ImportDataError{Type: "project_member", Id: org.GetOrgId() + "_" + member.GetProjectId() + "_" + member.GetUserId(), Message: err.Error()})
|
||||
if isCtxTimeout(ctx) {
|
||||
|
||||
@@ -457,7 +457,7 @@ func TestServer_ImportData(t *testing.T) {
|
||||
{
|
||||
Type: "project_grant_member",
|
||||
Id: orgIDs[5] + "_" + projectIDs[4] + "_" + grantIDs[5] + "_" + userIDs[2],
|
||||
Message: "ID=V3-DKcYh Message=Errors.Project.Member.AlreadyExists Parent=(ERROR: duplicate key value violates unique constraint \"unique_constraints_pkey\" (SQLSTATE 23505))",
|
||||
Message: "ID=PROJECT-37fug Message=Errors.AlreadyExists",
|
||||
},
|
||||
},
|
||||
Success: &admin.ImportDataSuccess{
|
||||
|
||||
@@ -32,7 +32,7 @@ func (s *Server) RemoveMyUser(ctx context.Context, _ *auth_pb.RemoveMyUserReques
|
||||
return nil, err
|
||||
}
|
||||
queries := &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantUserID}}
|
||||
grants, err := s.query.UserGrants(ctx, queries, true)
|
||||
grants, err := s.query.UserGrants(ctx, queries, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func (s *Server) ListMyMetadata(ctx context.Context, req *auth_pb.ListMyMetadata
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUserMetadata(ctx, true, authz.GetCtxData(ctx).UserID, queries, false)
|
||||
res, err := s.query.SearchUserMetadata(ctx, true, authz.GetCtxData(ctx).UserID, queries, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -151,7 +151,7 @@ func (s *Server) ListMyUserGrants(ctx context.Context, req *auth_pb.ListMyUserGr
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.UserGrants(ctx, queries, false)
|
||||
res, err := s.query.UserGrants(ctx, queries, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -180,7 +180,7 @@ func (s *Server) ListMyProjectOrgs(ctx context.Context, req *auth_pb.ListMyProje
|
||||
return nil, err
|
||||
}
|
||||
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantProjectID, userGrantUserID}}, false)
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{Queries: []query.SearchQuery{userGrantProjectID, userGrantUserID}}, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package authorization
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
authorization "github.com/zitadel/zitadel/pkg/grpc/authorization/v2beta"
|
||||
)
|
||||
|
||||
func (s *Server) CreateAuthorization(ctx context.Context, req *connect.Request[authorization.CreateAuthorizationRequest]) (*connect.Response[authorization.CreateAuthorizationResponse], error) {
|
||||
grant := &domain.UserGrant{
|
||||
UserID: req.Msg.UserId,
|
||||
ProjectID: req.Msg.ProjectId,
|
||||
RoleKeys: req.Msg.RoleKeys,
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: req.Msg.GetOrganizationId(),
|
||||
},
|
||||
}
|
||||
grant, err := s.command.AddUserGrant(ctx, grant, s.command.NewPermissionCheckUserGrantWrite(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&authorization.CreateAuthorizationResponse{
|
||||
Id: grant.AggregateID,
|
||||
CreationDate: timestamppb.New(grant.ChangeDate),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateAuthorization(ctx context.Context, request *connect.Request[authorization.UpdateAuthorizationRequest]) (*connect.Response[authorization.UpdateAuthorizationResponse], error) {
|
||||
userGrant, err := s.command.ChangeUserGrant(ctx, &domain.UserGrant{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: request.Msg.Id,
|
||||
},
|
||||
RoleKeys: request.Msg.RoleKeys,
|
||||
}, true, true, s.command.NewPermissionCheckUserGrantWrite(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&authorization.UpdateAuthorizationResponse{
|
||||
ChangeDate: timestamppb.New(userGrant.ChangeDate),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeleteAuthorization(ctx context.Context, request *connect.Request[authorization.DeleteAuthorizationRequest]) (*connect.Response[authorization.DeleteAuthorizationResponse], error) {
|
||||
details, err := s.command.RemoveUserGrant(ctx, request.Msg.Id, "", true, s.command.NewPermissionCheckUserGrantDelete(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&authorization.DeleteAuthorizationResponse{
|
||||
DeletionDate: timestamppb.New(details.EventDate),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) ActivateAuthorization(ctx context.Context, request *connect.Request[authorization.ActivateAuthorizationRequest]) (*connect.Response[authorization.ActivateAuthorizationResponse], error) {
|
||||
details, err := s.command.ReactivateUserGrant(ctx, request.Msg.Id, "", s.command.NewPermissionCheckUserGrantWrite(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&authorization.ActivateAuthorizationResponse{
|
||||
ChangeDate: timestamppb.New(details.EventDate),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) DeactivateAuthorization(ctx context.Context, request *connect.Request[authorization.DeactivateAuthorizationRequest]) (*connect.Response[authorization.DeactivateAuthorizationResponse], error) {
|
||||
details, err := s.command.DeactivateUserGrant(ctx, request.Msg.Id, "", s.command.NewPermissionCheckUserGrantWrite(ctx))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&authorization.DeactivateAuthorizationResponse{
|
||||
ChangeDate: timestamppb.New(details.EventDate),
|
||||
}), nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,971 @@
|
||||
//go:build integration
|
||||
|
||||
package authorization_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
authorization "github.com/zitadel/zitadel/pkg/grpc/authorization/v2beta"
|
||||
filter "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
|
||||
project "github.com/zitadel/zitadel/pkg/grpc/project/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func TestServer_ListAuthorizations(t *testing.T) {
|
||||
iamOwnerCtx := Instance.WithAuthorizationToken(EmptyCTX, integration.UserTypeIAMOwner)
|
||||
projectOwnerResp := Instance.CreateMachineUser(iamOwnerCtx)
|
||||
projectOwnerPatResp := Instance.CreatePersonalAccessToken(iamOwnerCtx, projectOwnerResp.GetUserId())
|
||||
projectResp := createProject(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), false, false)
|
||||
Instance.CreateProjectMembership(t, iamOwnerCtx, projectResp.GetId(), projectOwnerResp.GetUserId())
|
||||
projectOwnerCtx := integration.WithAuthorizationToken(EmptyCTX, projectOwnerPatResp.Token)
|
||||
|
||||
projectGrantOwnerResp := Instance.CreateMachineUser(iamOwnerCtx)
|
||||
projectGrantOwnerPatResp := Instance.CreatePersonalAccessToken(iamOwnerCtx, projectGrantOwnerResp.GetUserId())
|
||||
grantedProjectResp := createGrantedProject(iamOwnerCtx, Instance, t, projectResp)
|
||||
Instance.CreateProjectGrantMembership(t, iamOwnerCtx, projectResp.GetId(), grantedProjectResp.GetGrantedOrganizationId(), projectGrantOwnerResp.GetUserId())
|
||||
projectGrantOwnerCtx := integration.WithAuthorizationToken(EmptyCTX, projectGrantOwnerPatResp.Token)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
dep func(*authorization.ListAuthorizationsRequest, *authorization.ListAuthorizationsResponse)
|
||||
req *authorization.ListAuthorizationsRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *authorization.ListAuthorizationsResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list by user id, unauthenticated",
|
||||
args: args{
|
||||
ctx: EmptyCTX,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list by id, no permission",
|
||||
args: args{
|
||||
ctx: Instance.WithAuthorizationToken(EmptyCTX, integration.UserTypeNoPermission),
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list, not found",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{
|
||||
{Filter: &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: "notexisting",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 0,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_AuthorizationIds{
|
||||
AuthorizationIds: &filter.InIDsFilter{
|
||||
Ids: []string{resp.GetId()},
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single project id",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectId{
|
||||
ProjectId: &filter.IDFilter{
|
||||
Id: resp.GetProjectId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single project name",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectName{
|
||||
ProjectName: &authorization.ProjectNameQuery{
|
||||
Name: resp.GetProjectName(),
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project grant",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single grant id, project grant",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectGrantId{
|
||||
ProjectGrantId: &filter.IDFilter{
|
||||
Id: resp.GetProjectGrantId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, multiple",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[5] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
response.Authorizations[4] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
response.Authorizations[3] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
response.Authorizations[2] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
response.Authorizations[1] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
response.Authorizations[0] = createAuthorization(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 6,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{}, {}, {}, {}, {}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, org owner",
|
||||
args: args{
|
||||
ctx: Instance.WithAuthorizationToken(EmptyCTX, integration.UserTypeOrgOwner),
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
response.Authorizations[1] = createAuthorizationForProject(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
|
||||
response.Authorizations[0] = createAuthorizationWithProjectGrant(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, project owner",
|
||||
args: args{
|
||||
ctx: projectOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
response.Authorizations[0] = createAuthorizationForProject(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
|
||||
createAuthorizationWithProjectGrant(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, project grant owner",
|
||||
args: args{
|
||||
ctx: projectGrantOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := Instance.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
createAuthorizationForProject(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
|
||||
response.Authorizations[0] = createAuthorizationForProjectGrant(iamOwnerCtx, Instance, t, Instance.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId(), grantedProjectResp.GetGrantedOrganizationId())
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.dep != nil {
|
||||
tt.args.dep(tt.args.req, tt.want)
|
||||
}
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCtx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, listErr := Instance.Client.AuthorizationV2Beta.ListAuthorizations(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, listErr)
|
||||
return
|
||||
}
|
||||
require.NoError(ttt, listErr)
|
||||
|
||||
// always first check length, otherwise its failed anyway
|
||||
if assert.Len(ttt, got.Authorizations, len(tt.want.Authorizations)) {
|
||||
for i := range tt.want.Authorizations {
|
||||
assert.EqualExportedValues(ttt, tt.want.Authorizations[i], got.Authorizations[i])
|
||||
}
|
||||
}
|
||||
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
||||
}, retryDuration, tick, "timeout waiting for expected execution result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationResponse, actual *filter.PaginationResponse) {
|
||||
assert.Equal(t, expected.AppliedLimit, actual.AppliedLimit)
|
||||
assert.Equal(t, expected.TotalResult, actual.TotalResult)
|
||||
}
|
||||
|
||||
func createAuthorization(ctx context.Context, instance *integration.Instance, t *testing.T, orgID, userID string, grant bool) *authorization.Authorization {
|
||||
projectName := gofakeit.AppName()
|
||||
projectResp := instance.CreateProject(ctx, t, orgID, projectName, false, false)
|
||||
|
||||
if grant {
|
||||
return createAuthorizationWithProjectGrant(ctx, instance, t, orgID, userID, projectName, projectResp.GetId())
|
||||
}
|
||||
return createAuthorizationForProject(ctx, instance, t, orgID, userID, projectName, projectResp.GetId())
|
||||
}
|
||||
|
||||
func createAuthorizationForProject(ctx context.Context, instance *integration.Instance, t *testing.T, orgID, userID, projectName, projectID string) *authorization.Authorization {
|
||||
userResp, err := instance.Client.UserV2.GetUserByID(ctx, &user.GetUserByIDRequest{UserId: userID})
|
||||
require.NoError(t, err)
|
||||
|
||||
userGrantResp := instance.CreateProjectUserGrant(t, ctx, projectID, userID)
|
||||
return &authorization.Authorization{
|
||||
Id: userGrantResp.GetUserGrantId(),
|
||||
ProjectId: projectID,
|
||||
ProjectName: projectName,
|
||||
ProjectOrganizationId: orgID,
|
||||
OrganizationId: orgID,
|
||||
CreationDate: userGrantResp.Details.GetCreationDate(),
|
||||
ChangeDate: userGrantResp.Details.GetCreationDate(),
|
||||
State: 1,
|
||||
User: &authorization.User{
|
||||
Id: userID,
|
||||
PreferredLoginName: userResp.User.GetPreferredLoginName(),
|
||||
DisplayName: userResp.User.GetHuman().GetProfile().GetDisplayName(),
|
||||
AvatarUrl: userResp.User.GetHuman().GetProfile().GetAvatarUrl(),
|
||||
OrganizationId: userResp.GetUser().GetDetails().GetResourceOwner(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createAuthorizationWithProjectGrant(ctx context.Context, instance *integration.Instance, t *testing.T, orgID, userID, projectName, projectID string) *authorization.Authorization {
|
||||
grantedOrgName := gofakeit.Company() + integration.RandString(10)
|
||||
grantedOrg := instance.CreateOrganization(ctx, grantedOrgName, gofakeit.Email())
|
||||
instance.CreateProjectGrant(ctx, t, projectID, grantedOrg.GetOrganizationId())
|
||||
|
||||
return createAuthorizationForProjectGrant(ctx, instance, t, orgID, userID, projectName, projectID, grantedOrg.GetOrganizationId())
|
||||
}
|
||||
|
||||
func createAuthorizationForProjectGrant(ctx context.Context, instance *integration.Instance, t *testing.T, orgID, userID, projectName, projectID, grantedOrgID string) *authorization.Authorization {
|
||||
userResp, err := instance.Client.UserV2.GetUserByID(ctx, &user.GetUserByIDRequest{UserId: userID})
|
||||
require.NoError(t, err)
|
||||
|
||||
userGrantResp := instance.CreateProjectGrantUserGrant(ctx, orgID, projectID, grantedOrgID, userID)
|
||||
return &authorization.Authorization{
|
||||
Id: userGrantResp.GetUserGrantId(),
|
||||
ProjectId: projectID,
|
||||
ProjectName: projectName,
|
||||
ProjectOrganizationId: orgID,
|
||||
ProjectGrantId: gu.Ptr(grantedOrgID),
|
||||
GrantedOrganizationId: gu.Ptr(grantedOrgID),
|
||||
OrganizationId: orgID,
|
||||
CreationDate: userGrantResp.Details.GetCreationDate(),
|
||||
ChangeDate: userGrantResp.Details.GetCreationDate(),
|
||||
State: 1,
|
||||
User: &authorization.User{
|
||||
Id: userID,
|
||||
PreferredLoginName: userResp.User.GetPreferredLoginName(),
|
||||
DisplayName: userResp.User.GetHuman().GetProfile().GetDisplayName(),
|
||||
AvatarUrl: userResp.User.GetHuman().GetProfile().GetAvatarUrl(),
|
||||
OrganizationId: userResp.GetUser().GetDetails().GetResourceOwner(),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func createProject(ctx context.Context, instance *integration.Instance, t *testing.T, orgID string, projectRoleCheck, hasProjectCheck bool) *project.Project {
|
||||
name := gofakeit.AppName()
|
||||
resp := instance.CreateProject(ctx, t, orgID, name, projectRoleCheck, hasProjectCheck)
|
||||
return &project.Project{
|
||||
Id: resp.GetId(),
|
||||
Name: name,
|
||||
OrganizationId: orgID,
|
||||
CreationDate: resp.GetCreationDate(),
|
||||
ChangeDate: resp.GetCreationDate(),
|
||||
State: 1,
|
||||
ProjectRoleAssertion: false,
|
||||
ProjectAccessRequired: hasProjectCheck,
|
||||
AuthorizationRequired: projectRoleCheck,
|
||||
PrivateLabelingSetting: project.PrivateLabelingSetting_PRIVATE_LABELING_SETTING_UNSPECIFIED,
|
||||
}
|
||||
}
|
||||
|
||||
func createGrantedProject(ctx context.Context, instance *integration.Instance, t *testing.T, projectToGrant *project.Project) *project.Project {
|
||||
grantedOrgName := gofakeit.AppName()
|
||||
grantedOrg := instance.CreateOrganization(ctx, grantedOrgName, gofakeit.Email())
|
||||
projectGrantResp := instance.CreateProjectGrant(ctx, t, projectToGrant.GetId(), grantedOrg.GetOrganizationId())
|
||||
|
||||
return &project.Project{
|
||||
Id: projectToGrant.GetId(),
|
||||
Name: projectToGrant.GetName(),
|
||||
OrganizationId: projectToGrant.GetOrganizationId(),
|
||||
CreationDate: projectGrantResp.GetCreationDate(),
|
||||
ChangeDate: projectGrantResp.GetCreationDate(),
|
||||
State: 1,
|
||||
ProjectRoleAssertion: false,
|
||||
ProjectAccessRequired: projectToGrant.GetProjectAccessRequired(),
|
||||
AuthorizationRequired: projectToGrant.GetAuthorizationRequired(),
|
||||
PrivateLabelingSetting: project.PrivateLabelingSetting_PRIVATE_LABELING_SETTING_UNSPECIFIED,
|
||||
GrantedOrganizationId: gu.Ptr(grantedOrg.GetOrganizationId()),
|
||||
GrantedOrganizationName: gu.Ptr(grantedOrgName),
|
||||
GrantedState: 1,
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ListAuthorizations_PermissionsV2(t *testing.T) {
|
||||
ensureFeaturePermissionV2Enabled(t, InstancePermissionV2)
|
||||
iamOwnerCtx := InstancePermissionV2.WithAuthorizationToken(EmptyCTX, integration.UserTypeIAMOwner)
|
||||
|
||||
projectOwnerResp := InstancePermissionV2.CreateMachineUser(iamOwnerCtx)
|
||||
projectOwnerPatResp := InstancePermissionV2.CreatePersonalAccessToken(iamOwnerCtx, projectOwnerResp.GetUserId())
|
||||
projectResp := createProject(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), false, false)
|
||||
InstancePermissionV2.CreateProjectMembership(t, iamOwnerCtx, projectResp.GetId(), projectOwnerResp.GetUserId())
|
||||
projectOwnerCtx := integration.WithAuthorizationToken(EmptyCTX, projectOwnerPatResp.Token)
|
||||
|
||||
//projectGrantOwnerResp := InstancePermissionV2.CreateMachineUser(iamOwnerCtx)
|
||||
//projectGrantOwnerPatResp := InstancePermissionV2.CreatePersonalAccessToken(iamOwnerCtx, projectGrantOwnerResp.GetUserId())
|
||||
grantedProjectResp := createGrantedProject(iamOwnerCtx, InstancePermissionV2, t, projectResp)
|
||||
//InstancePermissionV2.CreateProjectGrantMembership(t, iamOwnerCtx, projectResp.GetId(), grantedProjectResp.GetGrantedOrganizationId(), projectGrantOwnerResp.GetUserId())
|
||||
//projectGrantOwnerCtx := integration.WithAuthorizationToken(EmptyCTX, projectGrantOwnerPatResp.Token)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
dep func(*authorization.ListAuthorizationsRequest, *authorization.ListAuthorizationsResponse)
|
||||
req *authorization.ListAuthorizationsRequest
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *authorization.ListAuthorizationsResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "list by user id, unauthenticated",
|
||||
args: args{
|
||||
ctx: EmptyCTX,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list by id, no permission",
|
||||
args: args{
|
||||
ctx: InstancePermissionV2.WithAuthorizationToken(EmptyCTX, integration.UserTypeNoPermission),
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 0,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list, not found",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{
|
||||
{Filter: &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: "notexisting",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 0,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_AuthorizationIds{
|
||||
AuthorizationIds: &filter.InIDsFilter{
|
||||
Ids: []string{resp.GetId()},
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single project id",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectId{
|
||||
ProjectId: &filter.IDFilter{
|
||||
Id: resp.GetProjectId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single project name",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectName{
|
||||
ProjectName: &authorization.ProjectNameQuery{
|
||||
Name: resp.GetProjectName(),
|
||||
Method: filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS,
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project grant",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single grant id, project grant",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
resp := createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_ProjectGrantId{
|
||||
ProjectGrantId: &filter.IDFilter{
|
||||
Id: resp.GetProjectGrantId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[0] = resp
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, multiple",
|
||||
args: args{
|
||||
ctx: iamOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
response.Authorizations[5] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
response.Authorizations[4] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
response.Authorizations[3] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), false)
|
||||
response.Authorizations[2] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
response.Authorizations[1] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
response.Authorizations[0] = createAuthorization(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), true)
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 6,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{}, {}, {}, {}, {}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, org owner",
|
||||
args: args{
|
||||
ctx: InstancePermissionV2.WithAuthorizationToken(EmptyCTX, integration.UserTypeOrgOwner),
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
response.Authorizations[1] = createAuthorizationForProject(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
|
||||
response.Authorizations[0] = createAuthorizationWithProjectGrant(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list single id, project and project grant, project owner",
|
||||
args: args{
|
||||
ctx: projectOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
response.Authorizations[1] = createAuthorizationForProject(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
|
||||
response.Authorizations[0] = createAuthorizationWithProjectGrant(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
/*
|
||||
TODO: correct when permission check is added for project grants https://github.com/zitadel/zitadel/issues/9972
|
||||
{
|
||||
name: "list single id, project and project grant, project grant owner",
|
||||
args: args{
|
||||
ctx: projectGrantOwnerCtx,
|
||||
dep: func(request *authorization.ListAuthorizationsRequest, response *authorization.ListAuthorizationsResponse) {
|
||||
userResp := InstancePermissionV2.CreateUserTypeHuman(iamOwnerCtx, gofakeit.Email())
|
||||
|
||||
request.Filters[0].Filter = &authorization.AuthorizationsSearchFilter_UserId{
|
||||
UserId: &filter.IDFilter{
|
||||
Id: userResp.GetId(),
|
||||
},
|
||||
}
|
||||
|
||||
createAuthorizationForProject(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), projectResp.GetName(), projectResp.GetId())
|
||||
response.Authorizations[0] = createAuthorizationForProjectGrant(iamOwnerCtx, InstancePermissionV2, t, InstancePermissionV2.DefaultOrg.GetId(), userResp.GetId(), grantedProjectResp.GetName(), grantedProjectResp.GetId())
|
||||
},
|
||||
req: &authorization.ListAuthorizationsRequest{
|
||||
Filters: []*authorization.AuthorizationsSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &authorization.ListAuthorizationsResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 2,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Authorizations: []*authorization.Authorization{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
*/
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.dep != nil {
|
||||
tt.args.dep(tt.args.req, tt.want)
|
||||
}
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCtx, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, listErr := InstancePermissionV2.Client.AuthorizationV2Beta.ListAuthorizations(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, listErr)
|
||||
return
|
||||
}
|
||||
require.NoError(ttt, listErr)
|
||||
|
||||
// always first check length, otherwise its failed anyway
|
||||
if assert.Len(ttt, got.Authorizations, len(tt.want.Authorizations)) {
|
||||
for i := range tt.want.Authorizations {
|
||||
assert.EqualExportedValues(ttt, tt.want.Authorizations[i], got.Authorizations[i])
|
||||
}
|
||||
}
|
||||
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
||||
}, retryDuration, tick, "timeout waiting for expected execution result")
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
//go:build integration
|
||||
|
||||
package authorization_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
EmptyCTX context.Context
|
||||
IAMCTX context.Context
|
||||
Instance *integration.Instance
|
||||
InstancePermissionV2 *integration.Instance
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
|
||||
defer cancel()
|
||||
EmptyCTX = ctx
|
||||
Instance = integration.NewInstance(ctx)
|
||||
IAMCTX = Instance.WithAuthorizationToken(ctx, integration.UserTypeIAMOwner)
|
||||
InstancePermissionV2 = integration.NewInstance(ctx)
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
func ensureFeaturePermissionV2Enabled(t *testing.T, instance *integration.Instance) {
|
||||
ctx := instance.WithAuthorizationToken(EmptyCTX, integration.UserTypeIAMOwner)
|
||||
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if f.PermissionCheckV2.GetEnabled() {
|
||||
return
|
||||
}
|
||||
_, err = instance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
|
||||
require.EventuallyWithT(t,
|
||||
func(ttt *assert.CollectT) {
|
||||
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
assert.NoError(ttt, err)
|
||||
if f.PermissionCheckV2.GetEnabled() {
|
||||
return
|
||||
}
|
||||
},
|
||||
retryDuration,
|
||||
tick,
|
||||
"timed out waiting for ensuring instance feature")
|
||||
}
|
||||
@@ -0,0 +1,208 @@
|
||||
package authorization
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
filter "github.com/zitadel/zitadel/internal/api/grpc/filter/v2beta"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
authorization "github.com/zitadel/zitadel/pkg/grpc/authorization/v2beta"
|
||||
filter_pb "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
|
||||
)
|
||||
|
||||
func (s *Server) ListAuthorizations(ctx context.Context, req *connect.Request[authorization.ListAuthorizationsRequest]) (*connect.Response[authorization.ListAuthorizationsResponse], error) {
|
||||
queries, err := s.listAuthorizationsRequestToModel(req.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := s.query.UserGrants(ctx, queries, false, s.checkPermission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&authorization.ListAuthorizationsResponse{
|
||||
Authorizations: userGrantsToPb(resp.UserGrants),
|
||||
Pagination: filter.QueryToPaginationPb(queries.SearchRequest, resp.SearchResponse),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) listAuthorizationsRequestToModel(req *authorization.ListAuthorizationsRequest) (*query.UserGrantsQueries, error) {
|
||||
offset, limit, asc, err := filter.PaginationPbToQuery(s.systemDefaults, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := AuthorizationQueriesToQuery(req.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.UserGrantsQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: authorizationFieldNameToSortingColumn(req.GetSortingColumn()),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func authorizationFieldNameToSortingColumn(field authorization.AuthorizationFieldName) query.Column {
|
||||
switch field {
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_UNSPECIFIED:
|
||||
return query.UserGrantCreationDate
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_CREATED_DATE:
|
||||
return query.UserGrantCreationDate
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_CHANGED_DATE:
|
||||
return query.UserGrantChangeDate
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_ID:
|
||||
return query.UserGrantID
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_USER_ID:
|
||||
return query.UserGrantUserID
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_PROJECT_ID:
|
||||
return query.UserGrantProjectID
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_ORGANIZATION_ID:
|
||||
return query.UserGrantResourceOwner
|
||||
case authorization.AuthorizationFieldName_AUTHORIZATION_FIELD_NAME_USER_ORGANIZATION_ID:
|
||||
return query.UserResourceOwnerCol
|
||||
default:
|
||||
return query.UserGrantCreationDate
|
||||
}
|
||||
}
|
||||
|
||||
func AuthorizationQueriesToQuery(queries []*authorization.AuthorizationsSearchFilter) (q []query.SearchQuery, err error) {
|
||||
q = make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = AuthorizationSearchFilterToQuery(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func AuthorizationSearchFilterToQuery(query *authorization.AuthorizationsSearchFilter) (query.SearchQuery, error) {
|
||||
switch q := query.Filter.(type) {
|
||||
case *authorization.AuthorizationsSearchFilter_AuthorizationIds:
|
||||
return AuthorizationIDQueryToModel(q.AuthorizationIds)
|
||||
case *authorization.AuthorizationsSearchFilter_OrganizationId:
|
||||
return AuthorizationOrganizationIDQueryToModel(q.OrganizationId)
|
||||
case *authorization.AuthorizationsSearchFilter_State:
|
||||
return AuthorizationStateQueryToModel(q.State)
|
||||
case *authorization.AuthorizationsSearchFilter_UserId:
|
||||
return AuthorizationUserUserIDQueryToModel(q.UserId)
|
||||
case *authorization.AuthorizationsSearchFilter_UserOrganizationId:
|
||||
return AuthorizationUserOrganizationIDQueryToModel(q.UserOrganizationId)
|
||||
case *authorization.AuthorizationsSearchFilter_UserPreferredLoginName:
|
||||
return AuthorizationUserNameQueryToModel(q.UserPreferredLoginName)
|
||||
case *authorization.AuthorizationsSearchFilter_UserDisplayName:
|
||||
return AuthorizationDisplayNameQueryToModel(q.UserDisplayName)
|
||||
case *authorization.AuthorizationsSearchFilter_ProjectId:
|
||||
return AuthorizationProjectIDQueryToModel(q.ProjectId)
|
||||
case *authorization.AuthorizationsSearchFilter_ProjectName:
|
||||
return AuthorizationProjectNameQueryToModel(q.ProjectName)
|
||||
case *authorization.AuthorizationsSearchFilter_RoleKey:
|
||||
return AuthorizationRoleKeyQueryToModel(q.RoleKey)
|
||||
case *authorization.AuthorizationsSearchFilter_ProjectGrantId:
|
||||
return AuthorizationProjectGrantIDQueryToModel(q.ProjectGrantId)
|
||||
default:
|
||||
return nil, errors.New("invalid query")
|
||||
}
|
||||
}
|
||||
|
||||
func AuthorizationIDQueryToModel(q *filter_pb.InIDsFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantInIDsSearchQuery(q.Ids)
|
||||
}
|
||||
|
||||
func AuthorizationDisplayNameQueryToModel(q *authorization.UserDisplayNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantDisplayNameQuery(q.DisplayName, filter.TextMethodPbToQuery(q.Method))
|
||||
}
|
||||
|
||||
func AuthorizationOrganizationIDQueryToModel(q *filter_pb.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantResourceOwnerSearchQuery(q.Id)
|
||||
}
|
||||
|
||||
func AuthorizationProjectIDQueryToModel(q *filter_pb.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantProjectIDSearchQuery(q.Id)
|
||||
}
|
||||
|
||||
func AuthorizationProjectNameQueryToModel(q *authorization.ProjectNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantProjectNameQuery(q.Name, filter.TextMethodPbToQuery(q.Method))
|
||||
}
|
||||
|
||||
func AuthorizationProjectGrantIDQueryToModel(q *filter_pb.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantGrantIDSearchQuery(q.Id)
|
||||
}
|
||||
|
||||
func AuthorizationRoleKeyQueryToModel(q *authorization.RoleKeyQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantRoleQuery(q.Key)
|
||||
}
|
||||
|
||||
func AuthorizationUserNameQueryToModel(q *authorization.UserPreferredLoginNameQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantUsernameQuery(q.LoginName, filter.TextMethodPbToQuery(q.Method))
|
||||
}
|
||||
|
||||
func AuthorizationUserUserIDQueryToModel(q *filter_pb.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantUserIDSearchQuery(q.Id)
|
||||
}
|
||||
|
||||
func AuthorizationUserOrganizationIDQueryToModel(q *filter_pb.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantUserResourceOwnerSearchQuery(q.Id)
|
||||
}
|
||||
|
||||
func AuthorizationStateQueryToModel(q *authorization.StateQuery) (query.SearchQuery, error) {
|
||||
return query.NewUserGrantStateQuery(domain.UserGrantState(q.State))
|
||||
}
|
||||
|
||||
func userGrantsToPb(userGrants []*query.UserGrant) []*authorization.Authorization {
|
||||
o := make([]*authorization.Authorization, len(userGrants))
|
||||
for i, grant := range userGrants {
|
||||
o[i] = userGrantToPb(grant)
|
||||
}
|
||||
return o
|
||||
}
|
||||
|
||||
func userGrantToPb(userGrant *query.UserGrant) *authorization.Authorization {
|
||||
var grantID, grantedOrgID *string
|
||||
if userGrant.GrantID != "" {
|
||||
grantID = &userGrant.GrantID
|
||||
}
|
||||
if userGrant.GrantedOrgID != "" {
|
||||
grantedOrgID = &userGrant.GrantedOrgID
|
||||
}
|
||||
return &authorization.Authorization{
|
||||
Id: userGrant.ID,
|
||||
ProjectId: userGrant.ProjectID,
|
||||
ProjectName: userGrant.ProjectName,
|
||||
ProjectOrganizationId: userGrant.ProjectResourceOwner,
|
||||
ProjectGrantId: grantID,
|
||||
GrantedOrganizationId: grantedOrgID,
|
||||
OrganizationId: userGrant.ResourceOwner,
|
||||
CreationDate: timestamppb.New(userGrant.CreationDate),
|
||||
ChangeDate: timestamppb.New(userGrant.ChangeDate),
|
||||
State: userGrantStateToPb(userGrant.State),
|
||||
User: &authorization.User{
|
||||
Id: userGrant.UserID,
|
||||
PreferredLoginName: userGrant.PreferredLoginName,
|
||||
DisplayName: userGrant.DisplayName,
|
||||
AvatarUrl: userGrant.AvatarURL,
|
||||
OrganizationId: userGrant.UserResourceOwner,
|
||||
},
|
||||
Roles: userGrant.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func userGrantStateToPb(state domain.UserGrantState) authorization.State {
|
||||
switch state {
|
||||
case domain.UserGrantStateActive:
|
||||
return authorization.State_STATE_ACTIVE
|
||||
case domain.UserGrantStateInactive:
|
||||
return authorization.State_STATE_INACTIVE
|
||||
case domain.UserGrantStateUnspecified, domain.UserGrantStateRemoved:
|
||||
return authorization.State_STATE_UNSPECIFIED
|
||||
default:
|
||||
return authorization.State_STATE_UNSPECIFIED
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package authorization
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
authorization "github.com/zitadel/zitadel/pkg/grpc/authorization/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/authorization/v2beta/authorizationconnect"
|
||||
)
|
||||
|
||||
var _ authorizationconnect.AuthorizationServiceHandler = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
systemDefaults: systemDefaults,
|
||||
command: command,
|
||||
query: query,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterConnectServer(interceptors ...connect.Interceptor) (string, http.Handler) {
|
||||
return authorizationconnect.NewAuthorizationServiceHandler(s, connect.WithInterceptors(interceptors...))
|
||||
}
|
||||
|
||||
func (s *Server) FileDescriptor() protoreflect.FileDescriptor {
|
||||
return authorization.File_zitadel_authorization_v2beta_authorization_service_proto
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return authorization.AuthorizationService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return authorization.AuthorizationService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return authorization.AuthorizationService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return authorization.RegisterAuthorizationServiceHandler
|
||||
}
|
||||
@@ -9,6 +9,29 @@ import (
|
||||
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
||||
)
|
||||
|
||||
func TextMethodPbToQuery(method filter.TextFilterMethod) query.TextComparison {
|
||||
switch method {
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS:
|
||||
return query.TextEquals
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS_IGNORE_CASE:
|
||||
return query.TextEqualsIgnoreCase
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH:
|
||||
return query.TextStartsWith
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH_IGNORE_CASE:
|
||||
return query.TextStartsWithIgnoreCase
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_CONTAINS:
|
||||
return query.TextContains
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_CONTAINS_IGNORE_CASE:
|
||||
return query.TextContainsIgnoreCase
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_ENDS_WITH:
|
||||
return query.TextEndsWith
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_ENDS_WITH_IGNORE_CASE:
|
||||
return query.TextEndsWithIgnoreCase
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
func TimestampMethodPbToQuery(method filter.TimestampFilterMethod) query.TimestampComparison {
|
||||
switch method {
|
||||
case filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_EQUALS:
|
||||
@@ -48,26 +71,3 @@ func QueryToPaginationPb(request query.SearchRequest, response query.SearchRespo
|
||||
TotalResult: response.Count,
|
||||
}
|
||||
}
|
||||
|
||||
func TextMethodPbToQuery(method filter.TextFilterMethod) query.TextComparison {
|
||||
switch method {
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS:
|
||||
return query.TextEquals
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_EQUALS_IGNORE_CASE:
|
||||
return query.TextEqualsIgnoreCase
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH:
|
||||
return query.TextStartsWith
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_STARTS_WITH_IGNORE_CASE:
|
||||
return query.TextStartsWithIgnoreCase
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_CONTAINS:
|
||||
return query.TextContains
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_CONTAINS_IGNORE_CASE:
|
||||
return query.TextContainsIgnoreCase
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_ENDS_WITH:
|
||||
return query.TextEndsWith
|
||||
case filter.TextFilterMethod_TEXT_FILTER_METHOD_ENDS_WITH_IGNORE_CASE:
|
||||
return query.TextEndsWithIgnoreCase
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,6 +32,23 @@ func TextMethodPbToQuery(method filter.TextFilterMethod) query.TextComparison {
|
||||
}
|
||||
}
|
||||
|
||||
func TimestampMethodPbToQuery(method filter.TimestampFilterMethod) query.TimestampComparison {
|
||||
switch method {
|
||||
case filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_EQUALS:
|
||||
return query.TimestampEquals
|
||||
case filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_LESS:
|
||||
return query.TimestampLess
|
||||
case filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_GREATER:
|
||||
return query.TimestampGreater
|
||||
case filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_LESS_OR_EQUALS:
|
||||
return query.TimestampLessOrEquals
|
||||
case filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_GREATER_OR_EQUALS:
|
||||
return query.TimestampGreaterOrEquals
|
||||
default:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
func PaginationPbToQuery(defaults systemdefaults.SystemDefaults, query *filter.PaginationRequest) (offset, limit uint64, asc bool, err error) {
|
||||
limit = defaults.DefaultQueryLimit
|
||||
if query == nil {
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
package internal_permission
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
internal_permission "github.com/zitadel/zitadel/pkg/grpc/internal_permission/v2beta"
|
||||
)
|
||||
|
||||
func (s *Server) CreateAdministrator(ctx context.Context, req *connect.Request[internal_permission.CreateAdministratorRequest]) (*connect.Response[internal_permission.CreateAdministratorResponse], error) {
|
||||
var creationDate *timestamppb.Timestamp
|
||||
|
||||
switch resource := req.Msg.GetResource().GetResource().(type) {
|
||||
case *internal_permission.ResourceType_Instance:
|
||||
if resource.Instance {
|
||||
member, err := s.command.AddInstanceMember(ctx, createAdministratorInstanceToCommand(authz.GetInstance(ctx).InstanceID(), req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
creationDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
}
|
||||
case *internal_permission.ResourceType_OrganizationId:
|
||||
member, err := s.command.AddOrgMember(ctx, createAdministratorOrganizationToCommand(resource, req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
creationDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
case *internal_permission.ResourceType_ProjectId:
|
||||
member, err := s.command.AddProjectMember(ctx, createAdministratorProjectToCommand(resource, req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
creationDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
case *internal_permission.ResourceType_ProjectGrant_:
|
||||
member, err := s.command.AddProjectGrantMember(ctx, createAdministratorProjectGrantToCommand(resource, req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
creationDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "ADMIN-IbPp47HDP5", "Errors.Invalid.Argument")
|
||||
}
|
||||
|
||||
return connect.NewResponse(&internal_permission.CreateAdministratorResponse{
|
||||
CreationDate: creationDate,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func createAdministratorInstanceToCommand(instanceID, userID string, roles []string) *command.AddInstanceMember {
|
||||
return &command.AddInstanceMember{
|
||||
InstanceID: instanceID,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func createAdministratorOrganizationToCommand(req *internal_permission.ResourceType_OrganizationId, userID string, roles []string) *command.AddOrgMember {
|
||||
return &command.AddOrgMember{
|
||||
OrgID: req.OrganizationId,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func createAdministratorProjectToCommand(req *internal_permission.ResourceType_ProjectId, userID string, roles []string) *command.AddProjectMember {
|
||||
return &command.AddProjectMember{
|
||||
ProjectID: req.ProjectId,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func createAdministratorProjectGrantToCommand(req *internal_permission.ResourceType_ProjectGrant_, userID string, roles []string) *command.AddProjectGrantMember {
|
||||
return &command.AddProjectGrantMember{
|
||||
GrantID: req.ProjectGrant.ProjectGrantId,
|
||||
ProjectID: req.ProjectGrant.ProjectId,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) UpdateAdministrator(ctx context.Context, req *connect.Request[internal_permission.UpdateAdministratorRequest]) (*connect.Response[internal_permission.UpdateAdministratorResponse], error) {
|
||||
var changeDate *timestamppb.Timestamp
|
||||
|
||||
switch resource := req.Msg.GetResource().GetResource().(type) {
|
||||
case *internal_permission.ResourceType_Instance:
|
||||
if resource.Instance {
|
||||
member, err := s.command.ChangeInstanceMember(ctx, updateAdministratorInstanceToCommand(authz.GetInstance(ctx).InstanceID(), req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
changeDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
}
|
||||
case *internal_permission.ResourceType_OrganizationId:
|
||||
member, err := s.command.ChangeOrgMember(ctx, updateAdministratorOrganizationToCommand(resource, req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
changeDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
case *internal_permission.ResourceType_ProjectId:
|
||||
member, err := s.command.ChangeProjectMember(ctx, updateAdministratorProjectToCommand(resource, req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
changeDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
case *internal_permission.ResourceType_ProjectGrant_:
|
||||
member, err := s.command.ChangeProjectGrantMember(ctx, updateAdministratorProjectGrantToCommand(resource, req.Msg.UserId, req.Msg.Roles))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
changeDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "ADMIN-i0V2IbdloZ", "Errors.Invalid.Argument")
|
||||
}
|
||||
|
||||
return connect.NewResponse(&internal_permission.UpdateAdministratorResponse{
|
||||
ChangeDate: changeDate,
|
||||
}), nil
|
||||
}
|
||||
|
||||
func updateAdministratorInstanceToCommand(instanceID, userID string, roles []string) *command.ChangeInstanceMember {
|
||||
return &command.ChangeInstanceMember{
|
||||
InstanceID: instanceID,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func updateAdministratorOrganizationToCommand(req *internal_permission.ResourceType_OrganizationId, userID string, roles []string) *command.ChangeOrgMember {
|
||||
return &command.ChangeOrgMember{
|
||||
OrgID: req.OrganizationId,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func updateAdministratorProjectToCommand(req *internal_permission.ResourceType_ProjectId, userID string, roles []string) *command.ChangeProjectMember {
|
||||
return &command.ChangeProjectMember{
|
||||
ProjectID: req.ProjectId,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func updateAdministratorProjectGrantToCommand(req *internal_permission.ResourceType_ProjectGrant_, userID string, roles []string) *command.ChangeProjectGrantMember {
|
||||
return &command.ChangeProjectGrantMember{
|
||||
GrantID: req.ProjectGrant.ProjectGrantId,
|
||||
ProjectID: req.ProjectGrant.ProjectId,
|
||||
UserID: userID,
|
||||
Roles: roles,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) DeleteAdministrator(ctx context.Context, req *connect.Request[internal_permission.DeleteAdministratorRequest]) (*connect.Response[internal_permission.DeleteAdministratorResponse], error) {
|
||||
var deletionDate *timestamppb.Timestamp
|
||||
|
||||
switch resource := req.Msg.GetResource().GetResource().(type) {
|
||||
case *internal_permission.ResourceType_Instance:
|
||||
if resource.Instance {
|
||||
member, err := s.command.RemoveInstanceMember(ctx, authz.GetInstance(ctx).InstanceID(), req.Msg.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
deletionDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
}
|
||||
case *internal_permission.ResourceType_OrganizationId:
|
||||
member, err := s.command.RemoveOrgMember(ctx, resource.OrganizationId, req.Msg.UserId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
deletionDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
case *internal_permission.ResourceType_ProjectId:
|
||||
member, err := s.command.RemoveProjectMember(ctx, resource.ProjectId, req.Msg.UserId, "")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
deletionDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
case *internal_permission.ResourceType_ProjectGrant_:
|
||||
member, err := s.command.RemoveProjectGrantMember(ctx, resource.ProjectGrant.ProjectId, req.Msg.UserId, resource.ProjectGrant.ProjectGrantId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !member.EventDate.IsZero() {
|
||||
deletionDate = timestamppb.New(member.EventDate)
|
||||
}
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "ADMIN-3UOjLtuohh", "Errors.Invalid.Argument")
|
||||
}
|
||||
|
||||
return connect.NewResponse(&internal_permission.DeleteAdministratorResponse{
|
||||
DeletionDate: deletionDate,
|
||||
}), nil
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,63 @@
|
||||
//go:build integration
|
||||
|
||||
package project_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/muhlemmer/gu"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/feature/v2"
|
||||
)
|
||||
|
||||
var (
|
||||
CTX context.Context
|
||||
instance *integration.Instance
|
||||
instancePermissionV2 *integration.Instance
|
||||
)
|
||||
|
||||
func TestMain(m *testing.M) {
|
||||
os.Exit(func() int {
|
||||
ctx, cancel := context.WithTimeout(context.Background(), 15*time.Minute)
|
||||
defer cancel()
|
||||
CTX = ctx
|
||||
instance = integration.NewInstance(ctx)
|
||||
instancePermissionV2 = integration.NewInstance(CTX)
|
||||
return m.Run()
|
||||
}())
|
||||
}
|
||||
|
||||
func ensureFeaturePermissionV2Enabled(t *testing.T, instance *integration.Instance) {
|
||||
ctx := instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
require.NoError(t, err)
|
||||
if f.PermissionCheckV2.GetEnabled() {
|
||||
return
|
||||
}
|
||||
_, err = instance.Client.FeatureV2.SetInstanceFeatures(ctx, &feature.SetInstanceFeaturesRequest{
|
||||
PermissionCheckV2: gu.Ptr(true),
|
||||
})
|
||||
require.NoError(t, err)
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(ctx, 5*time.Minute)
|
||||
require.EventuallyWithT(t,
|
||||
func(ttt *assert.CollectT) {
|
||||
f, err := instance.Client.FeatureV2.GetInstanceFeatures(ctx, &feature.GetInstanceFeaturesRequest{
|
||||
Inheritance: true,
|
||||
})
|
||||
assert.NoError(ttt, err)
|
||||
if f.PermissionCheckV2.GetEnabled() {
|
||||
return
|
||||
}
|
||||
},
|
||||
retryDuration,
|
||||
tick,
|
||||
"timed out waiting for ensuring instance feature")
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package internal_permission
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
filter "github.com/zitadel/zitadel/internal/api/grpc/filter/v2beta"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
filter_pb "github.com/zitadel/zitadel/pkg/grpc/filter/v2beta"
|
||||
internal_permission "github.com/zitadel/zitadel/pkg/grpc/internal_permission/v2beta"
|
||||
)
|
||||
|
||||
func (s *Server) ListAdministrators(ctx context.Context, req *connect.Request[internal_permission.ListAdministratorsRequest]) (*connect.Response[internal_permission.ListAdministratorsResponse], error) {
|
||||
queries, err := s.listAdministratorsRequestToModel(req.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
resp, err := s.query.SearchAdministrators(ctx, queries, s.checkPermission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&internal_permission.ListAdministratorsResponse{
|
||||
Administrators: administratorsToPb(resp.Administrators),
|
||||
Pagination: filter.QueryToPaginationPb(queries.SearchRequest, resp.SearchResponse),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) listAdministratorsRequestToModel(req *internal_permission.ListAdministratorsRequest) (*query.MembershipSearchQuery, error) {
|
||||
offset, limit, asc, err := filter.PaginationPbToQuery(s.systemDefaults, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := administratorSearchFiltersToQuery(req.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.MembershipSearchQuery{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: administratorFieldNameToSortingColumn(req.GetSortingColumn()),
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func administratorFieldNameToSortingColumn(field internal_permission.AdministratorFieldName) query.Column {
|
||||
switch field {
|
||||
case internal_permission.AdministratorFieldName_ADMINISTRATOR_FIELD_NAME_CREATION_DATE:
|
||||
return query.MembershipCreationDate
|
||||
case internal_permission.AdministratorFieldName_ADMINISTRATOR_FIELD_NAME_USER_ID:
|
||||
return query.MembershipUserID
|
||||
case internal_permission.AdministratorFieldName_ADMINISTRATOR_FIELD_NAME_CHANGE_DATE:
|
||||
return query.MembershipChangeDate
|
||||
case internal_permission.AdministratorFieldName_ADMINISTRATOR_FIELD_NAME_UNSPECIFIED:
|
||||
return query.MembershipCreationDate
|
||||
default:
|
||||
return query.MembershipCreationDate
|
||||
}
|
||||
}
|
||||
|
||||
func administratorSearchFiltersToQuery(queries []*internal_permission.AdministratorSearchFilter) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, qry := range queries {
|
||||
q[i], err = administratorFilterToModel(qry)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func administratorFilterToModel(filter *internal_permission.AdministratorSearchFilter) (query.SearchQuery, error) {
|
||||
switch q := filter.Filter.(type) {
|
||||
case *internal_permission.AdministratorSearchFilter_InUserIdsFilter:
|
||||
return inUserIDsFilterToQuery(q.InUserIdsFilter)
|
||||
case *internal_permission.AdministratorSearchFilter_CreationDate:
|
||||
return creationDateFilterToQuery(q.CreationDate)
|
||||
case *internal_permission.AdministratorSearchFilter_ChangeDate:
|
||||
return changeDateFilterToQuery(q.ChangeDate)
|
||||
case *internal_permission.AdministratorSearchFilter_UserOrganizationId:
|
||||
return userResourceOwnerFilterToQuery(q.UserOrganizationId)
|
||||
case *internal_permission.AdministratorSearchFilter_UserPreferredLoginName:
|
||||
return userLoginNameFilterToQuery(q.UserPreferredLoginName)
|
||||
case *internal_permission.AdministratorSearchFilter_UserDisplayName:
|
||||
return userDisplayNameFilterToQuery(q.UserDisplayName)
|
||||
case *internal_permission.AdministratorSearchFilter_Resource:
|
||||
return resourceFilterToQuery(q.Resource)
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "ORG-vR9nC", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
|
||||
func inUserIDsFilterToQuery(q *filter_pb.InIDsFilter) (query.SearchQuery, error) {
|
||||
return query.NewMemberInUserIDsSearchQuery(q.GetIds())
|
||||
}
|
||||
|
||||
func userResourceOwnerFilterToQuery(q *filter_pb.IDFilter) (query.SearchQuery, error) {
|
||||
return query.NewAdministratorUserResourceOwnerSearchQuery(q.GetId())
|
||||
}
|
||||
|
||||
func userLoginNameFilterToQuery(q *internal_permission.UserPreferredLoginNameFilter) (query.SearchQuery, error) {
|
||||
return query.NewAdministratorUserLoginNameSearchQuery(q.GetPreferredLoginName())
|
||||
}
|
||||
|
||||
func userDisplayNameFilterToQuery(q *internal_permission.UserDisplayNameFilter) (query.SearchQuery, error) {
|
||||
return query.NewAdministratorUserDisplayNameSearchQuery(q.GetDisplayName())
|
||||
}
|
||||
|
||||
func creationDateFilterToQuery(q *filter_pb.TimestampFilter) (query.SearchQuery, error) {
|
||||
return query.NewMembershipCreationDateQuery(q.GetTimestamp().AsTime(), filter.TimestampMethodPbToQuery(q.Method))
|
||||
}
|
||||
|
||||
func changeDateFilterToQuery(q *filter_pb.TimestampFilter) (query.SearchQuery, error) {
|
||||
return query.NewMembershipChangeDateQuery(q.GetTimestamp().AsTime(), filter.TimestampMethodPbToQuery(q.Method))
|
||||
}
|
||||
|
||||
func resourceFilterToQuery(q *internal_permission.ResourceFilter) (query.SearchQuery, error) {
|
||||
switch q.GetResource().(type) {
|
||||
case *internal_permission.ResourceFilter_Instance:
|
||||
if q.GetInstance() {
|
||||
return query.NewMembershipIsIAMQuery()
|
||||
}
|
||||
case *internal_permission.ResourceFilter_OrganizationId:
|
||||
return query.NewMembershipOrgIDQuery(q.GetOrganizationId())
|
||||
case *internal_permission.ResourceFilter_ProjectId:
|
||||
return query.NewMembershipProjectIDQuery(q.GetProjectId())
|
||||
case *internal_permission.ResourceFilter_ProjectGrantId:
|
||||
return query.NewMembershipProjectGrantIDQuery(q.GetProjectGrantId())
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func administratorsToPb(administrators []*query.Administrator) []*internal_permission.Administrator {
|
||||
a := make([]*internal_permission.Administrator, len(administrators))
|
||||
for i, admin := range administrators {
|
||||
a[i] = administratorToPb(admin)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
func administratorToPb(admin *query.Administrator) *internal_permission.Administrator {
|
||||
var resource internal_permission.Resource
|
||||
if admin.Instance != nil {
|
||||
resource = &internal_permission.Administrator_Instance{Instance: true}
|
||||
}
|
||||
if admin.Org != nil {
|
||||
resource = &internal_permission.Administrator_Organization{
|
||||
Organization: &internal_permission.Organization{
|
||||
Id: admin.Org.OrgID,
|
||||
Name: admin.Org.Name,
|
||||
},
|
||||
}
|
||||
}
|
||||
if admin.Project != nil {
|
||||
resource = &internal_permission.Administrator_Project{
|
||||
Project: &internal_permission.Project{
|
||||
Id: admin.Project.ProjectID,
|
||||
Name: admin.Project.Name,
|
||||
OrganizationId: admin.Project.ResourceOwner,
|
||||
},
|
||||
}
|
||||
}
|
||||
if admin.ProjectGrant != nil {
|
||||
resource = &internal_permission.Administrator_ProjectGrant{
|
||||
ProjectGrant: &internal_permission.ProjectGrant{
|
||||
Id: admin.ProjectGrant.GrantID,
|
||||
ProjectId: admin.ProjectGrant.ProjectID,
|
||||
ProjectName: admin.ProjectGrant.ProjectName,
|
||||
OrganizationId: admin.ProjectGrant.ResourceOwner,
|
||||
GrantedOrganizationId: admin.ProjectGrant.GrantedOrgID,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
return &internal_permission.Administrator{
|
||||
CreationDate: timestamppb.New(admin.CreationDate),
|
||||
ChangeDate: timestamppb.New(admin.ChangeDate),
|
||||
User: &internal_permission.User{
|
||||
Id: admin.User.UserID,
|
||||
PreferredLoginName: admin.User.LoginName,
|
||||
DisplayName: admin.User.DisplayName,
|
||||
OrganizationId: admin.User.ResourceOwner,
|
||||
},
|
||||
Resource: resource,
|
||||
Roles: admin.Roles,
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package internal_permission
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/reflect/protoreflect"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/authz"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/server"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/config/systemdefaults"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
internal_permission "github.com/zitadel/zitadel/pkg/grpc/internal_permission/v2beta"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/internal_permission/v2beta/internal_permissionconnect"
|
||||
)
|
||||
|
||||
var _ internal_permissionconnect.InternalPermissionServiceHandler = (*Server)(nil)
|
||||
|
||||
type Server struct {
|
||||
systemDefaults systemdefaults.SystemDefaults
|
||||
command *command.Commands
|
||||
query *query.Queries
|
||||
checkPermission domain.PermissionCheck
|
||||
}
|
||||
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
systemDefaults: systemDefaults,
|
||||
command: command,
|
||||
query: query,
|
||||
checkPermission: checkPermission,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Server) RegisterConnectServer(interceptors ...connect.Interceptor) (string, http.Handler) {
|
||||
return internal_permissionconnect.NewInternalPermissionServiceHandler(s, connect.WithInterceptors(interceptors...))
|
||||
}
|
||||
|
||||
func (s *Server) FileDescriptor() protoreflect.FileDescriptor {
|
||||
return internal_permission.File_zitadel_internal_permission_v2beta_internal_permission_service_proto
|
||||
}
|
||||
|
||||
func (s *Server) AppName() string {
|
||||
return internal_permission.InternalPermissionService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) MethodPrefix() string {
|
||||
return internal_permission.InternalPermissionService_ServiceDesc.ServiceName
|
||||
}
|
||||
|
||||
func (s *Server) AuthMethods() authz.MethodMapping {
|
||||
return internal_permission.InternalPermissionService_AuthMethods
|
||||
}
|
||||
|
||||
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
|
||||
return internal_permission.RegisterInternalPermissionServiceHandler
|
||||
}
|
||||
@@ -278,28 +278,28 @@ func (s *Server) ListOrgMembers(ctx context.Context, req *mgmt_pb.ListOrgMembers
|
||||
}
|
||||
|
||||
func (s *Server) AddOrgMember(ctx context.Context, req *mgmt_pb.AddOrgMemberRequest) (*mgmt_pb.AddOrgMemberResponse, error) {
|
||||
addedMember, err := s.command.AddOrgMember(ctx, authz.GetCtxData(ctx).OrgID, req.UserId, req.Roles...)
|
||||
addedMember, err := s.command.AddOrgMember(ctx, AddOrgMemberRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddOrgMemberResponse{
|
||||
Details: object.AddToDetailsPb(
|
||||
addedMember.Sequence,
|
||||
addedMember.ChangeDate,
|
||||
addedMember.EventDate,
|
||||
addedMember.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateOrgMember(ctx context.Context, req *mgmt_pb.UpdateOrgMemberRequest) (*mgmt_pb.UpdateOrgMemberResponse, error) {
|
||||
changedMember, err := s.command.ChangeOrgMember(ctx, UpdateOrgMemberRequestToDomain(ctx, req))
|
||||
changedMember, err := s.command.ChangeOrgMember(ctx, UpdateOrgMemberRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.UpdateOrgMemberResponse{
|
||||
Details: object.ChangeToDetailsPb(
|
||||
changedMember.Sequence,
|
||||
changedMember.ChangeDate,
|
||||
changedMember.EventDate,
|
||||
changedMember.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/metadata"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
org_grpc "github.com/zitadel/zitadel/internal/api/grpc/org"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
@@ -67,8 +68,20 @@ func SetPrimaryOrgDomainRequestToDomain(ctx context.Context, req *mgmt_pb.SetPri
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateOrgMemberRequestToDomain(ctx context.Context, req *mgmt_pb.UpdateOrgMemberRequest) *domain.Member {
|
||||
return domain.NewMember(authz.GetCtxData(ctx).OrgID, req.UserId, req.Roles...)
|
||||
func AddOrgMemberRequestToCommand(req *mgmt_pb.AddOrgMemberRequest, orgID string) *command.AddOrgMember {
|
||||
return &command.AddOrgMember{
|
||||
OrgID: orgID,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateOrgMemberRequestToCommand(req *mgmt_pb.UpdateOrgMemberRequest, orgID string) *command.ChangeOrgMember {
|
||||
return &command.ChangeOrgMember{
|
||||
OrgID: orgID,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func ListOrgMembersRequestToModel(ctx context.Context, req *mgmt_pb.ListOrgMembersRequest) (*query.OrgMembersQuery, error) {
|
||||
|
||||
@@ -227,7 +227,7 @@ func (s *Server) RemoveProject(ctx context.Context, req *mgmt_pb.RemoveProjectRe
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -312,7 +312,7 @@ func (s *Server) RemoveProjectRole(ctx context.Context, req *mgmt_pb.RemoveProje
|
||||
}
|
||||
userGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, rolesQuery},
|
||||
}, false)
|
||||
}, false, nil)
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -354,24 +354,28 @@ func (s *Server) ListProjectMembers(ctx context.Context, req *mgmt_pb.ListProjec
|
||||
}
|
||||
|
||||
func (s *Server) AddProjectMember(ctx context.Context, req *mgmt_pb.AddProjectMemberRequest) (*mgmt_pb.AddProjectMemberResponse, error) {
|
||||
member, err := s.command.AddProjectMember(ctx, AddProjectMemberRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
member, err := s.command.AddProjectMember(ctx, AddProjectMemberRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddProjectMemberResponse{
|
||||
Details: object_grpc.AddToDetailsPb(member.Sequence, member.ChangeDate, member.ResourceOwner),
|
||||
Details: object_grpc.AddToDetailsPb(
|
||||
member.Sequence,
|
||||
member.EventDate,
|
||||
member.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateProjectMember(ctx context.Context, req *mgmt_pb.UpdateProjectMemberRequest) (*mgmt_pb.UpdateProjectMemberResponse, error) {
|
||||
member, err := s.command.ChangeProjectMember(ctx, UpdateProjectMemberRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
member, err := s.command.ChangeProjectMember(ctx, UpdateProjectMemberRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.UpdateProjectMemberResponse{
|
||||
Details: object_grpc.ChangeToDetailsPb(
|
||||
member.Sequence,
|
||||
member.ChangeDate,
|
||||
member.EventDate,
|
||||
member.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
|
||||
@@ -104,12 +104,22 @@ func ProjectGrantsToIDs(projectGrants *query.ProjectGrants) []string {
|
||||
return converted
|
||||
}
|
||||
|
||||
func AddProjectMemberRequestToDomain(req *mgmt_pb.AddProjectMemberRequest) *domain.Member {
|
||||
return domain.NewMember(req.ProjectId, req.UserId, req.Roles...)
|
||||
func AddProjectMemberRequestToCommand(req *mgmt_pb.AddProjectMemberRequest, orgID string) *command.AddProjectMember {
|
||||
return &command.AddProjectMember{
|
||||
ResourceOwner: orgID,
|
||||
ProjectID: req.ProjectId,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateProjectMemberRequestToDomain(req *mgmt_pb.UpdateProjectMemberRequest) *domain.Member {
|
||||
return domain.NewMember(req.ProjectId, req.UserId, req.Roles...)
|
||||
func UpdateProjectMemberRequestToCommand(req *mgmt_pb.UpdateProjectMemberRequest, orgID string) *command.ChangeProjectMember {
|
||||
return &command.ChangeProjectMember{
|
||||
ResourceOwner: orgID,
|
||||
ProjectID: req.ProjectId,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func listProjectRequestToModel(req *mgmt_pb.ListProjectsRequest) (*query.ProjectSearchQueries, error) {
|
||||
|
||||
@@ -91,7 +91,7 @@ func (s *Server) UpdateProjectGrant(ctx context.Context, req *mgmt_pb.UpdateProj
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, grantQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -139,7 +139,7 @@ func (s *Server) RemoveProjectGrant(ctx context.Context, req *mgmt_pb.RemoveProj
|
||||
}
|
||||
userGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, grantQuery},
|
||||
}, false)
|
||||
}, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -176,28 +176,28 @@ func (s *Server) ListProjectGrantMembers(ctx context.Context, req *mgmt_pb.ListP
|
||||
}
|
||||
|
||||
func (s *Server) AddProjectGrantMember(ctx context.Context, req *mgmt_pb.AddProjectGrantMemberRequest) (*mgmt_pb.AddProjectGrantMemberResponse, error) {
|
||||
member, err := s.command.AddProjectGrantMember(ctx, AddProjectGrantMemberRequestToDomain(req))
|
||||
member, err := s.command.AddProjectGrantMember(ctx, AddProjectGrantMemberRequestToCommand(req, authz.GetCtxData(ctx).OrgID))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.AddProjectGrantMemberResponse{
|
||||
Details: object_grpc.AddToDetailsPb(
|
||||
member.Sequence,
|
||||
member.ChangeDate,
|
||||
member.EventDate,
|
||||
member.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) UpdateProjectGrantMember(ctx context.Context, req *mgmt_pb.UpdateProjectGrantMemberRequest) (*mgmt_pb.UpdateProjectGrantMemberResponse, error) {
|
||||
member, err := s.command.ChangeProjectGrantMember(ctx, UpdateProjectGrantMemberRequestToDomain(req))
|
||||
member, err := s.command.ChangeProjectGrantMember(ctx, UpdateProjectGrantMemberRequestToCommand(req))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &mgmt_pb.UpdateProjectGrantMemberResponse{
|
||||
Details: object_grpc.ChangeToDetailsPb(
|
||||
member.Sequence,
|
||||
member.ChangeDate,
|
||||
member.EventDate,
|
||||
member.ResourceOwner,
|
||||
),
|
||||
}, nil
|
||||
|
||||
@@ -7,7 +7,6 @@ import (
|
||||
member_grpc "github.com/zitadel/zitadel/internal/api/grpc/member"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/object"
|
||||
"github.com/zitadel/zitadel/internal/command"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/eventstore/v1/models"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
@@ -146,24 +145,21 @@ func ListProjectGrantMembersRequestToModel(ctx context.Context, req *mgmt_pb.Lis
|
||||
}, nil
|
||||
}
|
||||
|
||||
func AddProjectGrantMemberRequestToDomain(req *mgmt_pb.AddProjectGrantMemberRequest) *domain.ProjectGrantMember {
|
||||
return &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.ProjectId,
|
||||
},
|
||||
GrantID: req.GrantId,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
func AddProjectGrantMemberRequestToCommand(req *mgmt_pb.AddProjectGrantMemberRequest, orgID string) *command.AddProjectGrantMember {
|
||||
return &command.AddProjectGrantMember{
|
||||
ResourceOwner: orgID,
|
||||
ProjectID: req.ProjectId,
|
||||
GrantID: req.GrantId,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateProjectGrantMemberRequestToDomain(req *mgmt_pb.UpdateProjectGrantMemberRequest) *domain.ProjectGrantMember {
|
||||
return &domain.ProjectGrantMember{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.ProjectId,
|
||||
},
|
||||
GrantID: req.GrantId,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
func UpdateProjectGrantMemberRequestToCommand(req *mgmt_pb.UpdateProjectGrantMemberRequest) *command.ChangeProjectGrantMember {
|
||||
return &command.ChangeProjectGrantMember{
|
||||
ProjectID: req.ProjectId,
|
||||
GrantID: req.GrantId,
|
||||
UserID: req.UserId,
|
||||
Roles: req.Roles,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -142,7 +142,7 @@ func (s *Server) ListUserMetadata(ctx context.Context, req *mgmt_pb.ListUserMeta
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUserMetadata(ctx, true, req.Id, metadataQueries, false)
|
||||
res, err := s.query.SearchUserMetadata(ctx, true, req.Id, metadataQueries, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -369,7 +369,7 @@ func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]*
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ func (s *Server) ListUserGrants(ctx context.Context, req *mgmt_pb.ListUserGrantR
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.UserGrants(ctx, queries, false)
|
||||
res, err := s.query.UserGrants(ctx, queries, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -44,11 +44,11 @@ func (s *Server) ListUserGrants(ctx context.Context, req *mgmt_pb.ListUserGrantR
|
||||
}
|
||||
|
||||
func (s *Server) AddUserGrant(ctx context.Context, req *mgmt_pb.AddUserGrantRequest) (*mgmt_pb.AddUserGrantResponse, error) {
|
||||
grant := AddUserGrantRequestToDomain(req)
|
||||
grant := AddUserGrantRequestToDomain(req, authz.GetCtxData(ctx).OrgID)
|
||||
if err := checkExplicitProjectPermission(ctx, grant.ProjectGrantID, grant.ProjectID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
grant, err := s.command.AddUserGrant(ctx, grant, authz.GetCtxData(ctx).OrgID)
|
||||
grant, err := s.command.AddUserGrant(ctx, grant, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -63,7 +63,7 @@ func (s *Server) AddUserGrant(ctx context.Context, req *mgmt_pb.AddUserGrantRequ
|
||||
}
|
||||
|
||||
func (s *Server) UpdateUserGrant(ctx context.Context, req *mgmt_pb.UpdateUserGrantRequest) (*mgmt_pb.UpdateUserGrantResponse, error) {
|
||||
grant, err := s.command.ChangeUserGrant(ctx, UpdateUserGrantRequestToDomain(req), authz.GetCtxData(ctx).OrgID)
|
||||
grant, err := s.command.ChangeUserGrant(ctx, UpdateUserGrantRequestToDomain(req, authz.GetCtxData(ctx).OrgID), false, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -77,7 +77,7 @@ func (s *Server) UpdateUserGrant(ctx context.Context, req *mgmt_pb.UpdateUserGra
|
||||
}
|
||||
|
||||
func (s *Server) DeactivateUserGrant(ctx context.Context, req *mgmt_pb.DeactivateUserGrantRequest) (*mgmt_pb.DeactivateUserGrantResponse, error) {
|
||||
objectDetails, err := s.command.DeactivateUserGrant(ctx, req.GrantId, authz.GetCtxData(ctx).OrgID)
|
||||
objectDetails, err := s.command.DeactivateUserGrant(ctx, req.GrantId, authz.GetCtxData(ctx).OrgID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -87,7 +87,7 @@ func (s *Server) DeactivateUserGrant(ctx context.Context, req *mgmt_pb.Deactivat
|
||||
}
|
||||
|
||||
func (s *Server) ReactivateUserGrant(ctx context.Context, req *mgmt_pb.ReactivateUserGrantRequest) (*mgmt_pb.ReactivateUserGrantResponse, error) {
|
||||
objectDetails, err := s.command.ReactivateUserGrant(ctx, req.GrantId, authz.GetCtxData(ctx).OrgID)
|
||||
objectDetails, err := s.command.ReactivateUserGrant(ctx, req.GrantId, authz.GetCtxData(ctx).OrgID, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func (s *Server) ReactivateUserGrant(ctx context.Context, req *mgmt_pb.Reactivat
|
||||
}
|
||||
|
||||
func (s *Server) RemoveUserGrant(ctx context.Context, req *mgmt_pb.RemoveUserGrantRequest) (*mgmt_pb.RemoveUserGrantResponse, error) {
|
||||
objectDetails, err := s.command.RemoveUserGrant(ctx, req.GrantId, authz.GetCtxData(ctx).OrgID)
|
||||
objectDetails, err := s.command.RemoveUserGrant(ctx, req.GrantId, authz.GetCtxData(ctx).OrgID, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -49,19 +49,23 @@ func shouldAppendUserGrantOwnerQuery(queries []*user.UserGrantQuery) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func AddUserGrantRequestToDomain(req *mgmt_pb.AddUserGrantRequest) *domain.UserGrant {
|
||||
func AddUserGrantRequestToDomain(req *mgmt_pb.AddUserGrantRequest, resourceowner string) *domain.UserGrant {
|
||||
return &domain.UserGrant{
|
||||
UserID: req.UserId,
|
||||
ProjectID: req.ProjectId,
|
||||
ProjectGrantID: req.ProjectGrantId,
|
||||
RoleKeys: req.RoleKeys,
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
ResourceOwner: resourceowner,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func UpdateUserGrantRequestToDomain(req *mgmt_pb.UpdateUserGrantRequest) *domain.UserGrant {
|
||||
func UpdateUserGrantRequestToDomain(req *mgmt_pb.UpdateUserGrantRequest, resourceowner string) *domain.UserGrant {
|
||||
return &domain.UserGrant{
|
||||
ObjectRoot: models.ObjectRoot{
|
||||
AggregateID: req.GrantId,
|
||||
AggregateID: req.GrantId,
|
||||
ResourceOwner: resourceowner,
|
||||
},
|
||||
UserID: req.UserId,
|
||||
RoleKeys: req.RoleKeys,
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
package metadata
|
||||
|
||||
import (
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
filter_v2 "github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/internal/zerrors"
|
||||
meta_pb "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
|
||||
)
|
||||
|
||||
func UserMetadataListToPb(dataList []*query.UserMetadata) []*meta_pb.Metadata {
|
||||
mds := make([]*meta_pb.Metadata, len(dataList))
|
||||
for i, data := range dataList {
|
||||
mds[i] = UserMetadataToPb(data)
|
||||
}
|
||||
return mds
|
||||
}
|
||||
|
||||
func UserMetadataToPb(data *query.UserMetadata) *meta_pb.Metadata {
|
||||
return &meta_pb.Metadata{
|
||||
Key: data.Key,
|
||||
Value: data.Value,
|
||||
CreationDate: timestamppb.New(data.CreationDate),
|
||||
ChangeDate: timestamppb.New(data.ChangeDate),
|
||||
}
|
||||
}
|
||||
|
||||
func UserMetadataFiltersToQuery(queries []*meta_pb.MetadataSearchFilter) (_ []query.SearchQuery, err error) {
|
||||
q := make([]query.SearchQuery, len(queries))
|
||||
for i, query := range queries {
|
||||
q[i], err = UserMetadataFilterToQuery(query)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return q, nil
|
||||
}
|
||||
|
||||
func UserMetadataFilterToQuery(filter *meta_pb.MetadataSearchFilter) (query.SearchQuery, error) {
|
||||
switch q := filter.Filter.(type) {
|
||||
case *meta_pb.MetadataSearchFilter_KeyFilter:
|
||||
return query.NewUserMetadataKeySearchQuery(q.KeyFilter.Key, filter_v2.TextMethodPbToQuery(q.KeyFilter.Method))
|
||||
default:
|
||||
return nil, zerrors.ThrowInvalidArgument(nil, "METAD-fdg23", "List.Query.Invalid")
|
||||
}
|
||||
}
|
||||
@@ -389,7 +389,8 @@ func TestServer_ListProjects(t *testing.T) {
|
||||
resp2 := createProject(iamOwnerCtx, instance, t, orgID, true, false)
|
||||
resp3 := createProject(iamOwnerCtx, instance, t, orgResp.GetOrganizationId(), false, true)
|
||||
request.Filters[0].Filter = &project.ProjectSearchFilter_InProjectIdsFilter{
|
||||
InProjectIdsFilter: &filter.InIDsFilter{Ids: []string{resp1.GetId(), resp2.GetId(), resp3.GetId(), projectResp.GetId()}},
|
||||
InProjectIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{resp1.GetId(), resp2.GetId(), resp3.GetId(), projectResp.GetId()}},
|
||||
}
|
||||
response.Projects[0] = grantedProjectResp
|
||||
response.Projects[1] = projectResp
|
||||
@@ -1225,7 +1226,8 @@ func TestServer_ListProjectGrants(t *testing.T) {
|
||||
project2Resp := instance.CreateProject(iamOwnerCtx, t, orgResp.GetOrganizationId(), name2, false, false)
|
||||
project3Resp := instance.CreateProject(iamOwnerCtx, t, orgResp.GetOrganizationId(), name3, false, false)
|
||||
request.Filters[0].Filter = &project.ProjectGrantSearchFilter_InProjectIdsFilter{
|
||||
InProjectIdsFilter: &filter.InIDsFilter{Ids: []string{project1Resp.GetId(), project2Resp.GetId(), project3Resp.GetId(), projectResp.GetId()}},
|
||||
InProjectIdsFilter: &filter.InIDsFilter{
|
||||
Ids: []string{project1Resp.GetId(), project2Resp.GetId(), project3Resp.GetId(), projectResp.GetId()}},
|
||||
}
|
||||
|
||||
createProjectGrant(iamOwnerCtx, instance, t, orgID, project1Resp.GetId(), name1)
|
||||
|
||||
@@ -118,7 +118,7 @@ func (s *Server) userGrantsFromProject(ctx context.Context, projectID string) ([
|
||||
}
|
||||
userGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery},
|
||||
}, false)
|
||||
}, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -119,7 +119,7 @@ func (s *Server) userGrantsFromProjectGrant(ctx context.Context, projectID, gran
|
||||
}
|
||||
userGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, grantQuery},
|
||||
}, false)
|
||||
}, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -109,7 +109,7 @@ func (s *Server) userGrantsFromProjectAndRole(ctx context.Context, projectID, ro
|
||||
}
|
||||
userGrants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{projectQuery, rolesQuery},
|
||||
}, false)
|
||||
}, false, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -16,6 +16,13 @@ import (
|
||||
)
|
||||
|
||||
func (s *Server) createUserTypeHuman(ctx context.Context, humanPb *user.CreateUserRequest_Human, orgId string, userName, userId *string) (*connect.Response[user.CreateUserResponse], error) {
|
||||
metadataEntries := make([]*user.SetMetadataEntry, len(humanPb.Metadata))
|
||||
for i, metadataEntry := range humanPb.Metadata {
|
||||
metadataEntries[i] = &user.SetMetadataEntry{
|
||||
Key: metadataEntry.GetKey(),
|
||||
Value: metadataEntry.GetValue(),
|
||||
}
|
||||
}
|
||||
addHumanPb := &user.AddHumanUserRequest{
|
||||
Username: userName,
|
||||
UserId: userId,
|
||||
@@ -27,6 +34,7 @@ func (s *Server) createUserTypeHuman(ctx context.Context, humanPb *user.CreateUs
|
||||
Phone: humanPb.Phone,
|
||||
IdpLinks: humanPb.IdpLinks,
|
||||
TotpSecret: humanPb.TotpSecret,
|
||||
Metadata: metadataEntries,
|
||||
}
|
||||
switch pwType := humanPb.GetPasswordType().(type) {
|
||||
case *user.CreateUserRequest_Human_HashedPassword:
|
||||
|
||||
@@ -10,7 +10,7 @@ import (
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/object/v2"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestServer_AddKey(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -108,7 +108,7 @@ abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789
|
||||
ExpirationDate: expirationDate,
|
||||
},
|
||||
func(request *user.AddKeyRequest) error {
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX)
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX, gofakeit.Email())
|
||||
request.UserId = resp.Id
|
||||
return nil
|
||||
},
|
||||
@@ -220,7 +220,7 @@ func TestServer_AddKey_Permission(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_RemoveKey(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -388,7 +388,7 @@ func TestServer_ListKeys(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
otherOrgUserId := otherOrgUser.GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX).GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX, Instance.DefaultOrg.Id).GetId()
|
||||
onlySinceTestStartFilter := &user.KeysSearchFilter{Filter: &user.KeysSearchFilter_CreatedDateFilter{CreatedDateFilter: &filter.TimestampFilter{
|
||||
Timestamp: timestamppb.Now(),
|
||||
Method: filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_AFTER_OR_EQUALS,
|
||||
|
||||
@@ -0,0 +1,403 @@
|
||||
//go:build integration
|
||||
|
||||
package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/brianvoe/gofakeit/v6"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/integration"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/filter/v2"
|
||||
metadata "github.com/zitadel/zitadel/pkg/grpc/metadata/v2"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func TestServer_SetUserMetadata(t *testing.T) {
|
||||
iamOwnerCTX := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
dep func(request *user.SetUserMetadataRequest)
|
||||
req *user.SetUserMetadataRequest
|
||||
setDate bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeNoPermission),
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "set user metadata",
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
{
|
||||
name: "set user metadata, multiple",
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{
|
||||
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
||||
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
||||
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
|
||||
},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
{
|
||||
name: "set user metadata on non existent user",
|
||||
ctx: iamOwnerCTX,
|
||||
req: &user.SetUserMetadataRequest{
|
||||
UserId: "notexisting",
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "update user metadata",
|
||||
ctx: Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner),
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))}},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
{
|
||||
name: "update user metadata with same value",
|
||||
ctx: Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner),
|
||||
dep: func(req *user.SetUserMetadataRequest) {
|
||||
req.UserId = Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
Instance.SetUserMetadata(iamOwnerCTX, req.UserId, "key1", "value1")
|
||||
},
|
||||
req: &user.SetUserMetadataRequest{
|
||||
Metadata: []*user.Metadata{{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))}},
|
||||
},
|
||||
setDate: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
creationDate := time.Now().UTC()
|
||||
if tt.dep != nil {
|
||||
tt.dep(tt.req)
|
||||
}
|
||||
got, err := Client.SetUserMetadata(tt.ctx, tt.req)
|
||||
changeDate := time.Now().UTC()
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
return
|
||||
}
|
||||
require.NoError(t, err)
|
||||
|
||||
assertSetUserMetadataResponse(t, creationDate, changeDate, tt.setDate, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertSetUserMetadataResponse(t *testing.T, creationDate, changeDate time.Time, expectedSetDat bool, actualResp *user.SetUserMetadataResponse) {
|
||||
if expectedSetDat {
|
||||
if !changeDate.IsZero() {
|
||||
assert.WithinRange(t, actualResp.GetSetDate().AsTime(), creationDate, changeDate)
|
||||
} else {
|
||||
assert.WithinRange(t, actualResp.GetSetDate().AsTime(), creationDate, time.Now().UTC())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, actualResp.SetDate)
|
||||
}
|
||||
}
|
||||
|
||||
func TestServer_ListUserMetadata(t *testing.T) {
|
||||
iamOwnerCTX := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
type args struct {
|
||||
ctx context.Context
|
||||
dep func(context.Context, *user.ListUserMetadataRequest, *user.ListUserMetadataResponse)
|
||||
req *user.ListUserMetadataRequest
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want *user.ListUserMetadataResponse
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "missing permission",
|
||||
args: args{
|
||||
ctx: Instance.WithAuthorization(context.Background(), integration.UserTypeNoPermission),
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "list request",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
metadataResp := Instance.SetUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
||||
|
||||
response.Metadata[0] = &metadata.Metadata{
|
||||
CreationDate: metadataResp.GetSetDate(),
|
||||
ChangeDate: metadataResp.GetSetDate(),
|
||||
Key: "key1",
|
||||
Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1"))),
|
||||
}
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{},
|
||||
},
|
||||
want: &user.ListUserMetadataResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Metadata: []*metadata.Metadata{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list request single key",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
response.Metadata[0] = setUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, "key2", "value2")
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, "key3", "value3")
|
||||
request.Filters[0] = &metadata.MetadataSearchFilter{
|
||||
Filter: &metadata.MetadataSearchFilter_KeyFilter{KeyFilter: &metadata.MetadataKeyFilter{Key: key}},
|
||||
}
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{
|
||||
Filters: []*metadata.MetadataSearchFilter{{}},
|
||||
},
|
||||
},
|
||||
want: &user.ListUserMetadataResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 1,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Metadata: []*metadata.Metadata{
|
||||
{},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "list multiple keys",
|
||||
args: args{
|
||||
ctx: iamOwnerCTX,
|
||||
dep: func(ctx context.Context, request *user.ListUserMetadataRequest, response *user.ListUserMetadataResponse) {
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
|
||||
response.Metadata[2] = setUserMetadata(iamOwnerCTX, userID, "key1", "value1")
|
||||
response.Metadata[1] = setUserMetadata(iamOwnerCTX, userID, "key2", "value2")
|
||||
response.Metadata[0] = setUserMetadata(iamOwnerCTX, userID, "key3", "value3")
|
||||
},
|
||||
req: &user.ListUserMetadataRequest{},
|
||||
},
|
||||
want: &user.ListUserMetadataResponse{
|
||||
Pagination: &filter.PaginationResponse{
|
||||
TotalResult: 3,
|
||||
AppliedLimit: 100,
|
||||
},
|
||||
Metadata: []*metadata.Metadata{
|
||||
{}, {}, {},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if tt.args.dep != nil {
|
||||
tt.args.dep(tt.args.ctx, tt.args.req, tt.want)
|
||||
}
|
||||
|
||||
retryDuration, tick := integration.WaitForAndTickWithMaxDuration(iamOwnerCTX, time.Minute)
|
||||
require.EventuallyWithT(t, func(ttt *assert.CollectT) {
|
||||
got, listErr := Instance.Client.UserV2.ListUserMetadata(tt.args.ctx, tt.args.req)
|
||||
if tt.wantErr {
|
||||
require.Error(ttt, listErr)
|
||||
return
|
||||
}
|
||||
require.NoError(ttt, listErr)
|
||||
// always first check length, otherwise its failed anyway
|
||||
if assert.Len(ttt, got.Metadata, len(tt.want.Metadata)) {
|
||||
assert.EqualExportedValues(ttt, got.Metadata, tt.want.Metadata)
|
||||
}
|
||||
assertPaginationResponse(ttt, tt.want.Pagination, got.Pagination)
|
||||
}, retryDuration, tick, "timeout waiting for expected execution result")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func setUserMetadata(ctx context.Context, userID, key, value string) *metadata.Metadata {
|
||||
metadataResp := Instance.SetUserMetadata(ctx, userID, key, value)
|
||||
return &metadata.Metadata{
|
||||
CreationDate: metadataResp.GetSetDate(),
|
||||
ChangeDate: metadataResp.GetSetDate(),
|
||||
Key: key,
|
||||
Value: []byte(base64.StdEncoding.EncodeToString([]byte(value))),
|
||||
}
|
||||
}
|
||||
|
||||
func assertPaginationResponse(t *assert.CollectT, expected *filter.PaginationResponse, actual *filter.PaginationResponse) {
|
||||
assert.Equal(t, expected.AppliedLimit, actual.AppliedLimit)
|
||||
assert.Equal(t, expected.TotalResult, actual.TotalResult)
|
||||
}
|
||||
|
||||
func TestServer_DeleteUserMetadata(t *testing.T) {
|
||||
iamOwnerCTX := Instance.WithAuthorization(CTX, integration.UserTypeIAMOwner)
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
ctx context.Context
|
||||
prepare func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time)
|
||||
req *user.DeleteUserMetadataRequest
|
||||
wantDeletionDate bool
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "empty id",
|
||||
ctx: iamOwnerCTX,
|
||||
req: &user.DeleteUserMetadataRequest{
|
||||
UserId: "",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, user not existing",
|
||||
ctx: iamOwnerCTX,
|
||||
req: &user.DeleteUserMetadataRequest{
|
||||
UserId: "notexisting",
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
request.Keys = []string{key}
|
||||
return creationDate, time.Time{}
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantDeletionDate: true,
|
||||
},
|
||||
{
|
||||
name: "delete, empty list",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
Instance.DeleteUserMetadata(iamOwnerCTX, userID, key)
|
||||
return creationDate, time.Now().UTC()
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, already removed",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key, "value1")
|
||||
Instance.DeleteUserMetadata(iamOwnerCTX, userID, key)
|
||||
request.Keys = []string{key}
|
||||
return creationDate, time.Now().UTC()
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "delete, multiple",
|
||||
ctx: iamOwnerCTX,
|
||||
prepare: func(request *user.DeleteUserMetadataRequest) (time.Time, time.Time) {
|
||||
creationDate := time.Now().UTC()
|
||||
userID := Instance.CreateUserTypeHuman(iamOwnerCTX, gofakeit.Email()).GetId()
|
||||
request.UserId = userID
|
||||
key1 := "key1"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key1, "value1")
|
||||
key2 := "key2"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key2, "value1")
|
||||
key3 := "key3"
|
||||
Instance.SetUserMetadata(iamOwnerCTX, userID, key3, "value1")
|
||||
request.Keys = []string{key1, key2, key3}
|
||||
return creationDate, time.Time{}
|
||||
},
|
||||
req: &user.DeleteUserMetadataRequest{},
|
||||
wantDeletionDate: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
var creationDate, deletionDate time.Time
|
||||
if tt.prepare != nil {
|
||||
creationDate, deletionDate = tt.prepare(tt.req)
|
||||
}
|
||||
got, err := Instance.Client.UserV2.DeleteUserMetadata(tt.ctx, tt.req)
|
||||
if tt.wantErr {
|
||||
assert.Error(t, err)
|
||||
return
|
||||
}
|
||||
assert.NoError(t, err)
|
||||
assertDeleteProjectResponse(t, creationDate, deletionDate, tt.wantDeletionDate, got)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func assertDeleteProjectResponse(t *testing.T, creationDate, deletionDate time.Time, expectedDeletionDate bool, actualResp *user.DeleteUserMetadataResponse) {
|
||||
if expectedDeletionDate {
|
||||
if !deletionDate.IsZero() {
|
||||
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), creationDate, deletionDate)
|
||||
} else {
|
||||
assert.WithinRange(t, actualResp.GetDeletionDate().AsTime(), creationDate, time.Now().UTC())
|
||||
}
|
||||
} else {
|
||||
assert.Nil(t, actualResp.DeletionDate)
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ import (
|
||||
)
|
||||
|
||||
func TestServer_AddPersonalAccessToken(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -64,7 +64,7 @@ func TestServer_AddPersonalAccessToken(t *testing.T) {
|
||||
ExpirationDate: expirationDate,
|
||||
},
|
||||
func(request *user.AddPersonalAccessTokenRequest) error {
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX)
|
||||
resp := Instance.CreateUserTypeHuman(IamCTX, gofakeit.Email())
|
||||
request.UserId = resp.Id
|
||||
return nil
|
||||
},
|
||||
@@ -172,7 +172,7 @@ func TestServer_AddPersonalAccessToken_Permission(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestServer_RemovePersonalAccessToken(t *testing.T) {
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX)
|
||||
resp := Instance.CreateUserTypeMachine(IamCTX, Instance.DefaultOrg.Id)
|
||||
userId := resp.GetId()
|
||||
expirationDate := timestamppb.New(time.Now().Add(time.Hour * 24))
|
||||
type args struct {
|
||||
@@ -339,7 +339,7 @@ func TestServer_ListPersonalAccessTokens(t *testing.T) {
|
||||
})
|
||||
require.NoError(t, err)
|
||||
otherOrgUserId := otherOrgUser.GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX).GetId()
|
||||
otherUserId := Instance.CreateUserTypeMachine(SystemCTX, Instance.DefaultOrg.Id).GetId()
|
||||
onlySinceTestStartFilter := &user.PersonalAccessTokensSearchFilter{Filter: &user.PersonalAccessTokensSearchFilter_CreatedDateFilter{CreatedDateFilter: &filter.TimestampFilter{
|
||||
Timestamp: timestamppb.Now(),
|
||||
Method: filter.TimestampFilterMethod_TIMESTAMP_FILTER_METHOD_AFTER_OR_EQUALS,
|
||||
|
||||
@@ -43,7 +43,7 @@ func TestServer_AddSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.AddSecretRequest{},
|
||||
func(request *user.AddSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
return nil
|
||||
},
|
||||
@@ -55,7 +55,7 @@ func TestServer_AddSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.AddSecretRequest{},
|
||||
func(request *user.AddSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
return nil
|
||||
},
|
||||
@@ -67,7 +67,7 @@ func TestServer_AddSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.AddSecretRequest{},
|
||||
func(request *user.AddSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
_, err := Client.AddSecret(CTX, &user.AddSecretRequest{
|
||||
UserId: resp.GetId(),
|
||||
@@ -202,7 +202,7 @@ func TestServer_RemoveSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.RemoveSecretRequest{},
|
||||
func(request *user.RemoveSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
return nil
|
||||
},
|
||||
@@ -215,7 +215,7 @@ func TestServer_RemoveSecret(t *testing.T) {
|
||||
CTX,
|
||||
&user.RemoveSecretRequest{},
|
||||
func(request *user.RemoveSecretRequest) error {
|
||||
resp := Instance.CreateUserTypeMachine(CTX)
|
||||
resp := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id)
|
||||
request.UserId = resp.GetId()
|
||||
_, err := Instance.Client.UserV2.AddSecret(CTX, &user.AddSecretRequest{
|
||||
UserId: resp.GetId(),
|
||||
|
||||
@@ -4,6 +4,7 @@ package user_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
@@ -1818,7 +1819,7 @@ func TestServer_DeleteUser(t *testing.T) {
|
||||
request.UserId = resp.GetUserId()
|
||||
Instance.CreateProjectUserGrant(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateProjectMembership(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, Instance.DefaultOrg.Id, request.UserId)
|
||||
return CTX
|
||||
},
|
||||
},
|
||||
@@ -3945,6 +3946,44 @@ func TestServer_CreateUser(t *testing.T) {
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with metadata",
|
||||
testCase: func(runId string) testCase {
|
||||
username := fmt.Sprintf("donald.duck+%s", runId)
|
||||
email := username + "@example.com"
|
||||
return testCase{
|
||||
args: args{
|
||||
CTX,
|
||||
&user.CreateUserRequest{
|
||||
OrganizationId: Instance.DefaultOrg.Id,
|
||||
Username: &username,
|
||||
UserType: &user.CreateUserRequest_Human_{
|
||||
Human: &user.CreateUserRequest_Human{
|
||||
Profile: &user.SetHumanProfile{
|
||||
GivenName: "Donald",
|
||||
FamilyName: "Duck",
|
||||
},
|
||||
Email: &user.SetHumanEmail{
|
||||
Email: email,
|
||||
Verification: &user.SetHumanEmail_IsVerified{
|
||||
IsVerified: true,
|
||||
},
|
||||
},
|
||||
Metadata: []*user.Metadata{
|
||||
{Key: "key1", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value1")))},
|
||||
{Key: "key2", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value2")))},
|
||||
{Key: "key3", Value: []byte(base64.StdEncoding.EncodeToString([]byte("value3")))},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
want: &user.CreateUserResponse{
|
||||
Id: "is generated",
|
||||
},
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "with idp",
|
||||
testCase: func(runId string) testCase {
|
||||
@@ -4890,7 +4929,7 @@ func TestServer_UpdateUserTypeHuman(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
now := time.Now()
|
||||
runId := fmt.Sprint(now.UnixNano() + int64(i))
|
||||
userId := Instance.CreateUserTypeHuman(CTX).GetId()
|
||||
userId := Instance.CreateUserTypeHuman(CTX, gofakeit.Email()).GetId()
|
||||
test := tt.testCase(runId, userId)
|
||||
got, err := Client.UpdateUser(test.args.ctx, test.args.req)
|
||||
if test.wantErr {
|
||||
@@ -4972,7 +5011,7 @@ func TestServer_UpdateUserTypeMachine(t *testing.T) {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
now := time.Now()
|
||||
runId := fmt.Sprint(now.UnixNano() + int64(i))
|
||||
userId := Instance.CreateUserTypeMachine(CTX).GetId()
|
||||
userId := Instance.CreateUserTypeMachine(CTX, Instance.DefaultOrg.Id).GetId()
|
||||
test := tt.testCase(runId, userId)
|
||||
got, err := Client.UpdateUser(test.args.ctx, test.args.req)
|
||||
if test.wantErr {
|
||||
|
||||
@@ -0,0 +1,80 @@
|
||||
package user
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"connectrpc.com/connect"
|
||||
"google.golang.org/protobuf/types/known/timestamppb"
|
||||
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/filter/v2"
|
||||
"github.com/zitadel/zitadel/internal/api/grpc/metadata/v2"
|
||||
"github.com/zitadel/zitadel/internal/domain"
|
||||
"github.com/zitadel/zitadel/internal/query"
|
||||
"github.com/zitadel/zitadel/pkg/grpc/user/v2"
|
||||
)
|
||||
|
||||
func (s *Server) ListUserMetadata(ctx context.Context, req *connect.Request[user.ListUserMetadataRequest]) (*connect.Response[user.ListUserMetadataResponse], error) {
|
||||
metadataQueries, err := s.listUserMetadataRequestToModel(req.Msg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
res, err := s.query.SearchUserMetadata(ctx, true, req.Msg.UserId, metadataQueries, s.checkPermission)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&user.ListUserMetadataResponse{
|
||||
Metadata: metadata.UserMetadataListToPb(res.Metadata),
|
||||
Pagination: filter.QueryToPaginationPb(metadataQueries.SearchRequest, res.SearchResponse),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func (s *Server) listUserMetadataRequestToModel(req *user.ListUserMetadataRequest) (*query.UserMetadataSearchQueries, error) {
|
||||
offset, limit, asc, err := filter.PaginationPbToQuery(s.systemDefaults, req.Pagination)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
queries, err := metadata.UserMetadataFiltersToQuery(req.Filters)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &query.UserMetadataSearchQueries{
|
||||
SearchRequest: query.SearchRequest{
|
||||
Offset: offset,
|
||||
Limit: limit,
|
||||
Asc: asc,
|
||||
SortingColumn: query.UserMetadataCreationDateCol,
|
||||
},
|
||||
Queries: queries,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (s *Server) SetUserMetadata(ctx context.Context, req *connect.Request[user.SetUserMetadataRequest]) (*connect.Response[user.SetUserMetadataResponse], error) {
|
||||
result, err := s.command.BulkSetUserMetadata(ctx, req.Msg.UserId, "", setUserMetadataToDomain(req.Msg)...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&user.SetUserMetadataResponse{
|
||||
SetDate: timestamppb.New(result.EventDate),
|
||||
}), nil
|
||||
}
|
||||
|
||||
func setUserMetadataToDomain(req *user.SetUserMetadataRequest) []*domain.Metadata {
|
||||
metadata := make([]*domain.Metadata, len(req.Metadata))
|
||||
for i, data := range req.Metadata {
|
||||
metadata[i] = &domain.Metadata{
|
||||
Key: data.Key,
|
||||
Value: data.Value,
|
||||
}
|
||||
}
|
||||
return metadata
|
||||
}
|
||||
|
||||
func (s *Server) DeleteUserMetadata(ctx context.Context, req *connect.Request[user.DeleteUserMetadataRequest]) (*connect.Response[user.DeleteUserMetadataResponse], error) {
|
||||
result, err := s.command.BulkRemoveUserMetadata(ctx, req.Msg.UserId, "", req.Msg.Keys...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return connect.NewResponse(&user.DeleteUserMetadataResponse{
|
||||
DeletionDate: timestamppb.New(result.EventDate),
|
||||
}), nil
|
||||
}
|
||||
@@ -37,9 +37,9 @@ type Server struct {
|
||||
type Config struct{}
|
||||
|
||||
func CreateServer(
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
command *command.Commands,
|
||||
query *query.Queries,
|
||||
systemDefaults systemdefaults.SystemDefaults,
|
||||
userCodeAlg crypto.EncryptionAlgorithm,
|
||||
idpAlg crypto.EncryptionAlgorithm,
|
||||
idpCallback func(ctx context.Context) string,
|
||||
@@ -48,7 +48,6 @@ func CreateServer(
|
||||
checkPermission domain.PermissionCheck,
|
||||
) *Server {
|
||||
return &Server{
|
||||
systemDefaults: systemDefaults,
|
||||
command: command,
|
||||
query: query,
|
||||
userCodeAlg: userCodeAlg,
|
||||
@@ -57,6 +56,7 @@ func CreateServer(
|
||||
samlRootURL: samlRootURL,
|
||||
assetAPIPrefix: assetAPIPrefix,
|
||||
checkPermission: checkPermission,
|
||||
systemDefaults: systemDefaults,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]*
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
@@ -1773,7 +1773,7 @@ func TestServer_DeleteUser(t *testing.T) {
|
||||
request.UserId = resp.GetUserId()
|
||||
Instance.CreateProjectUserGrant(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateProjectMembership(t, CTX, projectResp.GetId(), request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, request.UserId)
|
||||
Instance.CreateOrgMembership(t, CTX, Instance.DefaultOrg.Id, request.UserId)
|
||||
},
|
||||
},
|
||||
want: &user.DeleteUserResponse{
|
||||
@@ -2125,7 +2125,7 @@ func TestServer_StartIdentityProviderIntent(t *testing.T) {
|
||||
if tt.want.url != "" && !tt.want.postForm {
|
||||
authUrl, err := url.Parse(got.GetAuthUrl())
|
||||
require.NoError(t, err)
|
||||
|
||||
|
||||
assert.Equal(t, tt.want.url, authUrl.Scheme+"://"+authUrl.Host+authUrl.Path)
|
||||
require.Len(t, authUrl.Query(), len(tt.want.parametersEqual)+len(tt.want.parametersExisting))
|
||||
|
||||
|
||||
@@ -296,7 +296,7 @@ func (s *Server) removeUserDependencies(ctx context.Context, userID string) ([]*
|
||||
}
|
||||
grants, err := s.query.UserGrants(ctx, &query.UserGrantsQueries{
|
||||
Queries: []query.SearchQuery{userGrantUserQuery},
|
||||
}, true)
|
||||
}, true, nil)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user