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:
Stefan Benz
2024-08-23 08:44:18 +02:00
committed by GitHub
parent 8051a63147
commit 2847806531
27 changed files with 552 additions and 111 deletions

View File

@@ -30,11 +30,10 @@ func (s *Server) ListIDPLinks(ctx context.Context, req *user.ListIDPLinksRequest
if err != nil {
return nil, err
}
res, err := s.query.IDPUserLinks(ctx, queries, false)
res, err := s.query.IDPUserLinks(ctx, queries, s.checkPermission)
if err != nil {
return nil, err
}
res.RemoveNoPermission(ctx, s.checkPermission)
return &user.ListIDPLinksResponse{
Result: IDPLinksToPb(res.Links),
Details: object.ToListDetails(res.SearchResponse),

View File

@@ -122,6 +122,16 @@ func TestServer_ListIDPLinks(t *testing.T) {
want *user.ListIDPLinksResponse
wantErr bool
}{
{
name: "list links, missing userID",
args: args{
IamCTX,
&user.ListIDPLinksRequest{
UserId: "",
},
},
wantErr: true,
},
{
name: "list links, no permission",
args: args{
@@ -130,13 +140,7 @@ func TestServer_ListIDPLinks(t *testing.T) {
UserId: userOrgResp.GetUserId(),
},
},
want: &user.ListIDPLinksResponse{
Details: &object.ListDetails{
TotalResult: 0,
Timestamp: timestamppb.Now(),
},
Result: []*user.IDPLink{},
},
wantErr: true,
},
{
name: "list links, no permission, org",
@@ -146,13 +150,7 @@ func TestServer_ListIDPLinks(t *testing.T) {
UserId: userOrgResp.GetUserId(),
},
},
want: &user.ListIDPLinksResponse{
Details: &object.ListDetails{
TotalResult: 0,
Timestamp: timestamppb.Now(),
},
Result: []*user.IDPLink{},
},
wantErr: true,
},
{
name: "list idp links, org, ok",

View File

@@ -142,8 +142,7 @@ func (s *Server) ListPasskeys(ctx context.Context, req *user.ListPasskeysRequest
if err != nil {
return nil, err
}
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, false)
authMethods.RemoveNoPermission(ctx, s.checkPermission)
authMethods, err := s.query.SearchUserAuthMethods(ctx, query, s.checkPermission)
if err != nil {
return nil, err
}

View File

@@ -471,6 +471,16 @@ func TestServer_ListPasskeys(t *testing.T) {
want *user.ListPasskeysResponse
wantErr bool
}{
{
name: "list passkeys, no userID",
args: args{
IamCTX,
&user.ListPasskeysRequest{
UserId: "",
},
},
wantErr: true,
},
{
name: "list passkeys, no permission",
args: args{
@@ -479,18 +489,12 @@ func TestServer_ListPasskeys(t *testing.T) {
UserId: userIDVerified,
},
},
want: &user.ListPasskeysResponse{
Details: &object.ListDetails{
TotalResult: 0,
Timestamp: timestamppb.Now(),
},
Result: []*user.Passkey{},
},
wantErr: true,
},
{
name: "list passkeys, none",
args: args{
UserCTX,
IamCTX,
&user.ListPasskeysRequest{
UserId: userIDWithout,
},
@@ -506,7 +510,7 @@ func TestServer_ListPasskeys(t *testing.T) {
{
name: "list passkeys, registered",
args: args{
UserCTX,
IamCTX,
&user.ListPasskeysRequest{
UserId: userIDRegistered,
},

View File

@@ -6,7 +6,6 @@ import (
"github.com/muhlemmer/gu"
"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/domain"
"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) {
resp, err := s.query.GetUserByID(ctx, true, req.GetUserId())
resp, err := s.query.GetUserByIDWithPermission(ctx, true, req.GetUserId(), s.checkPermission)
if err != nil {
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{
Details: object.DomainToDetailsPb(&domain.ObjectDetails{
Sequence: resp.Sequence,

View File

@@ -421,7 +421,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
queries := []query.SearchQuery{
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 {
return "", err
}

View File

@@ -6,7 +6,6 @@ import (
"github.com/muhlemmer/gu"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/internal/api/authz"
object "github.com/zitadel/zitadel/internal/api/grpc/object/v2beta"
"github.com/zitadel/zitadel/internal/domain"
"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) {
resp, err := s.query.GetUserByID(ctx, true, req.GetUserId())
resp, err := s.query.GetUserByIDWithPermission(ctx, true, req.GetUserId(), s.checkPermission)
if err != nil {
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{
Details: object.DomainToDetailsPb(&domain.ObjectDetails{
Sequence: resp.Sequence,

View File

@@ -434,7 +434,7 @@ func (s *Server) checkLinkedExternalUser(ctx context.Context, idpID, externalUse
queries := []query.SearchQuery{
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 {
return "", err
}