From fdc9c9a0395a6abc42df8d9aadc1071f0a40ad2c Mon Sep 17 00:00:00 2001 From: Luis Albarenga Date: Thu, 28 Dec 2023 11:32:00 -0300 Subject: [PATCH] feat: improve casbin mw (#107) * feat: improve test readability and include README.md --------- Co-authored-by: Luis Albarenga --- casbin/README.md | 46 ++++++++++++++++++++++ casbin/casbin_test.go | 90 +++++++++++++++++++++++++------------------ 2 files changed, 98 insertions(+), 38 deletions(-) create mode 100644 casbin/README.md diff --git a/casbin/README.md b/casbin/README.md new file mode 100644 index 0000000..6383389 --- /dev/null +++ b/casbin/README.md @@ -0,0 +1,46 @@ +# Usage +Simple example: +```go + package main + + import ( + "github.com/casbin/casbin/v2" + "github.com/labstack/echo/v4" + casbin_mw "github.com/labstack/echo-contrib/casbin" + ) + + func main() { + e := echo.New() + + // Mediate the access for every request + e.Use(casbin_mw.Middleware(casbin.NewEnforcer("auth_model.conf", "auth_policy.csv"))) + + e.Logger.Fatal(e.Start(":1323")) + } +``` + +Advanced example: +```go + package main + + import ( + "github.com/casbin/casbin/v2" + "github.com/labstack/echo/v4" + casbin_mw "github.com/labstack/echo-contrib/casbin" + ) + + func main() { + ce, _ := casbin.NewEnforcer("auth_model.conf", "") + ce.AddRoleForUser("alice", "admin") + ce.AddPolicy("added_user", "data1", "read") + + e := echo.New() + + e.Use(casbin_mw.Middleware(ce)) + + e.Logger.Fatal(e.Start(":1323")) + } +``` + +# API Reference +See [API Overview](https://casbin.org/docs/api-overview). \ No newline at end of file diff --git a/casbin/casbin_test.go b/casbin/casbin_test.go index 5d4c8fb..69227be 100644 --- a/casbin/casbin_test.go +++ b/casbin/casbin_test.go @@ -26,12 +26,11 @@ func testRequest(t *testing.T, h echo.HandlerFunc, user string, path string, met err := h(c) if err != nil { - if errObj, ok := err.(*echo.HTTPError); ok { + var errObj *echo.HTTPError + if errors.As(err, &errObj) { if errObj.Code != code { t.Errorf("%s, %s, %s: %d, supposed to be %d", user, path, method, errObj.Code, code) } - } else { - t.Error(err) } } else { if c.Response().Status != code { @@ -46,10 +45,10 @@ func TestAuth(t *testing.T) { return c.String(http.StatusOK, "test") }) - testRequest(t, h, "alice", "/dataset1/resource1", echo.GET, 200) - testRequest(t, h, "alice", "/dataset1/resource1", echo.POST, 200) - testRequest(t, h, "alice", "/dataset1/resource2", echo.GET, 200) - testRequest(t, h, "alice", "/dataset1/resource2", echo.POST, 403) + testRequest(t, h, "alice", "/dataset1/resource1", echo.GET, http.StatusOK) + testRequest(t, h, "alice", "/dataset1/resource1", echo.POST, http.StatusOK) + testRequest(t, h, "alice", "/dataset1/resource2", echo.GET, http.StatusOK) + testRequest(t, h, "alice", "/dataset1/resource2", echo.POST, http.StatusForbidden) } func TestPathWildcard(t *testing.T) { @@ -58,19 +57,19 @@ func TestPathWildcard(t *testing.T) { return c.String(http.StatusOK, "test") }) - testRequest(t, h, "bob", "/dataset2/resource1", "GET", 200) - testRequest(t, h, "bob", "/dataset2/resource1", "POST", 200) - testRequest(t, h, "bob", "/dataset2/resource1", "DELETE", 200) - testRequest(t, h, "bob", "/dataset2/resource2", "GET", 200) - testRequest(t, h, "bob", "/dataset2/resource2", "POST", 403) - testRequest(t, h, "bob", "/dataset2/resource2", "DELETE", 403) - - testRequest(t, h, "bob", "/dataset2/folder1/item1", "GET", 403) - testRequest(t, h, "bob", "/dataset2/folder1/item1", "POST", 200) - testRequest(t, h, "bob", "/dataset2/folder1/item1", "DELETE", 403) - testRequest(t, h, "bob", "/dataset2/folder1/item2", "GET", 403) - testRequest(t, h, "bob", "/dataset2/folder1/item2", "POST", 200) - testRequest(t, h, "bob", "/dataset2/folder1/item2", "DELETE", 403) + testRequest(t, h, "bob", "/dataset2/resource1", echo.GET, http.StatusOK) + testRequest(t, h, "bob", "/dataset2/resource1", echo.POST, http.StatusOK) + testRequest(t, h, "bob", "/dataset2/resource1", echo.DELETE, http.StatusOK) + testRequest(t, h, "bob", "/dataset2/resource2", echo.GET, http.StatusOK) + testRequest(t, h, "bob", "/dataset2/resource2", echo.POST, http.StatusForbidden) + testRequest(t, h, "bob", "/dataset2/resource2", echo.DELETE, http.StatusForbidden) + + testRequest(t, h, "bob", "/dataset2/folder1/item1", echo.GET, http.StatusForbidden) + testRequest(t, h, "bob", "/dataset2/folder1/item1", echo.POST, http.StatusOK) + testRequest(t, h, "bob", "/dataset2/folder1/item1", echo.DELETE, http.StatusForbidden) + testRequest(t, h, "bob", "/dataset2/folder1/item2", echo.GET, http.StatusForbidden) + testRequest(t, h, "bob", "/dataset2/folder1/item2", echo.POST, http.StatusOK) + testRequest(t, h, "bob", "/dataset2/folder1/item2", echo.DELETE, http.StatusForbidden) } func TestRBAC(t *testing.T) { @@ -80,22 +79,22 @@ func TestRBAC(t *testing.T) { }) // cathy can access all /dataset1/* resources via all methods because it has the dataset1_admin role. - testRequest(t, h, "cathy", "/dataset1/item", "GET", 200) - testRequest(t, h, "cathy", "/dataset1/item", "POST", 200) - testRequest(t, h, "cathy", "/dataset1/item", "DELETE", 200) - testRequest(t, h, "cathy", "/dataset2/item", "GET", 403) - testRequest(t, h, "cathy", "/dataset2/item", "POST", 403) - testRequest(t, h, "cathy", "/dataset2/item", "DELETE", 403) + testRequest(t, h, "cathy", "/dataset1/item", echo.GET, http.StatusOK) + testRequest(t, h, "cathy", "/dataset1/item", echo.POST, http.StatusOK) + testRequest(t, h, "cathy", "/dataset1/item", echo.DELETE, http.StatusOK) + testRequest(t, h, "cathy", "/dataset2/item", echo.GET, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset2/item", echo.POST, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset2/item", echo.DELETE, http.StatusForbidden) // delete all roles on user cathy, so cathy cannot access any resources now. ce.DeleteRolesForUser("cathy") - testRequest(t, h, "cathy", "/dataset1/item", "GET", 403) - testRequest(t, h, "cathy", "/dataset1/item", "POST", 403) - testRequest(t, h, "cathy", "/dataset1/item", "DELETE", 403) - testRequest(t, h, "cathy", "/dataset2/item", "GET", 403) - testRequest(t, h, "cathy", "/dataset2/item", "POST", 403) - testRequest(t, h, "cathy", "/dataset2/item", "DELETE", 403) + testRequest(t, h, "cathy", "/dataset1/item", echo.GET, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset1/item", echo.POST, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset1/item", echo.DELETE, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset2/item", echo.GET, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset2/item", echo.POST, http.StatusForbidden) + testRequest(t, h, "cathy", "/dataset2/item", echo.DELETE, http.StatusForbidden) } func TestEnforceError(t *testing.T) { @@ -104,7 +103,7 @@ func TestEnforceError(t *testing.T) { return c.String(http.StatusOK, "test") }) - testRequest(t, h, "cathy", "/dataset1/item", "GET", 500) + testRequest(t, h, "cathy", "/dataset1/item", echo.GET, http.StatusInternalServerError) } func TestCustomUserGetter(t *testing.T) { @@ -119,7 +118,7 @@ func TestCustomUserGetter(t *testing.T) { h := MiddlewareWithConfig(cnf)(func(c echo.Context) error { return c.String(http.StatusOK, "test") }) - testRequest(t, h, "cathy", "/dataset1/item", "GET", 403) + testRequest(t, h, "cathy", "/dataset1/item", echo.GET, http.StatusForbidden) } func TestUserGetterError(t *testing.T) { @@ -134,7 +133,7 @@ func TestUserGetterError(t *testing.T) { h := MiddlewareWithConfig(cnf)(func(c echo.Context) error { return c.String(http.StatusOK, "test") }) - testRequest(t, h, "cathy", "/dataset1/item", "GET", 403) + testRequest(t, h, "cathy", "/dataset1/item", echo.GET, http.StatusForbidden) } func TestCustomEnforceHandler(t *testing.T) { @@ -156,7 +155,22 @@ func TestCustomEnforceHandler(t *testing.T) { h := MiddlewareWithConfig(cnf)(func(c echo.Context) error { return c.String(http.StatusOK, "test") }) - testRequest(t, h, "bob", "/dataset2/resource1", "GET", http.StatusOK) - testRequest(t, h, "bob", "/user/alice", "PATCH", http.StatusForbidden) - testRequest(t, h, "bob", "/user/bob", "PATCH", http.StatusOK) + testRequest(t, h, "bob", "/dataset2/resource1", echo.GET, http.StatusOK) + testRequest(t, h, "bob", "/user/alice", echo.PATCH, http.StatusForbidden) + testRequest(t, h, "bob", "/user/bob", echo.PATCH, http.StatusOK) +} + +func TestCustomSkipper(t *testing.T) { + ce, _ := casbin.NewEnforcer("auth_model.conf", "auth_policy.csv") + cnf := Config{ + Skipper: func(c echo.Context) bool { + return c.Request().URL.Path == "/dataset1/resource1" + }, + Enforcer: ce, + } + h := MiddlewareWithConfig(cnf)(func(c echo.Context) error { + return c.String(http.StatusOK, "test") + }) + testRequest(t, h, "alice", "/dataset1/resource1", echo.GET, http.StatusOK) + testRequest(t, h, "alice", "/dataset1/resource2", echo.POST, http.StatusForbidden) }