1
0
mirror of https://github.com/labstack/echo.git synced 2024-12-24 20:14:31 +02:00

Moved casbin middlware to echo-contrib

Signed-off-by: Vishal Rana <vr@labstack.com>
This commit is contained in:
Vishal Rana 2017-06-02 10:19:56 -07:00
parent c73dd1f54c
commit b6a9836120
6 changed files with 41 additions and 226 deletions

14
Gopkg.lock generated
View File

@ -1,4 +1,5 @@
memo = "df64dab23d007c5d87035a578790e1dc311b8cd82446433a778bb3274807def7"
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
name = "github.com/davecgh/go-spew"
@ -58,10 +59,17 @@ memo = "df64dab23d007c5d87035a578790e1dc311b8cd82446433a778bb3274807def7"
branch = "master"
name = "golang.org/x/crypto"
packages = ["acme","acme/autocert"]
revision = "122d919ec1efcfb58483215da23f815853e24b81"
revision = "e1a4589e7d3ea14a3352255d04b6f1a418845e5e"
[[projects]]
branch = "master"
name = "golang.org/x/sys"
packages = ["unix"]
revision = "9ccfe848b9db8435a24c424abbc07a921adf1df5"
revision = "b90f89a1e7a9c1f6b918820b3daa7f08488c8594"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
inputs-digest = "5f74a2a2ba5b07475ad0faa1b4c021b973ad40b2ae749e3d94e15fe839bb440e"
solver-name = "gps-cdcl"
solver-version = 1

View File

@ -1,6 +1,13 @@
## Gopkg.toml example (these lines may be deleted)
## "metadata" defines metadata about the project that could be used by other independent
## systems. The metadata defined here will be ignored by dep.
# [metadata]
# key1 = "value that convey data to other systems"
# system1-data = "value that is used by a system"
# system2-data = "value that is used by another system"
## "required" lists a set of packages (not projects) that must be included in
## Gopkg.lock. This list is merged with the set of packages imported by the current
## project. Use it when your project needs a package it doesn't explicitly import -
@ -12,9 +19,10 @@
## or in a dependency.
# ignored = ["github.com/user/project/badpkg"]
## Dependencies define constraints on dependent projects. They are respected by
## Constraints are rules for how directly imported projects
## may be incorporated into the depgraph. They are respected by
## dep whether coming from the Gopkg.toml of the current project or a dependency.
# [[dependencies]]
# [[constraint]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
@ -26,18 +34,25 @@
#
## Optional: an alternate location (URL or import path) for the project's source.
# source = "https://github.com/myfork/package.git"
#
## "metadata" defines metadata about the dependency or override that could be used
## by other independent systems. The metadata defined here will be ignored by dep.
# [metadata]
# key1 = "value that convey data to other systems"
# system1-data = "value that is used by a system"
# system2-data = "value that is used by another system"
## Overrides have the same structure as [[dependencies]], but supercede all
## [[dependencies]] declarations from all projects. Only the current project's
## [[overrides]] are applied.
## Overrides have the same structure as [[constraint]], but supersede all
## [[constraint]] declarations from all projects. Only [[override]] from
## the current project's are applied.
##
## Overrides are a sledgehammer. Use them only as a last resort.
# [[overrides]]
# [[override]]
## Required: the root import path of the project being constrained.
# name = "github.com/user/project"
#
## Optional: specifying a version constraint override will cause all other
## constraints on this project to be ignored; only the overriden constraint
## constraints on this project to be ignored; only the overridden constraint
## need be satisfied.
## Again, only one of "branch", "version" or "revision" can be specified.
# version = "1.0.0"
@ -51,22 +66,22 @@
[[dependencies]]
[[constraint]]
name = "github.com/dgrijalva/jwt-go"
version = "^3.0.0"
version = "3.0.0"
[[dependencies]]
[[constraint]]
name = "github.com/labstack/gommon"
version = ">=0.2.1, <1.0.0"
version = "0.2.1"
[[dependencies]]
[[constraint]]
name = "github.com/stretchr/testify"
version = "^1.1.4"
version = "1.1.4"
[[dependencies]]
[[constraint]]
branch = "master"
name = "github.com/valyala/fasttemplate"
[[dependencies]]
[[constraint]]
branch = "master"
name = "golang.org/x/crypto"

