-
Notifications
You must be signed in to change notification settings - Fork 5
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Added echo middleware with tests (#16)
- Loading branch information
1 parent
28c1b9a
commit dbca33c
Showing
5 changed files
with
324 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
package opamiddleware | ||
|
||
import ( | ||
"errors" | ||
"github.com/Joffref/opa-middleware/config" | ||
"github.com/Joffref/opa-middleware/internal" | ||
"github.com/labstack/echo/v4" | ||
"net/http" | ||
) | ||
|
||
type EchoInputCreationMethod func(c echo.Context) (map[string]interface{}, error) | ||
|
||
type EchoMiddleware struct { | ||
Config *config.Config | ||
InputCreationMethod EchoInputCreationMethod `json:"binding_method,omitempty"` | ||
} | ||
|
||
func NewEchoMiddleware(cfg *config.Config, input EchoInputCreationMethod) (*EchoMiddleware, error) { | ||
err := cfg.Validate() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if input == nil { | ||
if cfg.InputCreationMethod == nil { | ||
return nil, errors.New("[opa-middleware-echo] InputCreationMethod must be provided") | ||
} | ||
input = func(c echo.Context) (map[string]interface{}, error) { | ||
bind, err := cfg.InputCreationMethod(c.Request()) | ||
if err != nil { | ||
return nil, err | ||
} | ||
return bind, nil | ||
} | ||
} | ||
return &EchoMiddleware{ | ||
Config: cfg, | ||
InputCreationMethod: input, | ||
}, nil | ||
} | ||
|
||
func (e *EchoMiddleware) Use() echo.MiddlewareFunc { | ||
return func(next echo.HandlerFunc) echo.HandlerFunc { | ||
return func(c echo.Context) error { | ||
if e.Config.Debug { | ||
e.Config.Logger.Printf("[opa-middleware-echo] Request received") | ||
} | ||
result, err := e.query(c) | ||
if err != nil { | ||
if e.Config.Debug { | ||
e.Config.Logger.Printf("[opa-middleware-echo] Error: %s", err.Error()) | ||
} | ||
return c.JSON(http.StatusInternalServerError, map[string]interface{}{"error": err.Error()}) | ||
} | ||
if e.Config.Debug { | ||
e.Config.Logger.Printf("[opa-middleware-echo] Result: %t", result) | ||
} | ||
if result != e.Config.ExceptedResult { | ||
return c.JSON(e.Config.DeniedStatusCode, map[string]interface{}{"error": e.Config.DeniedMessage}) | ||
} | ||
return next(c) | ||
} | ||
} | ||
} | ||
|
||
func (e *EchoMiddleware) query(c echo.Context) (bool, error) { | ||
bind, err := e.InputCreationMethod(c) | ||
if err != nil { | ||
return !e.Config.ExceptedResult, err | ||
} | ||
if e.Config.URL != "" { | ||
input := make(map[string]interface{}) | ||
input["input"] = bind | ||
return internal.QueryURL(c.Request(), e.Config, input) | ||
} | ||
return internal.QueryPolicy(c.Request(), e.Config, bind) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,180 @@ | ||
package opamiddleware | ||
|
||
import ( | ||
"github.com/Joffref/opa-middleware/config" | ||
"github.com/labstack/echo/v4" | ||
"net/http" | ||
"net/http/httptest" | ||
"net/url" | ||
"testing" | ||
) | ||
|
||
var Test_Policy = ` | ||
package policy | ||
default allow = false | ||
allow { | ||
input.path = "/api/v1/users" | ||
input.method = "GET" | ||
}` | ||
|
||
func TestEchoMiddleware_Query(t *testing.T) { | ||
type fields struct { | ||
Config *config.Config | ||
InputCreationMethod EchoInputCreationMethod | ||
} | ||
type args struct { | ||
req *http.Request | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
args args | ||
want bool | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Test EchoMiddleware_Query", | ||
fields: fields{ | ||
Config: &config.Config{ | ||
Policy: Test_Policy, | ||
Query: "data.policy.allow", | ||
ExceptedResult: true, | ||
DeniedStatusCode: 403, | ||
DeniedMessage: "Forbidden", | ||
}, | ||
InputCreationMethod: func(c echo.Context) (map[string]interface{}, error) { | ||
return map[string]interface{}{ | ||
"path": c.Request().URL.Path, | ||
"method": c.Request().Method, | ||
}, nil | ||
}, | ||
}, | ||
args: args{ | ||
req: &http.Request{ | ||
URL: &url.URL{ | ||
Path: "/api/v1/users", | ||
}, | ||
Method: "GET", | ||
}, | ||
}, | ||
want: true, | ||
wantErr: false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
e := echo.New() | ||
h := &EchoMiddleware{ | ||
Config: tt.fields.Config, | ||
InputCreationMethod: tt.fields.InputCreationMethod, | ||
} | ||
c := e.NewContext(tt.args.req, httptest.NewRecorder()) | ||
got, err := h.query(c) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("Query() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
if got != tt.want { | ||
t.Errorf("Query() got = %v, want %v", got, tt.want) | ||
} | ||
}) | ||
} | ||
} | ||
|
||
func TestEchoMiddleware_Use(t *testing.T) { | ||
type fields struct { | ||
Config *config.Config | ||
InputCreationMethod EchoInputCreationMethod | ||
} | ||
tests := []struct { | ||
name string | ||
fields fields | ||
}{ | ||
{ | ||
name: "Test EchoMiddleware_Use", | ||
fields: fields{ | ||
Config: &config.Config{ | ||
Policy: Test_Policy, | ||
Query: "data.policy.allow", | ||
ExceptedResult: true, | ||
DeniedStatusCode: 403, | ||
DeniedMessage: "Forbidden", | ||
}, | ||
InputCreationMethod: func(c echo.Context) (map[string]interface{}, error) { | ||
return map[string]interface{}{ | ||
"path": c.Request().URL.Path, | ||
"method": c.Request().Method, | ||
}, nil | ||
}, | ||
}, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
h := &EchoMiddleware{ | ||
Config: tt.fields.Config, | ||
InputCreationMethod: tt.fields.InputCreationMethod, | ||
} | ||
h.Use() | ||
}) | ||
} | ||
} | ||
|
||
func TestNewEchoMiddleware(t *testing.T) { | ||
type args struct { | ||
cfg *config.Config | ||
inputCreationMethod EchoInputCreationMethod | ||
} | ||
tests := []struct { | ||
name string | ||
args args | ||
want *EchoMiddleware | ||
wantErr bool | ||
}{ | ||
{ | ||
name: "Test NewEchoMiddleware", | ||
args: args{ | ||
cfg: &config.Config{ | ||
Policy: "policy", | ||
Query: "data.query", | ||
ExceptedResult: true, | ||
DeniedStatusCode: 403, | ||
DeniedMessage: "Forbidden", | ||
}, | ||
inputCreationMethod: func(c echo.Context) (map[string]interface{}, error) { | ||
return map[string]interface{}{ | ||
"path": c.Request().URL.Path, | ||
"method": c.Request().Method, | ||
}, nil | ||
}, | ||
}, | ||
want: &EchoMiddleware{ | ||
Config: &config.Config{ | ||
Policy: "policy", | ||
Query: "data.query", | ||
ExceptedResult: true, | ||
DeniedStatusCode: 403, | ||
DeniedMessage: "Forbidden", | ||
}, | ||
InputCreationMethod: func(c echo.Context) (map[string]interface{}, error) { | ||
return map[string]interface{}{ | ||
"path": c.Request().URL.Path, | ||
"method": c.Request().Method, | ||
}, nil | ||
}, | ||
}, | ||
wantErr: false, | ||
}, | ||
} | ||
for _, tt := range tests { | ||
t.Run(tt.name, func(t *testing.T) { | ||
_, err := NewEchoMiddleware(tt.args.cfg, tt.args.inputCreationMethod) | ||
if (err != nil) != tt.wantErr { | ||
t.Errorf("NewEchoMiddleware() error = %v, wantErr %v", err, tt.wantErr) | ||
return | ||
} | ||
}) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.