Basic usage of casbin access control framework in GO

Posted by Pigmaster on Sat, 27 Jul 2019 05:11:25 +0200

My understanding of casbin

This paper takes the iris framework as an example, and the native or other framework is basically the same.

according to Official Documents First of all, introduce several important parts.(Installation method skip)

There are two places to configure using casbin, one is the model and the other is the policy

RBAC (Role-based Rights Control) is the one that we use a lot for rights control.

Students who have read the official documents know that the content source of the model can be a.conf file or written in code; however, the policy content should be dynamic and can be updated at any time. How inconvenient is it to manage it in a.csv file?Don't fret, I'll talk about my solution later.

Simple test

See what others do first

package main

import (
    "github.com/kataras/iris"

    "github.com/casbin/casbin"
    cm "github.com/iris-contrib/middleware/casbin"
)

var Enforcer = casbin.NewEnforcer("casbin_model.conf", "casbi_npolicy.csv")

func newApp() *iris.Application {
    casbinMiddleware := cm.New(Enforcer)
    app := iris.New()
    app.WrapRouter(casbinMiddleware.Wrapper())
    app.Get("/", hi)
    app.Any("/dataset1/{p:path}", hi)
    app.Post("/dataset1/resource1", hi)
    app.Get("/dataset2/resource2", hi)
    app.Post("/dataset2/folder1/{p:path}", hi)
    app.Any("/dataset2/resource1", hi)

    return app
}

func main() {
    app := newApp()
    app.Run(iris.Addr(":8080"))
}

func hi(ctx iris.Context) {
    ctx.Writef("Hello %s", cm.Username(ctx.Request()))
}

Above is a sample code from the iris document, which may have a different version. I run the following code and it will directly error because of a method in github.com/iris-contrib/middleware/casbin/casbin.go:

func (c *Casbin) Check(r *http.Request) bool {
    username := Username(r)
    method := r.Method
    path := r.URL.Path
    b:=c.enforcer.Enforce(username, path, method)//c.enforcer.Enforce returns two values here, bool and error. There is only one defined here, so the error is reported.
    return b
}

Another problem with this middleware is that it uses BasicAuth by default to get the user name. If you do the same, it will be great in Italy. If not, you can do it yourself.

Since it's a simple test, it's simple and rude to rewrite the entire casbin.go file directly

Start working

/rbac_model.conf

//RBAC1

//Request Definition
//Subuser who wants to access the resource
//Resources to be accessed by obj
//act user's actions on resources, acts can be read, write, print, etc. you want to customize
[request_definition]
r = sub, obj, act

//Policy definition, that is, the format of the p definition of the *.cvs file
[policy_definition]
p = sub, obj, act

//Group definitions, that is, the format defined by the *.cvs file g.G is a user group or role
[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 == "*")

/rbac_policy.csv (comment deleted)

p,abc123,/user,GET //User abc123 has GET privileges on/user, the same below
p,abc123,/user,POST
p,admin,/test,* //Role or user group admin has all permissions on/test
g,super_admin,admin //User super_admin belongs to Admin Group or role

Copy the entire casbin.go file under my project, and I put it in / middleware /

/middleware/casbin.go

package middleware

import (
    "github.com/casbin/casbin"
    "github.com/kataras/iris/context"
    "net/http"
)

func New(e *casbin.Enforcer) *Casbin {
    return &Casbin{enforcer: e}
}

func (c *Casbin) Wrapper() func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
    return func(w http.ResponseWriter, r *http.Request, router http.HandlerFunc) {
        if !c.Check(r) {
            w.WriteHeader(http.StatusForbidden)
            _, _ = w.Write([]byte("403 Forbidden"))
            return
        }
        router(w, r)
    }
}

func (c *Casbin) ServeHTTP(ctx context.Context) {
    if !c.Check(ctx.Request()) {
        ctx.StatusCode(http.StatusForbidden) // Status Forbiden
        ctx.StopExecution()
        return
    }
    ctx.Next()
}

type Casbin struct {
    enforcer *casbin.Enforcer
}

func (c *Casbin) Check(r *http.Request) bool {
    username := Username(r)
    method := r.Method
    path := r.URL.Path
    b,_:=c.enforcer.Enforce(username, path, method)
    return b
}

func Username(r *http.Request) string {
    //Username,,: = r.BasicAuth()//I can't use this, comment it out
    return "abc123" //Return to the user name directly to see the test results
}

main.go

package main

import (
    "github.com/kataras/iris"

    "github.com/casbin/casbin"
    cm "project_path/middleware/casbin"
)

var e = casbin.NewEnforcer("casbin_model.conf", "casbi_npolicy.csv")

func newApp() *iris.Application {
    casbinMiddleware := cm.New(e)
    app := iris.New()
    app.WrapRouter(casbinMiddleware.Wrapper())
    // If you don't want to use middleware, you can make a decision by following these methods
    /*
    if b,err:=e.Enforce("abc123","/user","Get");b {
        fmt.Println(""Success")
    } else {
        fmt.Println("Failure ")
    }
    */
    app.Get("/user", hi)
    app.Post("/user", hi)
    app.Put("/test", hi)

    return app
}

func main() {
    app := newApp()
    app.Run(iris.Addr(":8080"))
}

func hi(ctx iris.Context) {
    ctx.Writef("When you see this, you've passed the privilege validation")
}

Okay, that's it. Use it below
Get access to localhost:8080/user
Post accesses localhost:8080/user
Both of the above visits were successfully verified, and the following
Get, Post, Put, Delete, etc. Access localhost:8080/test
All returned 403 Forbidden, try again when you change the Username method to return "super_admin".

In a real-world project, it may be possible to change the username in the Check method to either a user ID or a mailbox or a mobile number.

policy to be continued...

Topics: Go github Mobile