View File

@ -1,101 +0,0 @@
// Package middleware CasbinAuth provides handlers to enable ACL, RBAC, ABAC authorization support.
// Simple Usage:
// package main
//
// import (
// "github.com/casbin/casbin"
// "github.com/labstack/echo"
// "github.com/labstack/echo/middleware"
// )
//
// func main() {
// e := echo.New()
//
// // mediate the access for every request
// e.Use(middleware.CasbinAuth(casbin.NewEnforcer("casbin_auth_model.conf", "casbin_auth_policy.csv")))
//
// e.Logger.Fatal(e.Start(":1323"))
// }
//
// Advanced Usage:
//
// func main(){
// ce := casbin.NewEnforcer("casbin_auth_model.conf", "")
// ce.AddRoleForUser("alice", "admin")
// ce.AddPolicy(...)
//
// e := echo.New()
//
// echo.Use(middleware.CasbinAuth(ce))
//
// e.Logger.Fatal(e.Start(":1323"))
// }
package middleware
import (
"github.com/casbin/casbin"
"github.com/labstack/echo"
)
type (
// CasbinAuthConfig defines the config for CasbinAuth middleware.
CasbinAuthConfig struct {
// Skipper defines a function to skip middleware.
Skipper Skipper
// Enforcer CasbinAuth main rule.
// Required.
Enforcer *casbin.Enforcer
}
)
var (
// DefaultCasbinAuthConfig is the default CasbinAuth middleware config.
DefaultCasbinAuthConfig = CasbinAuthConfig{
Skipper: DefaultSkipper,
}
)
// CasbinAuth returns an CasbinAuth middleware.
//
// For valid credentials it calls the next handler.
// For missing or invalid credentials, it sends "401 - Unauthorized" response.
func CasbinAuth(ce *casbin.Enforcer) echo.MiddlewareFunc {
c := DefaultCasbinAuthConfig
c.Enforcer = ce
return CasbinAuthWithConfig(c)
}
// CasbinAuthWithConfig returns an CasbinAuth middleware with config.
// See `CasbinAuth()`.
func CasbinAuthWithConfig(config CasbinAuthConfig) echo.MiddlewareFunc {
// Defaults
if config.Skipper == nil {
config.Skipper = DefaultCasbinAuthConfig.Skipper
}
return func(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
if config.Skipper(c) || config.CheckPermission(c) {
return next(c)
}
return echo.ErrForbidden
}
}
}
// GetUserName gets the user name from the request.
// Currently, only HTTP basic authentication is supported
func (a *CasbinAuthConfig) GetUserName(c echo.Context) string {
username, _, _ := c.Request().BasicAuth()
return username
}
// CheckPermission checks the user/method/path combination from the request.
// Returns true (permission granted) or false (permission forbidden)
func (a *CasbinAuthConfig) CheckPermission(c echo.Context) bool {
user := a.GetUserName(c)
method := c.Request().Method
path := c.Request().URL.Path
return a.Enforcer.Enforce(user, path, method)
}

View File

@ -1,14 +0,0 @@
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && keyMatch(r.obj, p.obj) && (r.act == p.act || p.act == "*")

View File

