Skip to content

Commit

Permalink
feat: implement access control using casbin (beego#806)
Browse files Browse the repository at this point in the history
* feat: implement access control using casbin

Signed-off-by: Yixiang Zhao <[email protected]>

* chore: sort imports

Signed-off-by: Yixiang Zhao <[email protected]>

* fix: remove

Signed-off-by: Yixiang Zhao <[email protected]>

* Update auth.go

Co-authored-by: Gucheng <[email protected]>
  • Loading branch information
seriouszyx and nomeguy authored Jul 12, 2022
1 parent de49a45 commit 2bca424
Show file tree
Hide file tree
Showing 5 changed files with 159 additions and 1 deletion.
11 changes: 11 additions & 0 deletions controllers/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,17 @@ func tokenToResponse(token *object.Token) *Response {
// HandleLoggedIn ...
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
userId := user.GetId()

allowed, err := object.CheckPermission(userId, application)
if err != nil {
c.ResponseError(err.Error(), nil)
return
}
if !allowed {
c.ResponseError("Unauthorized operation")
return
}

if form.Type == ResponseTypeLogin {
c.SetSessionUsername(userId)
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
Expand Down
5 changes: 5 additions & 0 deletions object/adapter.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,11 @@ func (a *Adapter) createTable() {
if err != nil {
panic(err)
}

err = a.Engine.Sync2(new(PermissionRule))
if err != nil {
panic(err)
}
}

func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
Expand Down
17 changes: 17 additions & 0 deletions object/check.go
Original file line number Diff line number Diff line change
Expand Up @@ -229,4 +229,21 @@ func CheckUserPermission(requestUserId, userId string, strict bool) (bool, error
}

return hasPermission, fmt.Errorf("you don't have the permission to do this")
}

func CheckPermission(userId string, application *Application) (bool, error) {
permissions := GetPermissions(application.Organization)
allow := true
var err error
for _, permission := range permissions {
if permission.IsEnabled {
for _, resource := range permission.Resources {
if resource == application.Name {
enforcer := getEnforcer(permission)
allow, err = enforcer.Enforce(userId, application.Name, "read")
}
}
}
}
return allow, err
}
103 changes: 102 additions & 1 deletion object/permission.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,12 @@ package object

import (
"fmt"
"strings"

"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/model"
xormadapter "github.com/casbin/xorm-adapter/v2"
"github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/util"
"xorm.io/core"
)
Expand All @@ -39,6 +44,16 @@ type Permission struct {
IsEnabled bool `json:"isEnabled"`
}

type PermissionRule struct {
PType string `xorm:"varchar(100) index not null default ''"`
V0 string `xorm:"varchar(100) index not null default ''"`
V1 string `xorm:"varchar(100) index not null default ''"`
V2 string `xorm:"varchar(100) index not null default ''"`
V3 string `xorm:"varchar(100) index not null default ''"`
V4 string `xorm:"varchar(100) index not null default ''"`
V5 string `xorm:"varchar(100) index not null default ''"`
}

func GetPermissionCount(owner, field, value string) int {
session := GetSession(owner, -1, -1, field, value, "", "")
count, err := session.Count(&Permission{})
Expand Down Expand Up @@ -95,7 +110,8 @@ func GetPermission(id string) *Permission {

func UpdatePermission(id string, permission *Permission) bool {
owner, name := util.GetOwnerAndNameFromId(id)
if getPermission(owner, name) == nil {
oldPermission := getPermission(owner, name)
if oldPermission == nil {
return false
}

Expand All @@ -104,6 +120,11 @@ func UpdatePermission(id string, permission *Permission) bool {
panic(err)
}

if affected != 0 {
removePolicies(oldPermission)
addPolicies(permission)
}

return affected != 0
}

Expand All @@ -113,6 +134,10 @@ func AddPermission(permission *Permission) bool {
panic(err)
}

if affected != 0 {
addPolicies(permission)
}

return affected != 0
}

Expand All @@ -122,9 +147,85 @@ func DeletePermission(permission *Permission) bool {
panic(err)
}

if affected != 0 {
removePolicies(permission)
}

return affected != 0
}

func (permission *Permission) GetId() string {
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
}

func getEnforcer(permission *Permission) *casbin.Enforcer {
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
adapter, err := xormadapter.NewAdapterWithTableName(conf.GetConfigString("driverName"), conf.GetBeegoConfDataSourceName()+conf.GetConfigString("dbName"), "permission_rule", tableNamePrefix, true)
if err != nil {
panic(err)
}

modelText := `
[request_definition]
r = sub, obj, act
[policy_definition]
p = permission, sub, obj, act
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act`
permissionModel := getModel(permission.Owner, permission.Model)
if permissionModel != nil {
modelText = permissionModel.ModelText
}
m, err := model.NewModelFromString(modelText)
if err != nil {
panic(err)
}

enforcer, err := casbin.NewEnforcer(m, adapter)
if err != nil {
panic(err)
}

err = enforcer.LoadFilteredPolicy(xormadapter.Filter{V0: []string{permission.GetId()}})
if err != nil {
panic(err)
}

return enforcer
}

func getPolicies(permission *Permission) [][]string {
var policies [][]string
for _, user := range permission.Users {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
policies = append(policies, []string{permission.GetId(), user, resource, strings.ToLower(action)})
}
}
}
return policies
}

func addPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies := getPolicies(permission)

_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
}

func removePolicies(permission *Permission) {
enforcer := getEnforcer(permission)

_, err := enforcer.RemoveFilteredPolicy(0, permission.GetId())
if err != nil {
panic(err)
}
}
24 changes: 24 additions & 0 deletions web/src/PermissionEditPage.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import * as Setting from "./Setting";
import i18next from "i18next";
import * as RoleBackend from "./backend/RoleBackend";
import * as ModelBackend from "./backend/ModelBackend";
import * as ApplicationBackend from "./backend/ApplicationBackend";

const {Option} = Select;

Expand All @@ -36,6 +37,7 @@ class PermissionEditPage extends React.Component {
users: [],
roles: [],
models: [],
resources: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit",
};
}
Expand All @@ -55,6 +57,7 @@ class PermissionEditPage extends React.Component {
this.getUsers(permission.owner);
this.getRoles(permission.owner);
this.getModels(permission.owner);
this.getResources(permission.owner);
});
}

Expand Down Expand Up @@ -94,6 +97,15 @@ class PermissionEditPage extends React.Component {
});
}

getResources(organizationName) {
ApplicationBackend.getApplicationsByOrganization("admin", organizationName)
.then((res) => {
this.setState({
resources: (res.msg === undefined) ? res : [],
});
});
}

parsePermissionField(key, value) {
if ([""].includes(key)) {
value = Setting.myParseInt(value);
Expand Down Expand Up @@ -212,6 +224,18 @@ class PermissionEditPage extends React.Component {
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("permission:Resources"), i18next.t("permission:Resources - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} mode="tags" style={{width: "100%"}} value={this.state.permission.resources} onChange={(value => {this.updatePermissionField("resources", value);})}>
{
this.state.resources.map((resource, index) => <Option key={index} value={`${resource.name}`}>{`${resource.name}`}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("permission:Actions"), i18next.t("permission:Actions - Tooltip"))} :
Expand Down

0 comments on commit 2bca424

Please sign in to comment.