echosec

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Aug 18, 2025 License: MIT Imports: 16 Imported by: 0

README

EchoSec

EchoSec is a Golang middleware for the Labstack Echo Server that simplifies the process of enforcing request prerequisites across endpoint-method combinations. It uses an OpenAPI extension to direct routing and custom Go functions to define access rules.

Its primary use case is offloading repetitive, boilerplate security logic to middleware. On top of this, EchoSec introduces an advanced request labeling system, allowing you to define rules that dynamically assign labels to a request context when matched.

Example: Can a user with a given JWT token perform DELETE /user/:id?


OpenAPI Integration

Start by annotating your OpenAPI file with the custom x-echosec tag. Any operation requiring EchoSec enforcement must include this annotation.

Core: Functions
"/user/{userId}":
  get:
    summary: |-
        Returns the user details. Use `/me` to get the current user. Users can only fetch their own details; admins can see everything.
    operationId: getUser
    x-echosec:
      function: can_admin_user

The x-echosec.function key defines an identifier that maps to a Go function. While the string is arbitrary, we recommend defining all access modes up front and assigning them consistently.

In this example, can_admin_user refers to a function that verifies the requesting user's ability to manage the targeted user.


Parameters

You can further specify behavior using params. These are simple strings passed to the validation function to distinguish sub-cases.

"/workspace/{workspaceId}/_compute-organizations":
  post:
    operationId: computeWorkspaceOrganizations
    x-echosec:
      function: workspace_user_read
      params:
        - premium

Here, the base function workspace_user_read applies, but the premium param signals a more specific behavior to enforce.


Labels

You can assign labels to the request context using conditional expressions:

"/workspace/{workspaceId}/_compute-organizations":
  post:
    operationId: computeWorkspaceOrganizations
    x-echosec:
      function: workspace_user_read
      params:
        - premium
      labels:
        - label: "agent"
          condition: '"agent" in claims.Roles'

The condition syntax follows the Expr language.


Go Code

Function Mapping

First, configure your validation logic:

cfg, _ := echosec.NewOApiConfig(openApiBytes, map[string]echosec.OApiValidationFunc{
    // Example functions—adapt to your actual claims and access model.
    "can_admin_user": func(c echo.Context, params []string) error {
        if GetClaims(c).CanAdminUserData(c.Param("userId")) {
            return nil
        }
        return errors.NewForbiddenError()
    },
    "workspace_user_read": func(ctx echo.Context, params []string) error {
        claims := GetClaims(ctx)
        if claims.Admin {
            return nil
        }
        if slices.Contains(params, "premium") && !claims.IsPremiumWorkspace(ctx.Param("workspaceId")) {
            return errors.NewForbiddenError()
        }
        if claims.CanAccessWorkspace(ctx.Param("workspaceId")) {
            return nil
        }
        return errors.NewForbiddenError()
    },
}, true)

Here, the x-echosec.function values in the OpenAPI spec are mapped to actual Go functions. These should return nil if access is granted, or an error if denied.

  • openApiBytes is your OpenAPI spec in serialized form (either plain text or gzipped).
  • The final true argument enables request validation. If enabled, malformed requests will be rejected before reaching your access rules.

Labels in Go

Label conditions rely on data extracted from the Echo context. You can promote certain values to the top-level expression scope with .WithVars():

cfg.WithVars("Claims")

This allows your label conditions to access Claims directly.

The evaluated labels are stored in the EchoSecContext, which is made available via:

ectx.Get("echosecContext")
// or
ctx.Value("echosecContext")

The EchoSecContext struct looks like this:

type EchoSecContext struct {
    Config OApiEchoSec
    Labels []string
}
  • Config refers to the matched security configuration.
  • Labels contains the list of active labels assigned to the request.

Response Validation (Optional)

EchoSec also supports validating responses against the OpenAPI spec. This ensures your application complies with the expected schema.

Note: Request validation must be enabled for this to work.

To validate a response, use SecBlob instead of ctx.JSON or ctx.Blob:

echosec.SecBlob(ctx, http.StatusOK, map[string]any{
    "a": "foobar",
    "b": 1,
})
  • The first parameter is the Echo context.
  • The second is the HTTP status code.
  • The third is the response payload (map, string, or []byte).

If the payload doesn’t match the OpenAPI schema, EchoSec returns an error.


Putting It All Together

Using EchoSec is as simple as plugging it in as a middleware:

server := echo.New()
server.Use(echosec.WithOpenApiConfig(cfg))

Documentation

Index

Constants

View Source
const EchosecContextAttr = "echosecContext"

Variables

This section is empty.

Functions

func MustHaveLabels added in v1.2.0

func MustHaveLabels(ctx any, labels ...string) bool

func SecBlob added in v1.1.0

func SecBlob(ctx echo.Context, status int, dto any) error

SecBlob validates whether the provided dto matches the output expected by OpenAPI specification. For this to work, validation of the input must also be enabled

func WithOpenApiConfig added in v0.3.0

func WithOpenApiConfig(cfg OApiConfig) echo.MiddlewareFunc

WithOpenApiConfig creates an echo.MiddlewareFunc function given an OApiConfig object

Types

type EchoSecContext added in v1.2.0

type EchoSecContext struct {
	Config OApiEchoSec
	Labels []string
}

EchoSecContext is the object containing key echosec information, such as the configuration that has been picked up, and the dynamic labels

func GetEchoSecContext added in v1.2.0

func GetEchoSecContext(ctx any) (*EchoSecContext, error)

func MustGetEchoSecContext added in v1.2.0

func MustGetEchoSecContext(ctx any) *EchoSecContext

type Evaluator added in v1.2.0

type Evaluator struct {
	// contains filtered or unexported fields
}

func (*Evaluator) Eval added in v1.2.0

func (c *Evaluator) Eval(ctx echo.Context, operationId string, labels []Label) ([]string, error)

type Label added in v1.2.0

type Label struct {
	Condition string `yaml:"expression"`
	Label     string `yaml:"label"`
}

Label is the configuration of one dynamic label, containing the label itself and the expr condition that activates the label

type OApiConfig added in v0.3.0

type OApiConfig struct {
	// contains filtered or unexported fields
}

OApiConfig is configuration for EchoSec

func NewOApiConfig added in v0.3.0

func NewOApiConfig(openapi []byte, validators map[string]OApiValidationFunc, oapiValidationEnabled bool) (OApiConfig, error)

NewOApiConfig is a constructor for an EchoSec config. openapiB64 can be an OpenAPI definition, either plain text of compressed

func (*OApiConfig) WithVars added in v1.2.0

func (c *OApiConfig) WithVars(vars ...string)

WithVars sets a list of variables that can be found in the EchoSec context and may be handy during label condition evaluation

type OApiEchoSec added in v0.3.0

type OApiEchoSec struct {
	Function string   `yaml:"function"`
	Params   []string `yaml:"params"`
	Labels   []Label  `yaml:"labels"`
}

OApiEchoSec is the route EchoSec configuration

type OApiValidationFunc added in v0.3.0

type OApiValidationFunc func(c echo.Context, params []string) error

OApiValidationFunc is a function meant to validate whether the requesting entity has the permission to access a certain resource

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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