@ -1,7 +0,0 @@
p, alice, /dataset1/*, GET
p, alice, /dataset1/resource1, POST
p, bob, /dataset2/resource1, *
p, bob, /dataset2/resource2, GET
p, bob, /dataset2/folder1/*, POST
p, dataset1_admin, /dataset1/*, *
g, cathy, dataset1_admin
1 p, alice, /dataset1/*, GET
2 p, alice, /dataset1/resource1, POST
3 p, bob, /dataset2/resource1, *
4 p, bob, /dataset2/resource2, GET
5 p, bob, /dataset2/folder1/*, POST
6 p, dataset1_admin, /dataset1/*, *
7 g, cathy, dataset1_admin

View File

@ -1,86 +0,0 @@
package middleware
import (
"net/http"
"net/http/httptest"
"testing"
"github.com/casbin/casbin"
"github.com/labstack/echo"
)
func testRequest(t *testing.T, ce *casbin.Enforcer, user string, path string, method string, code int) {
e := echo.New()
req := httptest.NewRequest(method, path, nil)
req.SetBasicAuth(user, "secret")
res := httptest.NewRecorder()
c := e.NewContext(req, res)
h := CasbinAuth(ce)(func(c echo.Context) error {
return c.String(http.StatusOK, "test")
})
err := h(c)
if err != nil {
if errObj, ok := err.(*echo.HTTPError); ok {
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 {
t.Errorf("%s, %s, %s: %d, supposed to be %d", user, path, method, c.Response().Status, code)
}
}
}
func TestCasbinAuth(t *testing.T) {
ce := casbin.NewEnforcer("casbin_auth_model.conf", "casbin_auth_policy.csv")
testRequest(t, ce, "alice", "/dataset1/resource1", echo.GET, 200)
testRequest(t, ce, "alice", "/dataset1/resource1", echo.POST, 200)
testRequest(t, ce, "alice", "/dataset1/resource2", echo.GET, 200)
testRequest(t, ce, "alice", "/dataset1/resource2", echo.POST, 403)
}
func TestPathWildcard(t *testing.T) {
ce := casbin.NewEnforcer("casbin_auth_model.conf", "casbin_auth_policy.csv")
testRequest(t, ce, "bob", "/dataset2/resource1", "GET", 200)
testRequest(t, ce, "bob", "/dataset2/resource1", "POST", 200)
testRequest(t, ce, "bob", "/dataset2/resource1", "DELETE", 200)
testRequest(t, ce, "bob", "/dataset2/resource2", "GET", 200)
testRequest(t, ce, "bob", "/dataset2/resource2", "POST", 403)
testRequest(t, ce, "bob", "/dataset2/resource2", "DELETE", 403)
testRequest(t, ce, "bob", "/dataset2/folder1/item1", "GET", 403)
testRequest(t, ce, "bob", "/dataset2/folder1/item1", "POST", 200)
testRequest(t, ce, "bob", "/dataset2/folder1/item1", "DELETE", 403)
testRequest(t, ce, "bob", "/dataset2/folder1/item2", "GET", 403)
testRequest(t, ce, "bob", "/dataset2/folder1/item2", "POST", 200)
testRequest(t, ce, "bob", "/dataset2/folder1/item2", "DELETE", 403)
}
func TestRBAC(t *testing.T) {
ce := casbin.NewEnforcer("casbin_auth_model.conf", "casbin_auth_policy.csv")
// cathy can access all /dataset1/* resources via all methods because it has the dataset1_admin role.
testRequest(t, ce, "cathy", "/dataset1/item", "GET", 200)
testRequest(t, ce, "cathy", "/dataset1/item", "POST", 200)
testRequest(t, ce, "cathy", "/dataset1/item", "DELETE", 200)
testRequest(t, ce, "cathy", "/dataset2/item", "GET", 403)
testRequest(t, ce, "cathy", "/dataset2/item", "POST", 403)
testRequest(t, ce, "cathy", "/dataset2/item", "DELETE", 403)
// delete all roles on user cathy, so cathy cannot access any resources now.
ce.DeleteRolesForUser("cathy")
testRequest(t, ce, "cathy", "/dataset1/item", "GET", 403)
testRequest(t, ce, "cathy", "/dataset1/item", "POST", 403)
testRequest(t, ce, "cathy", "/dataset1/item", "DELETE", 403)
testRequest(t, ce, "cathy", "/dataset2/item", "GET", 403)
testRequest(t, ce, "cathy", "/dataset2/item", "POST", 403)
testRequest(t, ce, "cathy", "/dataset2/item", "DELETE", 403)
}