Home

Awesome

Open Policy Agent Middleware

This middleware integrates Open Policy Agent (OPA) to your http/gin/fiber/echo app. You can use it to enforce policies on endpoints. You can use OPA as local policy engine, or as a remote policy engine.

Installation

go get github.com/Joffref/opa-middleware

Usage Generic with OPA and HTTP

Local based policy engine

package main

import (
	"github.com/Joffref/opa-middleware"
	"github.com/Joffref/opa-middleware/config"
	"net/http"
)

var Policy = `
package policy

default allow = false

allow {
	input.path = "/api/v1/users"
	input.method = "GET"
}`

type H struct {
	Name string
}

func (h *H) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World : " + h.Name))
}

func main() {
	handler, err := opamiddleware.NewHTTPMiddleware(
		&config.Config{
			Policy: Policy,
			Query:  "data.policy.allow",
			InputCreationMethod: func(r *http.Request) (map[string]interface{}, error) {
				return map[string]interface{}{
					"path":   r.URL.Path,
					"method": r.Method,
				}, nil
			},
			ExceptedResult:   true,
			DeniedStatusCode: 403,
			DeniedMessage:    "Forbidden",
		},
		&H{
			Name: "John Doe",
		},
	)
	if err != nil {
		panic(err)
	}
	http.HandleFunc("/", handler.ServeHTTP)
	err = http.ListenAndServe(":8080", nil)
	if err != nil {
		return
	}
}

Remote based policy engine

The policy is the same as above, but the policy is stored in a remote server.

package main

import (
	"github.com/Joffref/opa-middleware"
	"github.com/Joffref/opa-middleware/config"
	"net/http"
)

type H struct {
	Name string
}

func (h *H) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	w.Write([]byte("Hello World : " + h.Name))
}

func main() {
	handler, err := opamiddleware.NewHTTPMiddleware(
		&config.Config{
			URL: "http://localhost:8181/",
			Query:  "data.policy.allow",
			InputCreationMethod: func(r *http.Request) (map[string]interface{}, error) {
				return map[string]interface{}{
					"path":   r.URL.Path,
					"method": r.Method,
				}, nil
			},
			ExceptedResult:   true,
			DeniedStatusCode: 403,
			DeniedMessage:    "Forbidden",
		},
		&H{
			Name: "John Doe",
		},
	)
	if err != nil {
		panic(err)
	}
	http.HandleFunc("/", handler.ServeHTTP)
	err = http.ListenAndServe(":8080", nil)
	if err != nil {
		return
	}
}

Usage with GIN

package main

import (
	"github.com/Joffref/opa-middleware"
	"github.com/Joffref/opa-middleware/config"
	"github.com/gin-gonic/gin"
)

func main() {
	r := gin.Default()
	middleware, err := opamiddleware.NewGinMiddleware(
		&config.Config{
			URL:           "http://localhost:8181/",
			Query:            "data.policy.allow",
			ExceptedResult:   true,
			DeniedStatusCode: 403,
			DeniedMessage:    "Forbidden",
		},
		func(c *gin.Context) (map[string]interface{}, error) {
			return map[string]interface{}{
				"path":   c.Request.URL.Path,
				"method": c.Request.Method,
			}, nil
		},
	)
	if err != nil {
		return
	}
	r.Use(middleware.Use())
	r.GET("/ping", func(c *gin.Context) {
		c.JSON(200, gin.H{
			"message": "pong",
		})
	})
	r.Run(":8080")
}

Usage with Fiber

package main

import (
	"github.com/Joffref/opa-middleware"
	"github.com/Joffref/opa-middleware/config"
	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New()
	middleware, err := opamiddleware.NewFiberMiddleware(
		&config.Config{
			URL:           "http://localhost:8181/",
			Query:            "data.policy.allow",
			ExceptedResult:   true,
			DeniedStatusCode: 403,
			DeniedMessage:    "Forbidden",
		},
		func(c *fiber.Ctx) (map[string]interface{}, error) {
			return map[string]interface{}{
				"path":   c.Path(),
				"method": c.Method(),
			}, nil
		},
	)
	if err != nil {
		return
	}
	app.Use(middleware.Use())
	app.Get("/ping", func(c *fiber.Ctx) error {
		err := c.JSON("pong")
		if err != nil {
			return err
		}
		return nil
	})
	app.Listen(":8080")
}

Usage with Echo

package main

import (
	"github.com/Joffref/opa-middleware"
	"github.com/Joffref/opa-middleware/config"
	"github.com/labstack/echo/v4"
)

func main() {
	e := echo.New()
	middleware, err := opamiddleware.NewEchoMiddleware(
		&config.Config{
			URL:           "http://localhost:8181/",
			Query:            "data.policy.allow",
			ExceptedResult:   true,
			DeniedStatusCode: 403,
			DeniedMessage:    "Forbidden",
		},
		func(c echo.Context) (map[string]interface{}, error) {
			return map[string]interface{}{
				"path":   c.Request().URL.Path,
				"method": c.Request().Method,
			}, nil
		},
	)
	if err != nil {
		return
	}
	e.Use(middleware.Use())
	e.GET("/ping", func(c echo.Context) error {
		err := c.JSON(200, "pong")
		if err != nil {
			return err
		}
		return nil
	})
	e.Start(":8080")
}