casdoor/object/permission_enforcer.go

359 lines
8.8 KiB
Go
Raw Normal View History

2022-08-07 23:55:03 +08:00
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package object
import (
"fmt"
2022-08-07 23:55:03 +08:00
"strings"
"github.com/casbin/casbin/v2"
"github.com/casbin/casbin/v2/config"
"github.com/casbin/casbin/v2/log"
2022-08-07 23:55:03 +08:00
"github.com/casbin/casbin/v2/model"
"github.com/casdoor/casdoor/conf"
feat: app session control and db migrate (#1539) * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process (#1533) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process * feat: integrate application session management into Casdoor's session management (#774) && standardized the database migration process --------- Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com> * fix: migrate err * fix: migrate err * feat: app session control and db migrate * feat: app session control and db migrate * feat: app session control and db migrate --------- Co-authored-by: Zayn Xie <84443886+xiaoniuren99@users.noreply.github.com>
2023-02-12 09:33:24 +08:00
xormadapter "github.com/casdoor/xorm-adapter/v3"
2022-08-07 23:55:03 +08:00
)
func getEnforcer(permission *Permission) *casbin.Enforcer {
tableName := "permission_rule"
if len(permission.Adapter) != 0 {
adapterObj, err := getCasbinAdapter(permission.Owner, permission.Adapter)
if err != nil {
panic(err)
}
2023-04-20 01:33:26 +08:00
if adapterObj != nil && adapterObj.Table != "" {
tableName = adapterObj.Table
}
}
2022-08-07 23:55:03 +08:00
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
driverName := conf.GetConfigString("driverName")
dataSourceName := conf.GetConfigRealDataSourceName(driverName)
adapter, err := xormadapter.NewAdapterWithTableName(driverName, dataSourceName, tableName, tableNamePrefix, true)
2022-08-07 23:55:03 +08:00
if err != nil {
panic(err)
}
permissionModel, err := getModel(permission.Owner, permission.Model)
if err != nil {
panic(err)
}
m := model.Model{}
2022-08-07 23:55:03 +08:00
if permissionModel != nil {
m, err = GetBuiltInModel(permissionModel.ModelText)
} else {
m, err = GetBuiltInModel("")
2022-08-07 23:55:03 +08:00
}
2022-08-07 23:55:03 +08:00
if err != nil {
panic(err)
}
// Init an enforcer instance without specifying a model or adapter.
// If you specify an adapter, it will load all policies, which is a
// heavy process that can slow down the application.
enforcer, err := casbin.NewEnforcer(&log.DefaultLogger{}, false)
if err != nil {
panic(err)
}
2023-05-12 21:32:48 +08:00
err = enforcer.InitWithModelAndAdapter(m, nil)
if err != nil {
panic(err)
}
enforcer.SetAdapter(adapter)
policyFilter := xormadapter.Filter{
V5: []string{permission.GetId()},
}
if !HasRoleDefinition(m) {
policyFilter.Ptype = []string{"p"}
2022-08-07 23:55:03 +08:00
}
err = enforcer.LoadFilteredPolicy(policyFilter)
if err != nil {
panic(err)
}
2022-08-07 23:55:03 +08:00
return enforcer
}
func getPolicies(permission *Permission) [][]string {
2022-08-07 23:55:03 +08:00
var policies [][]string
permissionId := permission.GetId()
domainExist := len(permission.Domains) > 0
2022-08-07 23:55:03 +08:00
for _, user := range permission.Users {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{user, domain, resource, strings.ToLower(action), "", permissionId})
}
} else {
policies = append(policies, []string{user, resource, strings.ToLower(action), "", "", permissionId})
}
2022-08-07 23:55:03 +08:00
}
}
}
for _, role := range permission.Roles {
for _, resource := range permission.Resources {
for _, action := range permission.Actions {
if domainExist {
for _, domain := range permission.Domains {
policies = append(policies, []string{role, domain, resource, strings.ToLower(action), "", permissionId})
}
} else {
policies = append(policies, []string{role, resource, strings.ToLower(action), "", "", permissionId})
}
}
}
}
return policies
}
func getRolesInRole(roleId string, visited map[string]struct{}) ([]*Role, error) {
role, err := GetRole(roleId)
if err != nil {
return []*Role{}, err
}
if role == nil {
return []*Role{}, nil
}
visited[roleId] = struct{}{}
roles := []*Role{role}
for _, subRole := range role.Roles {
if _, ok := visited[subRole]; !ok {
r, err := getRolesInRole(subRole, visited)
if err != nil {
return []*Role{}, err
}
roles = append(roles, r...)
}
}
return roles, nil
}
func getGroupingPolicies(permission *Permission) [][]string {
var groupingPolicies [][]string
domainExist := len(permission.Domains) > 0
permissionId := permission.GetId()
for _, roleId := range permission.Roles {
visited := map[string]struct{}{}
rolesInRole, err := getRolesInRole(roleId, visited)
if err != nil {
panic(err)
}
for _, role := range rolesInRole {
roleId := role.GetId()
for _, subUser := range role.Users {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subUser, roleId, domain, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subUser, roleId, "", "", "", permissionId})
}
}
for _, subRole := range role.Roles {
if domainExist {
for _, domain := range permission.Domains {
groupingPolicies = append(groupingPolicies, []string{subRole, roleId, domain, "", "", permissionId})
}
} else {
groupingPolicies = append(groupingPolicies, []string{subRole, roleId, "", "", "", permissionId})
}
}
}
2022-08-07 23:55:03 +08:00
}
return groupingPolicies
2022-08-07 23:55:03 +08:00
}
func addPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies := getPolicies(permission)
_, err := enforcer.AddPolicies(policies)
if err != nil {
panic(err)
}
}
func addGroupingPolicies(permission *Permission) {
enforcer := getEnforcer(permission)
groupingPolicies := getGroupingPolicies(permission)
if len(groupingPolicies) > 0 {
_, err := enforcer.AddGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
2022-08-07 23:55:03 +08:00
}
func removeGroupingPolicies(permission *Permission) {
2022-08-07 23:55:03 +08:00
enforcer := getEnforcer(permission)
groupingPolicies := getGroupingPolicies(permission)
2022-08-07 23:55:03 +08:00
if len(groupingPolicies) > 0 {
_, err := enforcer.RemoveGroupingPolicies(groupingPolicies)
if err != nil {
panic(err)
}
}
}
func removePolicies(permission *Permission) {
enforcer := getEnforcer(permission)
policies := getPolicies(permission)
_, err := enforcer.RemovePolicies(policies)
if err != nil {
panic(err)
}
}
2023-05-12 21:32:48 +08:00
type CasbinRequest = []interface{}
2023-06-04 17:29:34 +08:00
func Enforce(permissionId string, request *CasbinRequest) (bool, error) {
permission, err := GetPermission(permissionId)
if err != nil {
2023-06-04 17:29:34 +08:00
return false, err
}
2023-05-12 21:32:48 +08:00
enforcer := getEnforcer(permission)
2023-06-04 17:29:34 +08:00
return enforcer.Enforce(*request...)
2022-08-07 23:55:03 +08:00
}
2023-06-04 17:29:34 +08:00
func BatchEnforce(permissionId string, requests *[]CasbinRequest) ([]bool, error) {
permission, err := GetPermission(permissionId)
if err != nil {
2023-06-04 17:29:34 +08:00
res := []bool{}
for i := 0; i < len(*requests); i++ {
res = append(res, false)
}
return res, err
}
2022-08-07 23:55:03 +08:00
enforcer := getEnforcer(permission)
2023-06-04 17:29:34 +08:00
return enforcer.BatchEnforce(*requests)
2022-08-07 23:55:03 +08:00
}
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) []string {
permissions, _, err := GetPermissionsAndRolesByUser(userId)
if err != nil {
panic(err)
}
for _, role := range GetAllRoles(userId) {
permissionsByRole, err := GetPermissionsByRole(role)
if err != nil {
panic(err)
}
permissions = append(permissions, permissionsByRole...)
}
2022-08-07 23:55:03 +08:00
var values []string
for _, permission := range permissions {
enforcer := getEnforcer(permission)
values = append(values, fn(enforcer)...)
2022-08-07 23:55:03 +08:00
}
return values
}
func GetAllObjects(userId string) []string {
return getAllValues(userId, func(enforcer *casbin.Enforcer) []string {
return enforcer.GetAllObjects()
})
2022-08-07 23:55:03 +08:00
}
func GetAllActions(userId string) []string {
return getAllValues(userId, func(enforcer *casbin.Enforcer) []string {
return enforcer.GetAllActions()
})
2022-08-07 23:55:03 +08:00
}
func GetAllRoles(userId string) []string {
roles, err := GetRolesByUser(userId)
if err != nil {
panic(err)
}
2022-08-07 23:55:03 +08:00
var res []string
for _, role := range roles {
res = append(res, role.Name)
}
return res
}
func GetBuiltInModel(modelText string) (model.Model, error) {
if modelText == "" {
modelText = `
[request_definition]
r = sub, obj, act
[policy_definition]
p = sub, obj, act, "", "", permissionId
[role_definition]
g = _, _
[policy_effect]
e = some(where (p.eft == allow))
[matchers]
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
return model.NewModelFromString(modelText)
} else {
cfg, err := config.NewConfigFromText(modelText)
if err != nil {
return nil, err
}
// load [policy_definition]
policyDefinition := strings.Split(cfg.String("policy_definition::p"), ",")
fieldsNum := len(policyDefinition)
if fieldsNum > builtInAvailableField {
panic(fmt.Errorf("the maximum policy_definition field number cannot exceed %d", builtInAvailableField))
}
// filled empty field with "" and V5 with "permissionId"
for i := builtInAvailableField - fieldsNum; i > 0; i-- {
policyDefinition = append(policyDefinition, "")
}
policyDefinition = append(policyDefinition, "permissionId")
m, _ := model.NewModelFromString(modelText)
m.AddDef("p", "p", strings.Join(policyDefinition, ","))
return m, err
}
}