diff --git a/controllers/auth.go b/controllers/auth.go index 70c73a9ed..d0db8a981 100644 --- a/controllers/auth.go +++ b/controllers/auth.go @@ -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) diff --git a/object/adapter.go b/object/adapter.go index 0394ba544..781151689 100644 --- a/object/adapter.go +++ b/object/adapter.go @@ -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 { diff --git a/object/check.go b/object/check.go index 649d8b333..230f9cec5 100644 --- a/object/check.go +++ b/object/check.go @@ -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 } \ No newline at end of file diff --git a/object/permission.go b/object/permission.go index e60c24838..de87b2356 100644 --- a/object/permission.go +++ b/object/permission.go @@ -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" ) @@ -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{}) @@ -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 } @@ -104,6 +120,11 @@ func UpdatePermission(id string, permission *Permission) bool { panic(err) } + if affected != 0 { + removePolicies(oldPermission) + addPolicies(permission) + } + return affected != 0 } @@ -113,6 +134,10 @@ func AddPermission(permission *Permission) bool { panic(err) } + if affected != 0 { + addPolicies(permission) + } + return affected != 0 } @@ -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) + } +} diff --git a/web/src/PermissionEditPage.js b/web/src/PermissionEditPage.js index 55cd4321a..052ae2c78 100644 --- a/web/src/PermissionEditPage.js +++ b/web/src/PermissionEditPage.js @@ -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; @@ -36,6 +37,7 @@ class PermissionEditPage extends React.Component { users: [], roles: [], models: [], + resources: [], mode: props.location.mode !== undefined ? props.location.mode : "edit", }; } @@ -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); }); } @@ -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); @@ -212,6 +224,18 @@ class PermissionEditPage extends React.Component { + + + {Setting.getLabel(i18next.t("permission:Resources"), i18next.t("permission:Resources - Tooltip"))} : + + + + + {Setting.getLabel(i18next.t("permission:Actions"), i18next.t("permission:Actions - Tooltip"))} :