feat: add new api services (#5619)

* feat: add new services

* improve demos and comments

* remove unused field

* add comment to demo proto calls

* Apply suggestions from code review

Co-authored-by: Silvan <silvan.reusser@gmail.com>

---------

Co-authored-by: Silvan <silvan.reusser@gmail.com>
This commit is contained in:
Livio Spring
2023-04-11 15:37:42 +02:00
committed by GitHub
parent c0c76a8ea9
commit b3d8787921
18 changed files with 516 additions and 44 deletions

View File

@@ -78,8 +78,8 @@ func (s *Server) AuthMethods() authz.MethodMapping {
return admin.AdminService_AuthMethods
}
func (s *Server) RegisterGateway() server.GatewayFunc {
return admin.RegisterAdminServiceHandlerFromEndpoint
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return admin.RegisterAdminServiceHandler
}
func (s *Server) GatewayPathPrefix() string {

View File

@@ -76,8 +76,8 @@ func (s *Server) AuthMethods() authz.MethodMapping {
return auth.AuthService_AuthMethods
}
func (s *Server) RegisterGateway() server.GatewayFunc {
return auth.RegisterAuthServiceHandlerFromEndpoint
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return auth.RegisterAuthServiceHandler
}
func (s *Server) GatewayPathPrefix() string {

View File

@@ -70,8 +70,8 @@ func (s *Server) AuthMethods() authz.MethodMapping {
return management.ManagementService_AuthMethods
}
func (s *Server) RegisterGateway() server.GatewayFunc {
return management.RegisterManagementServiceHandlerFromEndpoint
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return management.RegisterManagementServiceHandler
}
func (s *Server) GatewayPathPrefix() string {

View File

@@ -7,8 +7,10 @@ import (
"strings"
"github.com/grpc-ecosystem/grpc-gateway/v2/runtime"
"github.com/zitadel/logging"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/protobuf/encoding/protojson"
client_middleware "github.com/zitadel/zitadel/internal/api/grpc/client/middleware"
@@ -50,26 +52,84 @@ var (
)
)
type Gateway interface {
RegisterGateway() GatewayFunc
GatewayPathPrefix() string
type Gateway struct {
mux *runtime.ServeMux
http1HostName string
connection *grpc.ClientConn
}
type GatewayFunc func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) error
func (g *Gateway) Handler() http.Handler {
return addInterceptors(g.mux, g.http1HostName)
}
func CreateGateway(ctx context.Context, g Gateway, port uint16, http1HostName string) (http.Handler, string, error) {
type RegisterGatewayFunc func(ctx context.Context, mux *runtime.ServeMux, conn *grpc.ClientConn) error
func CreateGatewayWithPrefix(ctx context.Context, g WithGatewayPrefix, port uint16, http1HostName string) (http.Handler, string, error) {
runtimeMux := runtime.NewServeMux(serveMuxOptions...)
opts := []grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(client_middleware.DefaultTracingClient()),
}
err := g.RegisterGateway()(ctx, runtimeMux, fmt.Sprintf("localhost:%d", port), opts)
connection, err := dial(ctx, port, opts)
if err != nil {
return nil, "", err
}
err = g.RegisterGateway()(ctx, runtimeMux, connection)
if err != nil {
return nil, "", fmt.Errorf("failed to register grpc gateway: %w", err)
}
return addInterceptors(runtimeMux, http1HostName), g.GatewayPathPrefix(), nil
}
func CreateGateway(ctx context.Context, port uint16, http1HostName string) (*Gateway, error) {
connection, err := dial(ctx,
port,
[]grpc.DialOption{
grpc.WithTransportCredentials(insecure.NewCredentials()),
grpc.WithUnaryInterceptor(client_middleware.DefaultTracingClient()),
})
if err != nil {
return nil, err
}
runtimeMux := runtime.NewServeMux(append(serveMuxOptions, runtime.WithHealthzEndpoint(healthpb.NewHealthClient(connection)))...)
return &Gateway{
mux: runtimeMux,
http1HostName: http1HostName,
connection: connection,
}, nil
}
func RegisterGateway(ctx context.Context, gateway *Gateway, server Server) error {
err := server.RegisterGateway()(ctx, gateway.mux, gateway.connection)
if err != nil {
return fmt.Errorf("failed to register grpc gateway: %w", err)
}
return nil
}
func dial(ctx context.Context, port uint16, opts []grpc.DialOption) (*grpc.ClientConn, error) {
endpoint := fmt.Sprintf("localhost:%d", port)
conn, err := grpc.Dial(endpoint, opts...)
if err != nil {
return nil, err
}
defer func() {
if err != nil {
if cerr := conn.Close(); cerr != nil {
logging.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
return
}
go func() {
<-ctx.Done()
if cerr := conn.Close(); cerr != nil {
logging.Infof("Failed to close conn to %s: %v", endpoint, cerr)
}
}()
}()
return conn, nil
}
func addInterceptors(handler http.Handler, http1HostName string) http.Handler {
handler = http_mw.CallDurationHandler(handler)
handler = http1Host(handler, http1HostName)

View File

@@ -3,14 +3,18 @@ package middleware
import (
"context"
"google.golang.org/grpc"
"github.com/zitadel/zitadel/internal/api/service"
_ "github.com/zitadel/zitadel/internal/statik"
"google.golang.org/grpc"
)
func ServiceHandler() grpc.UnaryServerInterceptor {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
namer := info.Server.(interface{ AppName() string })
namer, ok := info.Server.(interface{ AppName() string })
if !ok {
return handler(ctx, req)
}
ctx = service.WithService(ctx, namer.AppName())
return handler(ctx, req)
}

View File

@@ -2,9 +2,11 @@ package server
import (
"crypto/tls"
grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"github.com/zitadel/zitadel/internal/api/authz"
grpc_api "github.com/zitadel/zitadel/internal/api/grpc"
@@ -16,13 +18,21 @@ import (
)
type Server interface {
Gateway
RegisterServer(*grpc.Server)
RegisterGateway() RegisterGatewayFunc
AppName() string
MethodPrefix() string
AuthMethods() authz.MethodMapping
}
// WithGatewayPrefix extends the server interface with a prefix for the grpc gateway
//
// it's used for the System, Admin, Mgmt and Auth API
type WithGatewayPrefix interface {
Server
GatewayPathPrefix() string
}
func CreateServer(
verifier *authz.TokenVerifier,
authConfig authz.Config,
@@ -40,7 +50,7 @@ func CreateServer(
middleware.MetricsHandler(metricTypes, grpc_api.Probes...),
middleware.NoCacheInterceptor(),
middleware.ErrorHandler(),
middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_MethodPrefix),
middleware.InstanceInterceptor(queries, hostHeaderName, system_pb.SystemService_MethodPrefix, healthpb.Health_ServiceDesc.ServiceName),
middleware.AccessStorageInterceptor(accessSvc),
middleware.AuthorizationInterceptor(verifier, authConfig),
middleware.TranslationHandler(),

View File

@@ -0,0 +1,51 @@
package session
import (
"google.golang.org/grpc"
"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/query"
"github.com/zitadel/zitadel/pkg/grpc/session/v2alpha"
)
var _ session.SessionServiceServer = (*Server)(nil)
type Server struct {
session.UnimplementedSessionServiceServer
command *command.Commands
query *query.Queries
}
type Config struct{}
func CreateServer(
command *command.Commands,
query *query.Queries,
) *Server {
return &Server{
command: command,
query: query,
}
}
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
session.RegisterSessionServiceServer(grpcServer, s)
}
func (s *Server) AppName() string {
return session.SessionService_ServiceDesc.ServiceName
}
func (s *Server) MethodPrefix() string {
return session.SessionService_ServiceDesc.ServiceName
}
func (s *Server) AuthMethods() authz.MethodMapping {
return session.SessionService_AuthMethods
}
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return session.RegisterSessionServiceHandler
}

View File

@@ -0,0 +1,18 @@
package session
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/pkg/grpc/session/v2alpha"
"github.com/zitadel/zitadel/pkg/grpc/user/v2alpha"
)
func (s *Server) GetSession(ctx context.Context, req *session.GetSessionRequest) (*session.GetSessionResponse, error) {
return &session.GetSessionResponse{
Session: &session.Session{
Id: req.Id,
User: &user.User{Id: authz.GetCtxData(ctx).UserID},
},
}, nil
}

View File

@@ -67,8 +67,8 @@ func (s *Server) AuthMethods() authz.MethodMapping {
return system.SystemService_AuthMethods
}
func (s *Server) RegisterGateway() server.GatewayFunc {
return system.RegisterSystemServiceHandlerFromEndpoint
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return system.RegisterSystemServiceHandler
}
func (s *Server) GatewayPathPrefix() string {

View File

@@ -0,0 +1,51 @@
package user
import (
"google.golang.org/grpc"
"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/query"
"github.com/zitadel/zitadel/pkg/grpc/user/v2alpha"
)
var _ user.UserServiceServer = (*Server)(nil)
type Server struct {
user.UnimplementedUserServiceServer
command *command.Commands
query *query.Queries
}
type Config struct{}
func CreateServer(
command *command.Commands,
query *query.Queries,
) *Server {
return &Server{
command: command,
query: query,
}
}
func (s *Server) RegisterServer(grpcServer *grpc.Server) {
user.RegisterUserServiceServer(grpcServer, s)
}
func (s *Server) AppName() string {
return user.UserService_ServiceDesc.ServiceName
}
func (s *Server) MethodPrefix() string {
return user.UserService_ServiceDesc.ServiceName
}
func (s *Server) AuthMethods() authz.MethodMapping {
return user.UserService_AuthMethods
}
func (s *Server) RegisterGateway() server.RegisterGatewayFunc {
return user.RegisterUserServiceHandler
}

View File

@@ -0,0 +1,55 @@
package user
import (
"context"
"github.com/zitadel/zitadel/internal/api/authz"
"github.com/zitadel/zitadel/internal/errors"
"github.com/zitadel/zitadel/pkg/grpc/user/v2alpha"
)
func (s *Server) TestGet(ctx context.Context, req *user.TestGetRequest) (*user.TestGetResponse, error) {
return &user.TestGetResponse{
Ctx: req.Ctx.String(),
}, nil
}
func (s *Server) TestPost(ctx context.Context, req *user.TestPostRequest) (*user.TestPostResponse, error) {
return &user.TestPostResponse{
Ctx: req.Ctx.String(),
}, nil
}
func (s *Server) TestAuth(ctx context.Context, req *user.TestAuthRequest) (*user.TestAuthResponse, error) {
reqCtx, err := authDemo(ctx, req.Ctx)
if err != nil {
return nil, err
}
return &user.TestAuthResponse{
User: &user.User{Id: authz.GetCtxData(ctx).UserID},
Ctx: reqCtx,
}, nil
}
func authDemo(ctx context.Context, reqCtx *user.Context) (*user.Context, error) {
ro := authz.GetCtxData(ctx).ResourceOwner
if reqCtx == nil {
return &user.Context{Ctx: &user.Context_OrgId{OrgId: ro}}, nil
}
switch c := reqCtx.Ctx.(type) {
case *user.Context_OrgId:
if c.OrgId == ro {
return reqCtx, nil
}
return nil, errors.ThrowPermissionDenied(nil, "USER-dg4g", "Errors.User.NotAllowedOrg")
case *user.Context_OrgDomain:
if c.OrgDomain == "forbidden.com" {
return nil, errors.ThrowPermissionDenied(nil, "USER-SDg4g", "Errors.User.NotAllowedOrg")
}
return reqCtx, nil
case *user.Context_Instance:
return reqCtx, nil
default:
return reqCtx, nil
}
}