mirror of
https://github.com/casdoor/casdoor.git
synced 2025-09-09 12:52:57 +08:00
feat: implement access control using casbin (#806)
* feat: implement access control using casbin Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com> * chore: sort imports Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com> * fix: remove Signed-off-by: Yixiang Zhao <seriouszyx@foxmail.com> * Update auth.go Co-authored-by: Gucheng <85475922+nomeguy@users.noreply.github.com>
This commit is contained in:
@@ -50,6 +50,17 @@ func tokenToResponse(token *object.Token) *Response {
|
|||||||
// HandleLoggedIn ...
|
// HandleLoggedIn ...
|
||||||
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
|
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *RequestForm) (resp *Response) {
|
||||||
userId := user.GetId()
|
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 {
|
if form.Type == ResponseTypeLogin {
|
||||||
c.SetSessionUsername(userId)
|
c.SetSessionUsername(userId)
|
||||||
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
util.LogInfo(c.Ctx, "API: [%s] signed in", userId)
|
||||||
|
@@ -203,6 +203,11 @@ func (a *Adapter) createTable() {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
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 {
|
func GetSession(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
|
||||||
|
@@ -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")
|
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
|
||||||
}
|
}
|
@@ -16,7 +16,12 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"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"
|
"github.com/casdoor/casdoor/util"
|
||||||
"xorm.io/core"
|
"xorm.io/core"
|
||||||
)
|
)
|
||||||
@@ -39,6 +44,16 @@ type Permission struct {
|
|||||||
IsEnabled bool `json:"isEnabled"`
|
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 {
|
func GetPermissionCount(owner, field, value string) int {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
count, err := session.Count(&Permission{})
|
count, err := session.Count(&Permission{})
|
||||||
@@ -95,7 +110,8 @@ func GetPermission(id string) *Permission {
|
|||||||
|
|
||||||
func UpdatePermission(id string, permission *Permission) bool {
|
func UpdatePermission(id string, permission *Permission) bool {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
if getPermission(owner, name) == nil {
|
oldPermission := getPermission(owner, name)
|
||||||
|
if oldPermission == nil {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -104,6 +120,11 @@ func UpdatePermission(id string, permission *Permission) bool {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if affected != 0 {
|
||||||
|
removePolicies(oldPermission)
|
||||||
|
addPolicies(permission)
|
||||||
|
}
|
||||||
|
|
||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -113,6 +134,10 @@ func AddPermission(permission *Permission) bool {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if affected != 0 {
|
||||||
|
addPolicies(permission)
|
||||||
|
}
|
||||||
|
|
||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,9 +147,85 @@ func DeletePermission(permission *Permission) bool {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if affected != 0 {
|
||||||
|
removePolicies(permission)
|
||||||
|
}
|
||||||
|
|
||||||
return affected != 0
|
return affected != 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func (permission *Permission) GetId() string {
|
func (permission *Permission) GetId() string {
|
||||||
return fmt.Sprintf("%s/%s", permission.Owner, permission.Name)
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -21,6 +21,7 @@ import * as Setting from "./Setting";
|
|||||||
import i18next from "i18next";
|
import i18next from "i18next";
|
||||||
import * as RoleBackend from "./backend/RoleBackend";
|
import * as RoleBackend from "./backend/RoleBackend";
|
||||||
import * as ModelBackend from "./backend/ModelBackend";
|
import * as ModelBackend from "./backend/ModelBackend";
|
||||||
|
import * as ApplicationBackend from "./backend/ApplicationBackend";
|
||||||
|
|
||||||
const {Option} = Select;
|
const {Option} = Select;
|
||||||
|
|
||||||
@@ -36,6 +37,7 @@ class PermissionEditPage extends React.Component {
|
|||||||
users: [],
|
users: [],
|
||||||
roles: [],
|
roles: [],
|
||||||
models: [],
|
models: [],
|
||||||
|
resources: [],
|
||||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -55,6 +57,7 @@ class PermissionEditPage extends React.Component {
|
|||||||
this.getUsers(permission.owner);
|
this.getUsers(permission.owner);
|
||||||
this.getRoles(permission.owner);
|
this.getRoles(permission.owner);
|
||||||
this.getModels(permission.owner);
|
this.getModels(permission.owner);
|
||||||
|
this.getResources(permission.owner);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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) {
|
parsePermissionField(key, value) {
|
||||||
if ([""].includes(key)) {
|
if ([""].includes(key)) {
|
||||||
value = Setting.myParseInt(value);
|
value = Setting.myParseInt(value);
|
||||||
@@ -212,6 +224,18 @@ class PermissionEditPage extends React.Component {
|
|||||||
</Select>
|
</Select>
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</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"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("permission:Actions"), i18next.t("permission:Actions - Tooltip"))} :
|
{Setting.getLabel(i18next.t("permission:Actions"), i18next.t("permission:Actions - Tooltip"))} :
|
||||||
|
Reference in New Issue
Block a user