Documentation
¶
Overview ¶
Example (Grpc) ¶
package main
import (
"context"
"fmt"
sconfig "github.com/exoscale/stelling/config"
"github.com/exoscale/stelling/fxauthorizer"
"github.com/exoscale/stelling/fxgrpc"
"github.com/exoscale/stelling/fxgrpc/health"
"go.uber.org/fx"
"go.uber.org/fx/fxevent"
"go.uber.org/zap"
pb "google.golang.org/grpc/examples/route_guide/routeguide"
healthpb "google.golang.org/grpc/health/grpc_health_v1"
"google.golang.org/grpc/status"
)
type GrpcConfig struct {
fxgrpc.Server
fxgrpc.Client
fxauthorizer.Authorizer
}
type RouteGuideServer struct {
pb.UnimplementedRouteGuideServer
}
func NewRouteGuideServer() pb.RouteGuideServer {
return &RouteGuideServer{}
}
func main() {
conf := &GrpcConfig{}
rule := "request.service == \"grpc.health.v1.Health\""
args := []string{"authorizer-test", "--authorizer.rule", rule, "--server.address", "localhost:8080", "--client.endpoint", "localhost:8080", "--client.insecure-connection"}
if err := sconfig.Load(conf, args); err != nil {
panic(err)
}
opts := fx.Options(
// Suppressing fx logs to ensure deterministic output
fx.WithLogger(func() fxevent.Logger { return fxevent.NopLogger }),
fxgrpc.NewServerModule(conf),
fxgrpc.NewClientModule(conf),
health.Module,
fxauthorizer.NewModule(conf),
fx.Provide(
zap.NewNop,
NewRouteGuideServer,
pb.NewRouteGuideClient,
healthpb.NewHealthClient,
),
fx.Invoke(
pb.RegisterRouteGuideServer,
fxgrpc.StartGrpcServer,
runGrpc,
),
)
if err := fx.ValidateApp(opts); err != nil {
panic(err)
}
fx.New(opts).Run()
}
func runGrpc(lc fx.Lifecycle, sd fx.Shutdowner, client pb.RouteGuideClient, healthClient healthpb.HealthClient) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
go func() {
defer sd.Shutdown()
_, err := client.GetFeature(context.Background(), &pb.Point{})
res, ok := status.FromError(err)
if !ok {
panic(fmt.Sprintln("could not extract grpc status code:", err))
}
fmt.Println("Endpoint returned status", res.String())
resp, err := healthClient.Check(context.Background(), &healthpb.HealthCheckRequest{})
if err != nil {
panic(fmt.Sprintln("healthcheck request returned error:", err))
}
fmt.Println("Healthcheck returned", resp.String())
}()
return nil
},
})
}
Output: Endpoint returned status rpc error: code = PermissionDenied desc = policy denied Healthcheck returned status:SERVING
Example (Http) ¶
package main
import (
"context"
"fmt"
"net/http"
sconfig "github.com/exoscale/stelling/config"
"github.com/exoscale/stelling/fxauthorizer"
"github.com/exoscale/stelling/fxhttp"
"go.uber.org/fx"
"go.uber.org/fx/fxevent"
"go.uber.org/zap"
)
type HttpConfig struct {
fxhttp.Server
fxauthorizer.Authorizer
}
func main() {
conf := &HttpConfig{}
rule := "request.path == \"/health\""
args := []string{"authorizer-test", "--authorizer.rule", rule, "--server.address", "localhost:8081"}
if err := sconfig.Load(conf, args); err != nil {
panic(err)
}
opts := fx.Options(
// Suppressing fx logs to ensure deterministic output
fx.WithLogger(func() fxevent.Logger { return fxevent.NopLogger }),
fxhttp.NewModule(conf),
fxauthorizer.NewModule(conf),
fx.Provide(
zap.NewNop,
newMux,
),
fx.Invoke(
fxhttp.StartHttpServer,
runHttp,
),
)
if err := fx.ValidateApp(opts); err != nil {
panic(err)
}
fx.New(opts).Run()
}
func newMux() http.Handler {
mux := http.NewServeMux()
mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusOK) })
return mux
}
func runHttp(lc fx.Lifecycle, sd fx.Shutdowner) {
lc.Append(fx.Hook{
OnStart: func(ctx context.Context) error {
go func() {
defer sd.Shutdown()
req, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://localhost:8081/foobar", nil)
if err != nil {
panic(err)
}
resp, err := http.DefaultClient.Do(req)
if err != nil {
panic(fmt.Sprintln("failed to do http request", err))
}
defer resp.Body.Close()
fmt.Println("Request /foobar returned status", resp.Status)
req2, err := http.NewRequestWithContext(context.Background(), http.MethodGet, "http://localhost:8081/health", nil)
if err != nil {
panic(err)
}
resp2, err := http.DefaultClient.Do(req2)
if err != nil {
panic(fmt.Sprintln("failed to do http request", err))
}
defer resp2.Body.Close()
fmt.Println("Request /health returned status", resp2.Status)
}()
return nil
},
})
}
Output: Request /foobar returned status 403 Forbidden Request /health returned status 200 OK
Index ¶
- Constants
- func NewGrpcAuthorizer(conf AuthorizerConfig) (interceptor.GrpcAuthorizer, error)
- func NewGrpcAuthorizerServerInterceptors(a interceptor.GrpcAuthorizer) (*fxgrpc.UnaryServerInterceptor, *fxgrpc.StreamServerInterceptor)
- func NewHttpAuthorizer(conf AuthorizerConfig) (interceptor.HttpAuthorizer, error)
- func NewModule(conf AuthorizerConfig) fx.Option
- type Authorizer
- type AuthorizerConfig
- type HttpMiddlewareResult
Examples ¶
Constants ¶
View Source
const GrpcInterceptorWeight uint = 70
Setting this late in the chain so observability interceptors can monitor requests that fail authorization
Variables ¶
This section is empty.
Functions ¶
func NewGrpcAuthorizer ¶
func NewGrpcAuthorizer(conf AuthorizerConfig) (interceptor.GrpcAuthorizer, error)
func NewGrpcAuthorizerServerInterceptors ¶
func NewGrpcAuthorizerServerInterceptors(a interceptor.GrpcAuthorizer) (*fxgrpc.UnaryServerInterceptor, *fxgrpc.StreamServerInterceptor)
func NewHttpAuthorizer ¶
func NewHttpAuthorizer(conf AuthorizerConfig) (interceptor.HttpAuthorizer, error)
func NewModule ¶
func NewModule(conf AuthorizerConfig) fx.Option
NewModule provides authorization middleware to the system: * Grpc server interceptors * Http server middleware (TODO) Keep in mind that the Authorizer components for Grpc and Http are distinct, but share the same config. If you need different rules for either protocol, you must supply 2 different configurations with proper annotations to your system
Types ¶
type Authorizer ¶
type Authorizer struct {
// The CEL expression that will be evaluated for each request made to the server
Rule string `validate:"required"`
}
Logging contains the configuration options for the authorizer module
func (*Authorizer) AuthorizerConfig ¶
func (a *Authorizer) AuthorizerConfig() *Authorizer
type AuthorizerConfig ¶
type AuthorizerConfig interface {
AuthorizerConfig() *Authorizer
}
type HttpMiddlewareResult ¶
type HttpMiddlewareResult struct {
fx.Out
Middleware *fxhttp.Middleware `group:"http_middleware"`
}
func NewHttpMiddleware ¶
func NewHttpMiddleware(authorizer interceptor.HttpAuthorizer) HttpMiddlewareResult
Click to show internal directories.
Click to hide internal directories.