mirror of
https://github.com/zitadel/zitadel.git
synced 2025-08-11 20:47:32 +00:00
fix: generalise permission check for query user information (#8458)
# Which Problems Are Solved IDPLinks list and other list endpoints can provide you with empty results if the used user has no permission for the information. # How the Problems Are Solved List endpoints with subelements to users, and provided userIDQuery, will return a PermissionDenied error if no permission for the user exsists. # Additional Changes Function to check for permission is re-used from the GetUserByID. # Additional Context Closes #8451
This commit is contained in:
@@ -468,7 +468,7 @@ func (s *Server) getUserLinks(ctx context.Context, orgID string) (_ []*idp_pb.ID
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
idpUserLinks, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{userLinksResourceOwner}}, false)
|
idpUserLinks, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{userLinksResourceOwner}}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -112,7 +112,7 @@ func (s *Server) RemoveIDP(ctx context.Context, req *admin_pb.RemoveIDPRequest)
|
|||||||
}
|
}
|
||||||
userLinks, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{
|
userLinks, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{
|
||||||
Queries: []query.SearchQuery{idpQuery},
|
Queries: []query.SearchQuery{idpQuery},
|
||||||
}, true)
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -13,7 +13,7 @@ func (s *Server) ListMyLinkedIDPs(ctx context.Context, req *auth_pb.ListMyLinked
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
links, err := s.query.IDPUserLinks(ctx, q, false)
|
links, err := s.query.IDPUserLinks(ctx, q, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -26,7 +26,7 @@ func (s *Server) ListMyAuthFactors(ctx context.Context, _ *auth_pb.ListMyAuthFac
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, false)
|
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -30,7 +30,7 @@ func (s *Server) ListMyPasswordless(ctx context.Context, _ *auth_pb.ListMyPasswo
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, false)
|
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -91,7 +91,7 @@ func (s *Server) RemoveOrgIDP(ctx context.Context, req *mgmt_pb.RemoveOrgIDPRequ
|
|||||||
}
|
}
|
||||||
userLinks, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{
|
userLinks, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{
|
||||||
Queries: []query.SearchQuery{idpQuery},
|
Queries: []query.SearchQuery{idpQuery},
|
||||||
}, true)
|
}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -608,7 +608,7 @@ func (s *Server) ListHumanAuthFactors(ctx context.Context, req *mgmt_pb.ListHuma
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, false)
|
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -671,7 +671,7 @@ func (s *Server) ListHumanPasswordless(ctx context.Context, req *mgmt_pb.ListHum
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, false)
|
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -892,7 +892,7 @@ func (s *Server) ListHumanLinkedIDPs(ctx context.Context, req *mgmt_pb.ListHuman
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res, err := s.query.IDPUserLinks(ctx, queries, false)
|
res, err := s.query.IDPUserLinks(ctx, queries, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -30,11 +30,10 @@ func (s *Server) ListIDPLinks(ctx context.Context, req *user.ListIDPLinksRequest
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res, err := s.query.IDPUserLinks(ctx, queries, false)
|
res, err := s.query.IDPUserLinks(ctx, queries, s.checkPermission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
res.RemoveNoPermission(ctx, s.checkPermission)
|
|
||||||
return &user.ListIDPLinksResponse{
|
return &user.ListIDPLinksResponse{
|
||||||
Result: IDPLinksToPb(res.Links),
|
Result: IDPLinksToPb(res.Links),
|
||||||
Details: object.ToListDetails(res.SearchResponse),
|
Details: object.ToListDetails(res.SearchResponse),
|
||||||
|
@@ -122,6 +122,16 @@ func TestServer_ListIDPLinks(t *testing.T) {
|
|||||||
want *user.ListIDPLinksResponse
|
want *user.ListIDPLinksResponse
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "list links, missing userID",
|
||||||
|
args: args{
|
||||||
|
IamCTX,
|
||||||
|
&user.ListIDPLinksRequest{
|
||||||
|
UserId: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "list links, no permission",
|
name: "list links, no permission",
|
||||||
args: args{
|
args: args{
|
||||||
@@ -130,13 +140,7 @@ func TestServer_ListIDPLinks(t *testing.T) {
|
|||||||
UserId: userOrgResp.GetUserId(),
|
UserId: userOrgResp.GetUserId(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &user.ListIDPLinksResponse{
|
wantErr: true,
|
||||||
Details: &object.ListDetails{
|
|
||||||
TotalResult: 0,
|
|
||||||
Timestamp: timestamppb.Now(),
|
|
||||||
},
|
|
||||||
Result: []*user.IDPLink{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "list links, no permission, org",
|
name: "list links, no permission, org",
|
||||||
@@ -146,13 +150,7 @@ func TestServer_ListIDPLinks(t *testing.T) {
|
|||||||
UserId: userOrgResp.GetUserId(),
|
UserId: userOrgResp.GetUserId(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &user.ListIDPLinksResponse{
|
wantErr: true,
|
||||||
Details: &object.ListDetails{
|
|
||||||
TotalResult: 0,
|
|
||||||
Timestamp: timestamppb.Now(),
|
|
||||||
},
|
|
||||||
Result: []*user.IDPLink{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "list idp links, org, ok",
|
name: "list idp links, org, ok",
|
||||||
|
@@ -142,8 +142,7 @@ func (s *Server) ListPasskeys(ctx context.Context, req *user.ListPasskeysRequest
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, false)
|
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, s.checkPermission)
|
||||||
authMethods.RemoveNoPermission(ctx, s.checkPermission)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@@ -471,6 +471,16 @@ func TestServer_ListPasskeys(t *testing.T) {
|
|||||||
want *user.ListPasskeysResponse
|
want *user.ListPasskeysResponse
|
||||||
wantErr bool
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
|
{
|
||||||
|
name: "list passkeys, no userID",
|
||||||
|
args: args{
|
||||||
|
IamCTX,
|
||||||
|
&user.ListPasskeysRequest{
|
||||||
|
UserId: "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: "list passkeys, no permission",
|
name: "list passkeys, no permission",
|
||||||
args: args{
|
args: args{
|
||||||
@@ -479,18 +489,12 @@ func TestServer_ListPasskeys(t *testing.T) {
|
|||||||
UserId: userIDVerified,
|
UserId: userIDVerified,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
want: &user.ListPasskeysResponse{
|
wantErr: true,
|
||||||
Details: &object.ListDetails{
|
|
||||||
TotalResult: 0,
|
|
||||||
Timestamp: timestamppb.Now(),
|
|
||||||
},
|
|
||||||
Result: []*user.Passkey{},
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "list passkeys, none",
|
name: "list passkeys, none",
|
||||||
args: args{
|
args: args{
|
||||||
UserCTX,
|
IamCTX,
|
||||||
&user.ListPasskeysRequest{
|
&user.ListPasskeysRequest{
|
||||||
UserId: userIDWithout,
|
UserId: userIDWithout,
|
||||||
},
|
},
|
||||||
@@ -506,7 +510,7 @@ func TestServer_ListPasskeys(t *testing.T) {
|
|||||||
{
|
{
|
||||||
name: "list passkeys, registered",
|
name: "list passkeys, registered",
|
||||||
args: args{
|
args: args{
|
||||||
UserCTX,
|
IamCTX,
|
||||||
&user.ListPasskeysRequest{
|
&user.ListPasskeysRequest{
|
||||||
UserId: userIDRegistered,
|
UserId: userIDRegistered,
|
||||||
},
|
},
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/muhlemmer/gu"
|
"github.com/muhlemmer/gu"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
|
||||||
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
|
"github.com/zitadel/zitadel/internal/api/grpc/object/v2"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
@@ -15,15 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) GetUserByID(ctx context.Context, req *user.GetUserByIDRequest) (_ *user.GetUserByIDResponse, err error) {
|
func (s *Server) GetUserByID(ctx context.Context, req *user.GetUserByIDRequest) (_ *user.GetUserByIDResponse, err error) {
|
||||||
resp, err := s.query.GetUserByID(ctx, true, req.GetUserId())
|
resp, err := s.query.GetUserByIDWithPermission(ctx, true, req.GetUserId(), s.checkPermission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if authz.GetCtxData(ctx).UserID != req.GetUserId() {
|
|
||||||
if err := s.checkPermission(ctx, domain.PermissionUserRead, resp.ResourceOwner, req.GetUserId()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &user.GetUserByIDResponse{
|
return &user.GetUserByIDResponse{
|
||||||
Details: object.DomainToDetailsPb(&domain.ObjectDetails{
|
Details: object.DomainToDetailsPb(&domain.ObjectDetails{
|
||||||
Sequence: resp.Sequence,
|
Sequence: resp.Sequence,
|
||||||
|
@@ -421,7 +421,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
|
|||||||
queries := []query.SearchQuery{
|
queries := []query.SearchQuery{
|
||||||
idQuery, externalIDQuery,
|
idQuery, externalIDQuery,
|
||||||
}
|
}
|
||||||
links, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, false)
|
links, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -6,7 +6,6 @@ import (
|
|||||||
"github.com/muhlemmer/gu"
|
"github.com/muhlemmer/gu"
|
||||||
"google.golang.org/protobuf/types/known/timestamppb"
|
"google.golang.org/protobuf/types/known/timestamppb"
|
||||||
|
|
||||||
"github.com/zitadel/zitadel/internal/api/authz"
|
|
||||||
object "github.com/zitadel/zitadel/internal/api/grpc/object/v2beta"
|
object "github.com/zitadel/zitadel/internal/api/grpc/object/v2beta"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/query"
|
"github.com/zitadel/zitadel/internal/query"
|
||||||
@@ -15,15 +14,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func (s *Server) GetUserByID(ctx context.Context, req *user.GetUserByIDRequest) (_ *user.GetUserByIDResponse, err error) {
|
func (s *Server) GetUserByID(ctx context.Context, req *user.GetUserByIDRequest) (_ *user.GetUserByIDResponse, err error) {
|
||||||
resp, err := s.query.GetUserByID(ctx, true, req.GetUserId())
|
resp, err := s.query.GetUserByIDWithPermission(ctx, true, req.GetUserId(), s.checkPermission)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if authz.GetCtxData(ctx).UserID != req.GetUserId() {
|
|
||||||
if err := s.checkPermission(ctx, domain.PermissionUserRead, resp.ResourceOwner, req.GetUserId()); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return &user.GetUserByIDResponse{
|
return &user.GetUserByIDResponse{
|
||||||
Details: object.DomainToDetailsPb(&domain.ObjectDetails{
|
Details: object.DomainToDetailsPb(&domain.ObjectDetails{
|
||||||
Sequence: resp.Sequence,
|
Sequence: resp.Sequence,
|
||||||
|
@@ -434,7 +434,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
|
|||||||
queries := []query.SearchQuery{
|
queries := []query.SearchQuery{
|
||||||
idQuery, externalIDQuery,
|
idQuery, externalIDQuery,
|
||||||
}
|
}
|
||||||
links, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, false)
|
links, err := s.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -420,7 +420,7 @@ func (h *Handler) checkExternalUser(ctx context.Context, idpID, externalUserID s
|
|||||||
queries := []query.SearchQuery{
|
queries := []query.SearchQuery{
|
||||||
idQuery, externalIDQuery,
|
idQuery, externalIDQuery,
|
||||||
}
|
}
|
||||||
links, err := h.queries.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, false)
|
links, err := h.queries.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -479,7 +479,7 @@ func (l *Login) resourceOwnerOfUserIDPLink(ctx context.Context, idpConfigID stri
|
|||||||
queries := []query.SearchQuery{
|
queries := []query.SearchQuery{
|
||||||
idQuery, externalIDQuery,
|
idQuery, externalIDQuery,
|
||||||
}
|
}
|
||||||
links, err := l.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, false)
|
links, err := l.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
|
@@ -846,7 +846,7 @@ func (l *Login) updateExternalUsername(ctx context.Context, user *query.User, ex
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
links, err := l.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{externalIDQuery, idpIDQuery, userIDQuery}}, false)
|
links, err := l.query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{externalIDQuery, idpIDQuery, userIDQuery}}, nil)
|
||||||
if err != nil || len(links.Links) == 0 {
|
if err != nil || len(links.Links) == 0 {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1326,6 +1326,6 @@ func (l *Login) getUserLinks(ctx context.Context, userID, idpID string) (*query.
|
|||||||
userIDQuery,
|
userIDQuery,
|
||||||
idpIDQuery,
|
idpIDQuery,
|
||||||
},
|
},
|
||||||
}, false,
|
}, nil,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@@ -100,7 +100,7 @@ type idpProviderViewProvider interface {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type idpUserLinksProvider interface {
|
type idpUserLinksProvider interface {
|
||||||
IDPUserLinks(ctx context.Context, queries *query.IDPUserLinksSearchQuery, withOwnerRemoved bool) (*query.IDPUserLinks, error)
|
IDPUserLinks(ctx context.Context, queries *query.IDPUserLinksSearchQuery, permissionCheck domain.PermissionCheck) (*query.IDPUserLinks, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type userEventProvider interface {
|
type userEventProvider interface {
|
||||||
@@ -1000,7 +1000,7 @@ func (repo *AuthRequestRepo) checkExternalUserLogin(ctx context.Context, request
|
|||||||
}
|
}
|
||||||
queries = append(queries, orgIDQuery)
|
queries = append(queries, orgIDQuery)
|
||||||
}
|
}
|
||||||
links, err := repo.Query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, false)
|
links, err := repo.Query.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: queries}, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@@ -1200,7 +1200,7 @@ func checkExternalIDPsOfUser(ctx context.Context, idpUserLinksProvider idpUserLi
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return idpUserLinksProvider.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{userIDQuery}}, false)
|
return idpUserLinksProvider.IDPUserLinks(ctx, &query.IDPUserLinksSearchQuery{Queries: []query.SearchQuery{userIDQuery}}, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (repo *AuthRequestRepo) usersForUserSelection(ctx context.Context, request *domain.AuthRequest) ([]domain.UserSelection, error) {
|
func (repo *AuthRequestRepo) usersForUserSelection(ctx context.Context, request *domain.AuthRequest) ([]domain.UserSelection, error) {
|
||||||
|
@@ -298,7 +298,7 @@ type mockIDPUserLinks struct {
|
|||||||
idps []*query.IDPUserLink
|
idps []*query.IDPUserLink
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *mockIDPUserLinks) IDPUserLinks(ctx context.Context, queries *query.IDPUserLinksSearchQuery, withOwnerRemoved bool) (*query.IDPUserLinks, error) {
|
func (m *mockIDPUserLinks) IDPUserLinks(ctx context.Context, queries *query.IDPUserLinksSearchQuery, permissionCheck domain.PermissionCheck) (*query.IDPUserLinks, error) {
|
||||||
return &query.IDPUserLinks{Links: m.idps}, nil
|
return &query.IDPUserLinks{Links: m.idps}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,6 +3,7 @@ package query
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
|
"slices"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
|
||||||
@@ -42,6 +43,15 @@ func (q *IDPUserLinksSearchQuery) toQuery(query sq.SelectBuilder) sq.SelectBuild
|
|||||||
return query
|
return query
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (q *IDPUserLinksSearchQuery) hasUserID() bool {
|
||||||
|
for _, query := range q.Queries {
|
||||||
|
if query.Col() == IDPUserLinkUserIDCol {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
idpUserLinkTable = table{
|
idpUserLinkTable = table{
|
||||||
name: projection.IDPUserLinkTable,
|
name: projection.IDPUserLinkTable,
|
||||||
@@ -89,30 +99,33 @@ var (
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
func (l *IDPUserLinks) RemoveNoPermission(ctx context.Context, permissionCheck domain.PermissionCheck) {
|
func idpLinksCheckPermission(ctx context.Context, links *IDPUserLinks, permissionCheck domain.PermissionCheck) {
|
||||||
removableIndexes := make([]int, 0)
|
links.Links = slices.DeleteFunc(links.Links,
|
||||||
for i := range l.Links {
|
func(link *IDPUserLink) bool {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
return userCheckPermission(ctx, link.ResourceOwner, link.UserID, permissionCheck) != nil
|
||||||
if ctxData.UserID != l.Links[i].UserID {
|
},
|
||||||
if err := permissionCheck(ctx, domain.PermissionUserRead, l.Links[i].ResourceOwner, l.Links[i].UserID); err != nil {
|
)
|
||||||
removableIndexes = append(removableIndexes, i)
|
}
|
||||||
|
|
||||||
|
func (q *Queries) IDPUserLinks(ctx context.Context, queries *IDPUserLinksSearchQuery, permissionCheck domain.PermissionCheck) (idps *IDPUserLinks, err error) {
|
||||||
|
links, err := q.idpUserLinks(ctx, queries, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if permissionCheck != nil && len(links.Links) > 0 {
|
||||||
|
// when userID for query is provided, only one check has to be done
|
||||||
|
if queries.hasUserID() {
|
||||||
|
if err := userCheckPermission(ctx, links.Links[0].ResourceOwner, links.Links[0].UserID, permissionCheck); err != nil {
|
||||||
|
return nil, err
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
idpLinksCheckPermission(ctx, links, permissionCheck)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
removed := 0
|
return links, nil
|
||||||
for _, removeIndex := range removableIndexes {
|
|
||||||
l.Links = removeIDPLink(l.Links, removeIndex-removed)
|
|
||||||
removed++
|
|
||||||
}
|
|
||||||
// reset count as some users could be removed
|
|
||||||
l.SearchResponse.Count = uint64(len(l.Links))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeIDPLink(slice []*IDPUserLink, s int) []*IDPUserLink {
|
func (q *Queries) idpUserLinks(ctx context.Context, queries *IDPUserLinksSearchQuery, withOwnerRemoved bool) (idps *IDPUserLinks, err error) {
|
||||||
return append(slice[:s], slice[s+1:]...)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (q *Queries) IDPUserLinks(ctx context.Context, queries *IDPUserLinksSearchQuery, withOwnerRemoved bool) (idps *IDPUserLinks, err error) {
|
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package query
|
package query
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"database/sql/driver"
|
"database/sql/driver"
|
||||||
"errors"
|
"errors"
|
||||||
@@ -8,9 +9,175 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestUser_idpLinksCheckPermission(t *testing.T) {
|
||||||
|
type want struct {
|
||||||
|
links []*IDPUserLink
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
user string
|
||||||
|
links *IDPUserLinks
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want want
|
||||||
|
permissions []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"permissions for all users",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"first", "second", "third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for one user, first",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{
|
||||||
|
{UserID: "first"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"first"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for one user, second",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{
|
||||||
|
{UserID: "second"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"second"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for one user, third",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{
|
||||||
|
{UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for two users, first",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"first", "third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for two users, second",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{
|
||||||
|
{UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"second", "third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no permissions",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{},
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no permissions, self",
|
||||||
|
args{
|
||||||
|
"second",
|
||||||
|
&IDPUserLinks{
|
||||||
|
Links: []*IDPUserLink{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
links: []*IDPUserLink{{UserID: "second"}},
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
checkPermission := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
||||||
|
for _, perm := range tt.permissions {
|
||||||
|
if resourceID == perm {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("failed")
|
||||||
|
}
|
||||||
|
idpLinksCheckPermission(authz.SetCtxData(context.Background(), authz.CtxData{UserID: tt.args.user}), tt.args.links, checkPermission)
|
||||||
|
require.Equal(t, tt.want.links, tt.args.links.Links)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
idpUserLinksQuery = regexp.QuoteMeta(`SELECT projections.idp_user_links3.idp_id,` +
|
idpUserLinksQuery = regexp.QuoteMeta(`SELECT projections.idp_user_links3.idp_id,` +
|
||||||
` projections.idp_user_links3.user_id,` +
|
` projections.idp_user_links3.user_id,` +
|
||||||
|
@@ -439,11 +439,10 @@ func TestQueries_IsOrgUnique(t *testing.T) {
|
|||||||
t.Errorf("expectation was met: %v", err)
|
t.Errorf("expectation was met: %v", err)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestOrg_RemoveNoPermission(t *testing.T) {
|
func TestOrg_orgsCheckPermission(t *testing.T) {
|
||||||
type want struct {
|
type want struct {
|
||||||
orgs []*Org
|
orgs []*Org
|
||||||
}
|
}
|
||||||
|
@@ -125,15 +125,9 @@ type NotifyUser struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersCheckPermission(ctx context.Context, users *Users, permissionCheck domain.PermissionCheck) {
|
func usersCheckPermission(ctx context.Context, users *Users, permissionCheck domain.PermissionCheck) {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
|
||||||
users.Users = slices.DeleteFunc(users.Users,
|
users.Users = slices.DeleteFunc(users.Users,
|
||||||
func(user *User) bool {
|
func(user *User) bool {
|
||||||
if ctxData.UserID != user.ID {
|
return userCheckPermission(ctx, user.ResourceOwner, user.ID, permissionCheck) != nil
|
||||||
if err := permissionCheck(ctx, domain.PermissionUserRead, user.ResourceOwner, user.ID); err != nil {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -347,6 +341,27 @@ var (
|
|||||||
//go:embed user_by_id.sql
|
//go:embed user_by_id.sql
|
||||||
var userByIDQuery string
|
var userByIDQuery string
|
||||||
|
|
||||||
|
func userCheckPermission(ctx context.Context, resourceOwner string, userID string, permissionCheck domain.PermissionCheck) error {
|
||||||
|
ctxData := authz.GetCtxData(ctx)
|
||||||
|
if ctxData.UserID != userID {
|
||||||
|
if err := permissionCheck(ctx, domain.PermissionUserRead, resourceOwner, userID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) GetUserByIDWithPermission(ctx context.Context, shouldTriggerBulk bool, userID string, permissionCheck domain.PermissionCheck) (*User, error) {
|
||||||
|
user, err := q.GetUserByID(ctx, shouldTriggerBulk, userID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if err := userCheckPermission(ctx, user.ResourceOwner, user.ID, permissionCheck); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return user, nil
|
||||||
|
}
|
||||||
|
|
||||||
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string) (user *User, err error) {
|
func (q *Queries) GetUserByID(ctx context.Context, shouldTriggerBulk bool, userID string) (user *User, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
@@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
|
"slices"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
@@ -98,27 +99,12 @@ type AuthMethods struct {
|
|||||||
AuthMethods []*AuthMethod
|
AuthMethods []*AuthMethod
|
||||||
}
|
}
|
||||||
|
|
||||||
func (l *AuthMethods) RemoveNoPermission(ctx context.Context, permissionCheck domain.PermissionCheck) {
|
func authMethodsCheckPermission(ctx context.Context, methods *AuthMethods, permissionCheck domain.PermissionCheck) {
|
||||||
removableIndexes := make([]int, 0)
|
methods.AuthMethods = slices.DeleteFunc(methods.AuthMethods,
|
||||||
for i := range l.AuthMethods {
|
func(method *AuthMethod) bool {
|
||||||
ctxData := authz.GetCtxData(ctx)
|
return userCheckPermission(ctx, method.ResourceOwner, method.UserID, permissionCheck) != nil
|
||||||
if ctxData.UserID != l.AuthMethods[i].UserID {
|
},
|
||||||
if err := permissionCheck(ctx, domain.PermissionUserRead, l.AuthMethods[i].ResourceOwner, l.AuthMethods[i].UserID); err != nil {
|
)
|
||||||
removableIndexes = append(removableIndexes, i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
removed := 0
|
|
||||||
for _, removeIndex := range removableIndexes {
|
|
||||||
l.AuthMethods = removeAuthMethod(l.AuthMethods, removeIndex-removed)
|
|
||||||
removed++
|
|
||||||
}
|
|
||||||
// reset count as some users could be removed
|
|
||||||
l.SearchResponse.Count = uint64(len(l.AuthMethods))
|
|
||||||
}
|
|
||||||
|
|
||||||
func removeAuthMethod(slice []*AuthMethod, s int) []*AuthMethod {
|
|
||||||
return append(slice[:s], slice[s+1:]...)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AuthMethod struct {
|
type AuthMethod struct {
|
||||||
@@ -144,7 +130,34 @@ type UserAuthMethodSearchQueries struct {
|
|||||||
Queries []SearchQuery
|
Queries []SearchQuery
|
||||||
}
|
}
|
||||||
|
|
||||||
func (q *Queries) SearchUserAuthMethods(ctx context.Context, queries *UserAuthMethodSearchQueries, withOwnerRemoved bool) (userAuthMethods *AuthMethods, err error) {
|
func (q *UserAuthMethodSearchQueries) hasUserID() bool {
|
||||||
|
for _, query := range q.Queries {
|
||||||
|
if query.Col() == UserAuthMethodColumnUserID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) SearchUserAuthMethods(ctx context.Context, queries *UserAuthMethodSearchQueries, permissionCheck domain.PermissionCheck) (userAuthMethods *AuthMethods, err error) {
|
||||||
|
methods, err := q.searchUserAuthMethods(ctx, queries, false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if permissionCheck != nil && len(methods.AuthMethods) > 0 {
|
||||||
|
// when userID for query is provided, only one check has to be done
|
||||||
|
if queries.hasUserID() {
|
||||||
|
if err := userCheckPermission(ctx, methods.AuthMethods[0].ResourceOwner, methods.AuthMethods[0].UserID, permissionCheck); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
authMethodsCheckPermission(ctx, methods, permissionCheck)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return methods, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (q *Queries) searchUserAuthMethods(ctx context.Context, queries *UserAuthMethodSearchQueries, withOwnerRemoved bool) (userAuthMethods *AuthMethods, err error) {
|
||||||
ctx, span := tracing.NewSpan(ctx)
|
ctx, span := tracing.NewSpan(ctx)
|
||||||
defer func() { span.EndWithError(err) }()
|
defer func() { span.EndWithError(err) }()
|
||||||
|
|
||||||
|
@@ -10,11 +10,176 @@ import (
|
|||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
sq "github.com/Masterminds/squirrel"
|
sq "github.com/Masterminds/squirrel"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestUser_authMethodsCheckPermission(t *testing.T) {
|
||||||
|
type want struct {
|
||||||
|
methods []*AuthMethod
|
||||||
|
}
|
||||||
|
type args struct {
|
||||||
|
user string
|
||||||
|
methods *AuthMethods
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
args args
|
||||||
|
want want
|
||||||
|
permissions []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"permissions for all users",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"first", "second", "third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for one user, first",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{
|
||||||
|
{UserID: "first"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"first"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for one user, second",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{
|
||||||
|
{UserID: "second"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"second"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for one user, third",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{
|
||||||
|
{UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for two users, first",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"first", "third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"permissions for two users, second",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{
|
||||||
|
{UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
[]string{"second", "third"},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no permissions",
|
||||||
|
args{
|
||||||
|
"none",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{},
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"no permissions, self",
|
||||||
|
args{
|
||||||
|
"second",
|
||||||
|
&AuthMethods{
|
||||||
|
AuthMethods: []*AuthMethod{
|
||||||
|
{UserID: "first"}, {UserID: "second"}, {UserID: "third"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
want{
|
||||||
|
methods: []*AuthMethod{{UserID: "second"}},
|
||||||
|
},
|
||||||
|
[]string{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
checkPermission := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
||||||
|
for _, perm := range tt.permissions {
|
||||||
|
if resourceID == perm {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("failed")
|
||||||
|
}
|
||||||
|
authMethodsCheckPermission(authz.SetCtxData(context.Background(), authz.CtxData{UserID: tt.args.user}), tt.args.methods, checkPermission)
|
||||||
|
require.Equal(t, tt.want.methods, tt.args.methods.AuthMethods)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
prepareUserAuthMethodsStmt = `SELECT projections.user_auth_methods4.token_id,` +
|
prepareUserAuthMethodsStmt = `SELECT projections.user_auth_methods4.token_id,` +
|
||||||
` projections.user_auth_methods4.creation_date,` +
|
` projections.user_auth_methods4.creation_date,` +
|
||||||
|
@@ -9,15 +9,17 @@ import (
|
|||||||
"regexp"
|
"regexp"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/text/language"
|
"golang.org/x/text/language"
|
||||||
|
|
||||||
|
"github.com/zitadel/zitadel/internal/api/authz"
|
||||||
"github.com/zitadel/zitadel/internal/database"
|
"github.com/zitadel/zitadel/internal/database"
|
||||||
"github.com/zitadel/zitadel/internal/domain"
|
"github.com/zitadel/zitadel/internal/domain"
|
||||||
"github.com/zitadel/zitadel/internal/zerrors"
|
"github.com/zitadel/zitadel/internal/zerrors"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestUser_RemoveNoPermission(t *testing.T) {
|
func TestUser_usersCheckPermission(t *testing.T) {
|
||||||
type want struct {
|
type want struct {
|
||||||
users []*User
|
users []*User
|
||||||
}
|
}
|
||||||
@@ -140,6 +142,85 @@ func TestUser_RemoveNoPermission(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestUser_userCheckPermission(t *testing.T) {
|
||||||
|
type args struct {
|
||||||
|
ctxData string
|
||||||
|
resourceowner string
|
||||||
|
user string
|
||||||
|
}
|
||||||
|
type perm struct {
|
||||||
|
resourceowner string
|
||||||
|
user string
|
||||||
|
}
|
||||||
|
tests := []struct {
|
||||||
|
name string
|
||||||
|
wantErr bool
|
||||||
|
args args
|
||||||
|
permissions []perm
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "permission, self",
|
||||||
|
args: args{
|
||||||
|
resourceowner: "org",
|
||||||
|
user: "user",
|
||||||
|
ctxData: "user",
|
||||||
|
},
|
||||||
|
permissions: []perm{},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "permission, user",
|
||||||
|
args: args{
|
||||||
|
resourceowner: "org1",
|
||||||
|
user: "user1",
|
||||||
|
ctxData: "user2",
|
||||||
|
},
|
||||||
|
permissions: []perm{{"org1", "user1"}},
|
||||||
|
wantErr: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "permission, org",
|
||||||
|
args: args{
|
||||||
|
resourceowner: "org1",
|
||||||
|
user: "user1",
|
||||||
|
ctxData: "user2",
|
||||||
|
},
|
||||||
|
permissions: []perm{{"org1", "user3"}},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "permission, none",
|
||||||
|
args: args{
|
||||||
|
resourceowner: "org1",
|
||||||
|
user: "user1",
|
||||||
|
ctxData: "user2",
|
||||||
|
},
|
||||||
|
permissions: []perm{},
|
||||||
|
wantErr: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, tt := range tests {
|
||||||
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
|
checkPermission := func(ctx context.Context, permission, orgID, resourceID string) (err error) {
|
||||||
|
for _, perm := range tt.permissions {
|
||||||
|
if resourceID == perm.user {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
if orgID == perm.resourceowner {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return errors.New("failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
granted := userCheckPermission(authz.SetCtxData(context.Background(), authz.CtxData{UserID: tt.args.ctxData}), tt.args.resourceowner, tt.args.user, checkPermission)
|
||||||
|
if tt.wantErr {
|
||||||
|
assert.Error(t, granted)
|
||||||
|
} else {
|
||||||
|
assert.NoError(t, granted)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var (
|
var (
|
||||||
loginNamesQuery = `SELECT login_names.user_id, ARRAY_AGG(login_names.login_name)::TEXT[] AS loginnames, ARRAY_AGG(LOWER(login_names.login_name))::TEXT[] AS loginnames_lower, login_names.instance_id` +
|
loginNamesQuery = `SELECT login_names.user_id, ARRAY_AGG(login_names.login_name)::TEXT[] AS loginnames, ARRAY_AGG(LOWER(login_names.login_name))::TEXT[] AS loginnames_lower, login_names.instance_id` +
|
||||||
` FROM projections.login_names3 AS login_names` +
|
` FROM projections.login_names3 AS login_names` +
|
||||||
|
Reference in New Issue
Block a user