openapi

package module
v0.11.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jun 5, 2024 License: Apache-2.0 Imports: 17 Imported by: 0

README

caddy-openapi

This middleware validates HTTP request and response against a OpenAPI V3 Specification file

Installation

Build caddy with caddy-openapi, run make. This will build for Linux, Windows and OSX.

You can also build with xcaddy

xcaddy build \
    --with github.com/chukmunnlee/caddy-openapi

Tested with go version go1.22.3 linux/amd64 on Linux <name> 6.5.0-35-generic #35~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC <timestamp> x86_64 x86_64 x86_64 GNU/Linux

Usage

Caddyfile

Load examples/customer/customer.yaml file with defaults

:8080 {
  route /api {
    openapi ./examples/customer/customer.yaml
  }
}

One with all the options

:8080 {
  route /api {
    openapi {
      spec ./examples/customer/customer.yaml
      fall_through
      log_error
    }
  }
}

Reports any errors as a {openapi.error} placeholder which can be used in other directives like respond

Fields Description
spec <oas_file> The OpenAPI3 YAML file. This attribute is a mandatory
policy_bundle <bundle> OPA policy bundle created with opa build.
fall_through Toggles fall through when the request does do match the provided OpenAPI spec. Default is false
validate_servers Enable server validation. Accepts true, false or just the directive which enables validation. Default is true.
log_error Toggles error logging. Default is false
check Enable validation of the request parameters; include one or more of the following directives in the body:req_params, req_body and resp_body. resp_body only validates application/json payload. Note that validating the request body will implicitly set req_params

Errors are reported in the following placeholders. You can use them in other directives like respond

Placeholders Description
openapi.error Description of the error
openapi.status_code Suggested status code
openapi.response_error Resonse error

Example

The following example validates all request, including query string as well as payloads, to localhost:8080/api against the ./examples/customer/customer.yaml file. Any non compliant request will be logged to Caddy's console. Respond to the client with the error {openapi.error}.

:8080 {

  @api {
    path /api/*
  }

  reverse_proxy @api {
    to localhost:3000
  }

  route @api {
    openapi {
      spec ./examples/customer/customer.yaml 
      policy_bundle ./examples/policy/bundle.tar.gz
      check {
        req_body 
        resp_body 
      }
      validate_servers
      log_error 
    }
  }

  handle_errors {
    respond @api "Resource: {http.request.orig_uri}. Error: {openapi.error}" {openapi.status_code}  {
      close
    }
  }
}

Try out the customer.yaml API by running the accompanying node application.

Using OpenPolicyAgent

You can enforce policies on routes by adding the x-policy field to either the OpenAPI3 document level, or the path item level or or the operation level.

If a x-policy field is added at the

  • OpenAPI3 document then the policy will be applied to all path
  • Path item then the policy will be applied to all methods specified for that path eg POST, GET to /api/v1/customer
  • Operation then the policy will only be applied to that operation eg. GET/api/v1/customer x-policy attribute nested deeper into the

The 'deeper' a x-policy field, the higher its precedence. Since policy_bundle is optional, no x-policy will be evaluated if no bundle are loaded.

Assume the following OPA policy file

package authz

default allow = false

allow {
  lower(input.method) = "get"
  array.slice(input.path, 0, 2) = [ "api", "customer" ]
  to_number(input.pathParams.custId) >= 100
}

has been bundled as bundle.tar.gz. Load it with policy_bundle

The following OpenAPI3 fragment show how you can evaluate authz.allow on all GET /api/customer/

paths:
  /api/customer/{custId}:
    get:
      description: Get customer
      operationId: getCustomer
      x-policy: authz.allow
      parameters:
      - name: custId
        in: path
        required: true
        schema:
           type: number

The HTTP request are converted into input according to the following table

Fields Description
input.scheme HTTP or HTTPS
input.host Host and port number
input.method HTTP method
input.path Array of path elements eg. /api/customer/123 is converted to [ 'api', 'customer', '123' ]
input.remoteAddr Host and port number of the client
input.queryString If a query string is present, the query string will be destructed into a map under queryString root. Example ?offset=10&limit=10 will be converted to the following keys: input.queryString.offset and input.queryString.limit. Query parameters with multiple value will have an array as its value. queryString will not be present if the request do not contain any query params
input.pathParams Like query string but a map of matched path parameters from the OpenAPI3 spec where parameter type is in: path. See above example
input.headers Map of all the request headers
input.body Access to the request's body. Only supports application/json content type. Not implemented yet

Assume all values are string

This plugin currently can only work with policies/rules that return true or false.

Documentation

Index

Constants

View Source
const (
	MODULE_ID              = "http.handlers.openapi"
	X_POLICY               = "x-policy"
	OPENAPI_ERROR          = "openapi.error"
	OPENAPI_STATUS_CODE    = "openapi.status_code"
	OPENAPI_RESPONSE_ERROR = "openapi.response_error"
	TOKEN_OPENAPI          = "openapi"
	TOKEN_POLICY_BUNDLE    = "policy_bundle"
	TOKEN_SPEC             = "spec"
	TOKEN_FALL_THROUGH     = "fall_through"
	TOKEN_LOG_ERROR        = "log_error"
	TOKEN_VALIDATE_SERVERS = "validate_servers"
	TOKEN_CHECK            = "check"
	VALUE_REQ_PARAMS       = "req_params"
	VALUE_REQ_BODY         = "req_body"
	VALUE_RESP_BODY        = "resp_body"
)

Variables

This section is empty.

Functions

This section is empty.

Types

type CheckOptions

type CheckOptions struct {
	// Enable request query validation. Default is `false`
	RequestParams bool `json:"req_params,omitempty"`

	// Enable request payload validation. Default is `false`
	RequestBody bool `json:"req_body,omitempty"`

	// Enable response body validation with an optional list of
	// `Content-Type` to examine. Default `application/json`. If you set
	// your content type, the default will be removed
	ResponseBody []string `json:"resp_body,omitempty"`
}

type OpenAPI

type OpenAPI struct {
	// The location of the OASv3 file
	Spec string `json:"spec"`

	PolicyBundle string `json:"policy_bundle"`

	// Should the request proceed if it fails validation. Default is `false`
	FallThrough bool `json:"fall_through,omitempty"`

	// Should the non compliant request be logged? Default is `false`
	LogError bool `json:"log_error,omitempty"`

	// Enable request and response validation
	Check *CheckOptions `json:"check,omitempty"`

	// Enable server validation
	ValidateServers bool `json:"valid_servers,omitempty"`
	// contains filtered or unexported fields
}

This middleware validates request against an OpenAPI V3 specification. No conforming request can be rejected

func (OpenAPI) CaddyModule

func (oapi OpenAPI) CaddyModule() caddy.ModuleInfo

func (*OpenAPI) Provision

func (oapi *OpenAPI) Provision(ctx caddy.Context) error

func (OpenAPI) ServeHTTP

func (oapi OpenAPI) ServeHTTP(w http.ResponseWriter, req *http.Request, next caddyhttp.Handler) error

func (*OpenAPI) UnmarshalCaddyfile

func (oapi *OpenAPI) UnmarshalCaddyfile(d *caddyfile.Dispenser) error

func (OpenAPI) Validate

func (oapi OpenAPI) Validate() error

type WrapperResponseWriter

type WrapperResponseWriter struct {
	http.ResponseWriter
	StatusCode int
	Buffer     []byte
}

func (*WrapperResponseWriter) Write

func (w *WrapperResponseWriter) Write(buff []byte) (int, error)

func (*WrapperResponseWriter) WriteHeader

func (w *WrapperResponseWriter) WriteHeader(sc int)

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL