mirror of
https://github.com/casdoor/casdoor.git
synced 2025-05-23 02:35:49 +08:00
feat: use the casbin model to store relationships between users and groups (#2178)
* fix:reslove conflict * fix: remove interface
This commit is contained in:
parent
eafaa135b4
commit
1a9d02be46
@ -90,7 +90,7 @@ func (c *ApiController) GetUsers() {
|
|||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
if groupName != "" {
|
if groupName != "" {
|
||||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
|
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@ -567,6 +567,22 @@ func (c *ApiController) RemoveUserFromGroup() {
|
|||||||
name := c.Ctx.Request.Form.Get("name")
|
name := c.Ctx.Request.Form.Get("name")
|
||||||
groupName := c.Ctx.Request.Form.Get("groupName")
|
groupName := c.Ctx.Request.Form.Get("groupName")
|
||||||
|
|
||||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, util.GetId(owner, groupName)))
|
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
||||||
c.ServeJSON()
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
item := object.GetAccountItemByName("Groups", organization)
|
||||||
|
res, msg := object.CheckAccountItemModifyRule(item, c.IsAdmin(), c.GetAcceptLanguage())
|
||||||
|
if !res {
|
||||||
|
c.ResponseError(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := object.DeleteGroupForUser(util.GetId(owner, name), groupName)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(affected)
|
||||||
}
|
}
|
||||||
|
1
main.go
1
main.go
@ -49,6 +49,7 @@ func main() {
|
|||||||
object.InitLdapAutoSynchronizer()
|
object.InitLdapAutoSynchronizer()
|
||||||
proxy.InitHttpClient()
|
proxy.InitHttpClient()
|
||||||
authz.InitApi()
|
authz.InitApi()
|
||||||
|
object.InitUserManager()
|
||||||
|
|
||||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
|
"github.com/xorm-io/xorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Adapter struct {
|
type Adapter struct {
|
||||||
@ -155,14 +156,17 @@ func (adapter *Adapter) initAdapter() error {
|
|||||||
|
|
||||||
if adapter.builtInAdapter() {
|
if adapter.builtInAdapter() {
|
||||||
dataSourceName = conf.GetConfigString("dataSourceName")
|
dataSourceName = conf.GetConfigString("dataSourceName")
|
||||||
|
if adapter.DatabaseType == "mysql" {
|
||||||
|
dataSourceName = dataSourceName + adapter.Database
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
switch adapter.DatabaseType {
|
switch adapter.DatabaseType {
|
||||||
case "mssql":
|
case "mssql":
|
||||||
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User,
|
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User,
|
||||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||||
case "mysql":
|
case "mysql":
|
||||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/", adapter.User,
|
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", adapter.User,
|
||||||
adapter.Password, adapter.Host, adapter.Port)
|
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||||
case "postgres":
|
case "postgres":
|
||||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", adapter.User,
|
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s", adapter.User,
|
||||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||||
@ -181,7 +185,8 @@ func (adapter *Adapter) initAdapter() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var err error
|
var err error
|
||||||
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(NewAdapter(adapter.DatabaseType, dataSourceName, adapter.Database).Engine, adapter.getTable(), adapter.TableNamePrefix)
|
engine, err := xorm.NewEngine(adapter.DatabaseType, dataSourceName)
|
||||||
|
adapter.Adapter, err = xormadapter.NewAdapterByEngineWithTableName(engine, adapter.getTable(), adapter.TableNamePrefix)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -327,7 +332,7 @@ func (adapter *Adapter) builtInAdapter() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
return adapter.Name == "permission-adapter-built-in" || adapter.Name == "api-adapter-built-in"
|
return adapter.Name == "user-adapter-built-in" || adapter.Name == "api-adapter-built-in"
|
||||||
}
|
}
|
||||||
|
|
||||||
func getModelDef() model.Model {
|
func getModelDef() model.Model {
|
||||||
|
@ -214,30 +214,18 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
|||||||
return treeData
|
return treeData
|
||||||
}
|
}
|
||||||
|
|
||||||
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
|
|
||||||
user, err := getUser(owner, name)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
if user == nil {
|
|
||||||
return false, errors.New("user not exist")
|
|
||||||
}
|
|
||||||
|
|
||||||
user.Groups = util.DeleteVal(user.Groups, groupId)
|
|
||||||
affected, err := updateUser(user.GetId(), user, []string{"groups"})
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
return affected != 0, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
||||||
|
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||||
|
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
if field == "" && value == "" {
|
if field == "" && value == "" {
|
||||||
return ormer.Engine.Where(builder.Like{"`groups`", groupId}).
|
return int64(len(names)), nil
|
||||||
Count(&User{})
|
|
||||||
} else {
|
} else {
|
||||||
return ormer.Engine.Table("user").
|
return ormer.Engine.Table("user").
|
||||||
Where(builder.Like{"`groups`", groupId}).
|
Where("owner = ?", owner).In("name", names).
|
||||||
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||||
Count()
|
Count()
|
||||||
}
|
}
|
||||||
@ -245,8 +233,14 @@ func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
|||||||
|
|
||||||
func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
|
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||||
|
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
session := ormer.Engine.Table("user").
|
session := ormer.Engine.Table("user").
|
||||||
Where(builder.Like{"`groups`", groupId + "\""})
|
Where("owner = ?", owner).In("name", names)
|
||||||
|
|
||||||
if offset != -1 && limit != -1 {
|
if offset != -1 && limit != -1 {
|
||||||
session.Limit(limit, offset)
|
session.Limit(limit, offset)
|
||||||
@ -265,7 +259,7 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
|
|||||||
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||||
}
|
}
|
||||||
|
|
||||||
err := session.Find(&users)
|
err = session.Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -275,13 +269,13 @@ func GetPaginationGroupUsers(groupId string, offset, limit int, field, value, so
|
|||||||
|
|
||||||
func GetGroupUsers(groupId string) ([]*User, error) {
|
func GetGroupUsers(groupId string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
err := ormer.Engine.Table("user").
|
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||||
Where(builder.Like{"`groups`", groupId + "\""}).
|
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||||
Find(&users)
|
|
||||||
|
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -29,6 +29,23 @@ const (
|
|||||||
UserPropertiesWechatOpenId = "wechatOpenId"
|
UserPropertiesWechatOpenId = "wechatOpenId"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const UserEnforcerId = "built-in/user-enforcer-built-in"
|
||||||
|
|
||||||
|
var userEnforcer *UserGroupEnforcer
|
||||||
|
|
||||||
|
func InitUserManager() {
|
||||||
|
enforcer, err := GetEnforcer(UserEnforcerId)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
err = enforcer.InitEnforcer()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
userEnforcer = NewUserGroupEnforcer(enforcer.Enforcer)
|
||||||
|
}
|
||||||
|
|
||||||
type User struct {
|
type User struct {
|
||||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
@ -531,6 +548,13 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
columns = append(columns, "name", "email", "phone", "country_code")
|
columns = append(columns, "name", "email", "phone", "country_code")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if util.ContainsString(columns, "groups") {
|
||||||
|
_, err := userEnforcer.UpdateGroupsForUser(user.GetId(), user.Groups)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := updateUser(id, user, columns)
|
affected, err := updateUser(id, user, columns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
@ -778,6 +802,10 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func DeleteGroupForUser(user string, group string) (bool, error) {
|
||||||
|
return userEnforcer.DeleteGroupForUser(user, group)
|
||||||
|
}
|
||||||
|
|
||||||
func userChangeTrigger(oldName string, newName string) error {
|
func userChangeTrigger(oldName string, newName string) error {
|
||||||
session := ormer.Engine.NewSession()
|
session := ormer.Engine.NewSession()
|
||||||
defer session.Close()
|
defer session.Close()
|
||||||
|
95
object/user_enforcer.go
Normal file
95
object/user_enforcer.go
Normal file
@ -0,0 +1,95 @@
|
|||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/casbin/casbin/v2"
|
||||||
|
"github.com/casbin/casbin/v2/errors"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserGroupEnforcer struct {
|
||||||
|
// use rbac model implement use group, the enforcer can also implement user role
|
||||||
|
enforcer *casbin.Enforcer
|
||||||
|
}
|
||||||
|
|
||||||
|
func NewUserGroupEnforcer(enforcer *casbin.Enforcer) *UserGroupEnforcer {
|
||||||
|
return &UserGroupEnforcer{
|
||||||
|
enforcer: enforcer,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) AddGroupForUser(user string, group string) (bool, error) {
|
||||||
|
return e.enforcer.AddRoleForUser(user, GetGroupWithPrefix(group))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) AddGroupsForUser(user string, groups []string) (bool, error) {
|
||||||
|
g := make([]string, len(groups))
|
||||||
|
for i, group := range groups {
|
||||||
|
g[i] = GetGroupWithPrefix(group)
|
||||||
|
}
|
||||||
|
return e.enforcer.AddRolesForUser(user, g)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) DeleteGroupForUser(user string, group string) (bool, error) {
|
||||||
|
return e.enforcer.DeleteRoleForUser(user, GetGroupWithPrefix(group))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) DeleteGroupsForUser(user string) (bool, error) {
|
||||||
|
return e.enforcer.DeleteRolesForUser(user)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) GetGroupsForUser(user string) ([]string, error) {
|
||||||
|
groups, err := e.enforcer.GetRolesForUser(user)
|
||||||
|
for i, group := range groups {
|
||||||
|
groups[i] = GetGroupWithoutPrefix(group)
|
||||||
|
}
|
||||||
|
return groups, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) GetAllUsersByGroup(group string) ([]string, error) {
|
||||||
|
users, err := e.enforcer.GetUsersForRole(GetGroupWithPrefix(group))
|
||||||
|
if err != nil {
|
||||||
|
if err == errors.ERR_NAME_NOT_FOUND {
|
||||||
|
return []string{}, nil
|
||||||
|
}
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupWithPrefix(group string) string {
|
||||||
|
return "group:" + group
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupWithoutPrefix(group string) string {
|
||||||
|
return group[len("group:"):]
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) GetUserNamesByGroupName(groupName string) ([]string, error) {
|
||||||
|
var names []string
|
||||||
|
|
||||||
|
userIds, err := e.GetAllUsersByGroup(groupName)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, userId := range userIds {
|
||||||
|
_, name := util.GetOwnerAndNameFromIdNoCheck(userId)
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return names, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (e *UserGroupEnforcer) UpdateGroupsForUser(user string, groups []string) (bool, error) {
|
||||||
|
_, err := e.DeleteGroupsForUser(user)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := e.AddGroupsForUser(user, groups)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected, nil
|
||||||
|
}
|
@ -221,6 +221,7 @@ class GroupTreePage extends React.Component {
|
|||||||
onChange={(value) => {
|
onChange={(value) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
organizationName: value,
|
organizationName: value,
|
||||||
|
groupName: "",
|
||||||
});
|
});
|
||||||
this.props.history.push(`/trees/${value}`);
|
this.props.history.push(`/trees/${value}`);
|
||||||
}}
|
}}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user