Files
zitadel/backend/v3/api/session/v2/session.go
T
Marco A.andGitHub 14acc60edd feat: ListSessions to relational (#11987)
# Which Problems Are Solved

This PR introduces the possibility of using the relational DB when
calling the ListSession gRPC endpoint.

# How the Problems Are Solved

  - Implement the Querier interface for ListSession:
- Create a `domain` model for the Session object to be able to map the
gRPC session into it.
- Match the permission filters on ES side to be sure the same sessions
are returned
- Implement converter methods to go from `gRPC` to `domain` models (both
ways)
- Fix the session sql relational migration: rework the trigger to update
the `expiration` field to perform the update only when the input
`lifetime` has a non-zero value.
  - Link the Querier implementation with the gRPC endpoint
- Run the same integration tests against the relational DB, making sure
the outcome is the same. ⚠️ Due to missing permission checks, some
divergence is expected. Hence, specific code to circumvent the
differences has been put in place

# Additional Context

- Closes #11039
2026-04-16 10:58:17 +02:00

60 lines
1.8 KiB
Go

package v2
import (
"context"
"connectrpc.com/connect"
"google.golang.org/protobuf/types/known/timestamppb"
"github.com/zitadel/zitadel/backend/v3/api/session/v2/convert"
"github.com/zitadel/zitadel/backend/v3/domain"
"github.com/zitadel/zitadel/backend/v3/storage/database/repository"
object "github.com/zitadel/zitadel/pkg/grpc/object/v2"
session "github.com/zitadel/zitadel/pkg/grpc/session/v2"
)
func ListSessions(ctx context.Context, request *connect.Request[session.ListSessionsRequest]) (*connect.Response[session.ListSessionsResponse], error) {
req, err := convert.ListSessionsRequestGRPCToDomain(ctx, request.Msg)
if err != nil {
return nil, err
}
q := domain.NewListSessionsQuery(req)
err = domain.Invoke(ctx, q, domain.WithSessionRepo(repository.SessionRepository()))
if err != nil {
return nil, err
}
sessions := convert.DomainSessionListToGRPCResponse(q.Result())
return connect.NewResponse(&session.ListSessionsResponse{
Sessions: sessions,
Details: &object.ListDetails{
TotalResult: uint64(len(sessions)),
// TODO(IAM-Marco): Put something meaningful once we have a reliable timestamp from DB
Timestamp: timestamppb.Now(),
},
}), nil
}
func DeleteSession(ctx context.Context, request *connect.Request[session.DeleteSessionRequest]) (*connect.Response[session.DeleteSessionResponse], error) {
sessionDeleteCmd := domain.NewDeleteSessionCommand(request.Msg.GetSessionId(), request.Msg.GetSessionToken(), true)
err := domain.Invoke(ctx, sessionDeleteCmd,
domain.WithSessionRepo(repository.SessionRepository()),
)
if err != nil {
return nil, err
}
details := &object.Details{}
if !sessionDeleteCmd.DeletedAt.IsZero() {
details.ChangeDate = timestamppb.New(sessionDeleteCmd.DeletedAt)
}
return connect.NewResponse(&session.DeleteSessionResponse{
Details: details,
}), nil
}