mirror of
https://github.com/casdoor/casdoor.git
synced 2025-07-22 05:53:50 +08:00
Compare commits
26 Commits
Author | SHA1 | Date | |
---|---|---|---|
1a9d02be46 | |||
eafaa135b4 | |||
6746551447 | |||
3cb46c3628 | |||
558bcf95d6 | |||
bb937c30c1 | |||
8dfdf7f767 | |||
62b2082e82 | |||
a1806439f8 | |||
01e58158b7 | |||
15427ad9d6 | |||
d058f78dc6 | |||
fd9dbf8251 | |||
3220a04fa9 | |||
f06a4990bd | |||
9df7de5f27 | |||
56c808c091 | |||
9fd2421564 | |||
689d45c7fa | |||
c24343bd53 | |||
979f43638d | |||
685a4514cd | |||
a05ca3af24 | |||
c6f301ff9e | |||
d7b2bcf288 | |||
67ac3d6d21 |
@ -90,7 +90,7 @@ func (c *ApiController) GetUsers() {
|
||||
|
||||
if limit == "" || page == "" {
|
||||
if groupName != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(util.GetId(owner, groupName)))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@ -567,6 +567,22 @@ func (c *ApiController) RemoveUserFromGroup() {
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
groupName := c.Ctx.Request.Form.Get("groupName")
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupName))
|
||||
c.ServeJSON()
|
||||
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
||||
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)
|
||||
}
|
||||
|
20
go.mod
20
go.mod
@ -12,9 +12,9 @@ require (
|
||||
github.com/beevik/etree v1.1.0
|
||||
github.com/casbin/casbin v1.9.1 // indirect
|
||||
github.com/casbin/casbin/v2 v2.30.1
|
||||
github.com/casdoor/go-sms-sender v0.6.1
|
||||
github.com/casdoor/go-sms-sender v0.12.0
|
||||
github.com/casdoor/gomail/v2 v2.0.1
|
||||
github.com/casdoor/oss v1.2.1
|
||||
github.com/casdoor/oss v1.3.0
|
||||
github.com/casdoor/xorm-adapter/v3 v3.0.4
|
||||
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
|
||||
github.com/denisenkom/go-mssqldb v0.9.0
|
||||
@ -28,9 +28,6 @@ require (
|
||||
github.com/go-sql-driver/mysql v1.6.0
|
||||
github.com/go-webauthn/webauthn v0.6.0
|
||||
github.com/golang-jwt/jwt/v4 v4.5.0
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
github.com/golang/protobuf v1.5.2 // indirect
|
||||
github.com/golang/snappy v0.0.4 // indirect
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.7.3 // indirect
|
||||
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0 // indirect
|
||||
@ -43,7 +40,7 @@ require (
|
||||
github.com/nyaruka/phonenumbers v1.1.5
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/prometheus/client_model v0.3.0
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
github.com/robfig/cron/v3 v3.0.1
|
||||
github.com/russellhaering/gosaml2 v0.9.0
|
||||
@ -53,7 +50,7 @@ require (
|
||||
github.com/shirou/gopsutil v3.21.11+incompatible
|
||||
github.com/siddontang/go-log v0.0.0-20190221022429-1e957dd83bed
|
||||
github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e
|
||||
github.com/stretchr/testify v1.8.2
|
||||
github.com/stretchr/testify v1.8.3
|
||||
github.com/tealeg/xlsx v1.0.5
|
||||
github.com/thanhpk/randstr v1.0.4
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
@ -61,12 +58,11 @@ require (
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
golang.org/x/crypto v0.6.0
|
||||
golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
|
||||
golang.org/x/net v0.7.0
|
||||
golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914
|
||||
golang.org/x/crypto v0.11.0
|
||||
golang.org/x/net v0.13.0
|
||||
golang.org/x/oauth2 v0.10.0
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
modernc.org/sqlite v1.10.1-0.20210314190707-798bbeb9bb84
|
||||
modernc.org/sqlite v1.18.2
|
||||
)
|
||||
|
@ -132,7 +132,7 @@ func (idp *GiteeIdProvider) GetToken(code string) (*oauth2.Token, error) {
|
||||
"type": "User",
|
||||
"blog": null,
|
||||
"weibo": null,
|
||||
"bio": "个人博客:https://gitee.com/xxx/xxx/pages",
|
||||
"bio": "bio",
|
||||
"public_repos": 2,
|
||||
"public_gists": 0,
|
||||
"followers": 0,
|
||||
|
1
main.go
1
main.go
@ -49,6 +49,7 @@ func main() {
|
||||
object.InitLdapAutoSynchronizer()
|
||||
proxy.InitHttpClient()
|
||||
authz.InitApi()
|
||||
object.InitUserManager()
|
||||
|
||||
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
|
||||
|
||||
|
@ -23,6 +23,7 @@ import (
|
||||
"github.com/casdoor/casdoor/util"
|
||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||
"github.com/xorm-io/core"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
type Adapter struct {
|
||||
@ -33,7 +34,7 @@ type Adapter struct {
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
DatabaseType string `xorm:"varchar(100)" json:"databaseType"`
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port string `xorm:"varchar(20)" json:"port"`
|
||||
Port int `json:"port"`
|
||||
User string `xorm:"varchar(100)" json:"user"`
|
||||
Password string `xorm:"varchar(100)" json:"password"`
|
||||
Database string `xorm:"varchar(100)" json:"database"`
|
||||
@ -153,21 +154,24 @@ func (adapter *Adapter) initAdapter() error {
|
||||
if adapter.Adapter == nil {
|
||||
var dataSourceName string
|
||||
|
||||
if adapter.buildInAdapter() {
|
||||
if adapter.builtInAdapter() {
|
||||
dataSourceName = conf.GetConfigString("dataSourceName")
|
||||
if adapter.DatabaseType == "mysql" {
|
||||
dataSourceName = dataSourceName + adapter.Database
|
||||
}
|
||||
} else {
|
||||
switch adapter.DatabaseType {
|
||||
case "mssql":
|
||||
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%s?database=%s", adapter.User,
|
||||
dataSourceName = fmt.Sprintf("sqlserver://%s:%s@%s:%d?database=%s", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "mysql":
|
||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%s)/", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port)
|
||||
dataSourceName = fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", adapter.User,
|
||||
adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "postgres":
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%s 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)
|
||||
case "CockroachDB":
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%s sslmode=disable dbname=%s serial_normalization=virtual_sequence",
|
||||
dataSourceName = fmt.Sprintf("user=%s password=%s host=%s port=%d sslmode=disable dbname=%s serial_normalization=virtual_sequence",
|
||||
adapter.User, adapter.Password, adapter.Host, adapter.Port, adapter.Database)
|
||||
case "sqlite3":
|
||||
dataSourceName = fmt.Sprintf("file:%s", adapter.Host)
|
||||
@ -181,7 +185,8 @@ func (adapter *Adapter) initAdapter() 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 {
|
||||
return err
|
||||
}
|
||||
@ -269,6 +274,11 @@ func UpdatePolicy(oldPolicy, newPolicy []string, adapter *Adapter) (bool, error)
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
err = adapter.SavePolicy(casbinModel)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
@ -285,6 +295,10 @@ func AddPolicy(policy []string, adapter *Adapter) (bool, error) {
|
||||
}
|
||||
|
||||
casbinModel.AddPolicy("p", "p", policy)
|
||||
err = adapter.SavePolicy(casbinModel)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return true, nil
|
||||
}
|
||||
@ -305,15 +319,20 @@ func RemovePolicy(policy []string, adapter *Adapter) (bool, error) {
|
||||
if err != nil {
|
||||
return affected, err
|
||||
}
|
||||
err = adapter.SavePolicy(casbinModel)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
func (adapter *Adapter) buildInAdapter() bool {
|
||||
func (adapter *Adapter) builtInAdapter() bool {
|
||||
if adapter.Owner != "built-in" {
|
||||
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 {
|
||||
|
@ -162,7 +162,7 @@ func UpdateCert(id string, cert *Cert) (bool, error) {
|
||||
if name != cert.Name {
|
||||
err := certChangeTrigger(name, cert.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
affected, err := ormer.Engine.ID(core.PK{owner, name}).AllCols().Update(cert)
|
||||
|
@ -15,7 +15,7 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
@ -120,45 +120,50 @@ func DeleteEnforcer(enforcer *Enforcer) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func (p *Enforcer) GetId() string {
|
||||
return fmt.Sprintf("%s/%s", p.Owner, p.Name)
|
||||
}
|
||||
|
||||
func (enforcer *Enforcer) InitEnforcer() error {
|
||||
if enforcer.Enforcer == nil {
|
||||
if enforcer == nil {
|
||||
return errors.New("enforcer is nil")
|
||||
}
|
||||
if enforcer.Model == "" || enforcer.Adapter == "" {
|
||||
return errors.New("missing model or adapter")
|
||||
}
|
||||
|
||||
var err error
|
||||
var m *Model
|
||||
var a *Adapter
|
||||
|
||||
if m, err = GetModel(enforcer.Model); err != nil {
|
||||
return err
|
||||
} else if m == nil {
|
||||
return errors.New("model not found")
|
||||
}
|
||||
if a, err = GetAdapter(enforcer.Adapter); err != nil {
|
||||
return err
|
||||
} else if a == nil {
|
||||
return errors.New("adapter not found")
|
||||
}
|
||||
|
||||
err = m.initModel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = a.initAdapter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
enforcer.Enforcer = casbinEnforcer
|
||||
if enforcer.Enforcer != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if enforcer.Model == "" {
|
||||
return fmt.Errorf("the model for enforcer: %s should not be empty", enforcer.GetId())
|
||||
}
|
||||
if enforcer.Adapter == "" {
|
||||
return fmt.Errorf("the adapter for enforcer: %s should not be empty", enforcer.GetId())
|
||||
}
|
||||
|
||||
m, err := GetModel(enforcer.Model)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if m == nil {
|
||||
return fmt.Errorf("the model: %s for enforcer: %s is not found", enforcer.Model, enforcer.GetId())
|
||||
}
|
||||
|
||||
a, err := GetAdapter(enforcer.Adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
} else if a == nil {
|
||||
return fmt.Errorf("the adapter: %s for enforcer: %s is not found", enforcer.Adapter, enforcer.GetId())
|
||||
}
|
||||
|
||||
err = m.initModel()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = a.initAdapter()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
casbinEnforcer, err := casbin.NewEnforcer(m.Model, a.Adapter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
enforcer.Enforcer = casbinEnforcer
|
||||
return nil
|
||||
}
|
||||
|
@ -164,7 +164,7 @@ func DeleteGroup(group *Group) (bool, error) {
|
||||
return false, errors.New("group has children group")
|
||||
}
|
||||
|
||||
if count, err := GetGroupUserCount(group.Name, "", ""); err != nil {
|
||||
if count, err := GetGroupUserCount(group.GetId(), "", ""); err != nil {
|
||||
return false, err
|
||||
} else if count > 0 {
|
||||
return false, errors.New("group has users")
|
||||
@ -214,39 +214,33 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||
return treeData
|
||||
}
|
||||
|
||||
func RemoveUserFromGroup(owner, name, groupName string) (bool, error) {
|
||||
user, err := getUser(owner, name)
|
||||
func GetGroupUserCount(groupId string, field, value string) (int64, error) {
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if user == nil {
|
||||
return false, errors.New("user not exist")
|
||||
return 0, err
|
||||
}
|
||||
|
||||
user.Groups = util.DeleteVal(user.Groups, groupName)
|
||||
affected, err := updateUser(user.GetId(), user, []string{"groups"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return affected != 0, err
|
||||
}
|
||||
|
||||
func GetGroupUserCount(groupName string, field, value string) (int64, error) {
|
||||
if field == "" && value == "" {
|
||||
return ormer.Engine.Where(builder.Like{"`groups`", groupName}).
|
||||
Count(&User{})
|
||||
return int64(len(names)), nil
|
||||
} else {
|
||||
return ormer.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName}).
|
||||
Where("owner = ?", owner).In("name", names).
|
||||
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||
Count()
|
||||
}
|
||||
}
|
||||
|
||||
func GetPaginationGroupUsers(groupName 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{}
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
session := ormer.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName + "\""})
|
||||
Where("owner = ?", owner).In("name", names)
|
||||
|
||||
if offset != -1 && limit != -1 {
|
||||
session.Limit(limit, offset)
|
||||
@ -265,7 +259,7 @@ func GetPaginationGroupUsers(groupName string, offset, limit int, field, value,
|
||||
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||
}
|
||||
|
||||
err := session.Find(&users)
|
||||
err = session.Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@ -273,15 +267,15 @@ func GetPaginationGroupUsers(groupName string, offset, limit int, field, value,
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetGroupUsers(groupName string) ([]*User, error) {
|
||||
func GetGroupUsers(groupId string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
err := ormer.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName + "\""}).
|
||||
Find(&users)
|
||||
owner, _ := util.GetOwnerAndNameFromId(groupId)
|
||||
names, err := userEnforcer.GetUserNamesByGroupName(groupId)
|
||||
|
||||
err = ormer.Engine.Where("owner = ?", owner).In("name", names).Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
|
@ -37,11 +37,11 @@ func InitDb() {
|
||||
|
||||
existed = initBuiltInApiModel()
|
||||
if !existed {
|
||||
initBuildInApiAdapter()
|
||||
initBuiltInApiAdapter()
|
||||
initBuiltInApiEnforcer()
|
||||
initBuiltInPermissionModel()
|
||||
initBuildInPermissionAdapter()
|
||||
initBuiltInPermissionEnforcer()
|
||||
initBuiltInUserModel()
|
||||
initBuiltInUserAdapter()
|
||||
initBuiltInUserEnforcer()
|
||||
}
|
||||
|
||||
initWebAuthn()
|
||||
@ -303,8 +303,8 @@ func initWebAuthn() {
|
||||
gob.Register(webauthn.SessionData{})
|
||||
}
|
||||
|
||||
func initBuiltInPermissionModel() {
|
||||
model, err := GetModel("built-in/permission-model-built-in")
|
||||
func initBuiltInUserModel() {
|
||||
model, err := GetModel("built-in/user-model-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@ -315,7 +315,7 @@ func initBuiltInPermissionModel() {
|
||||
|
||||
model = &Model{
|
||||
Owner: "built-in",
|
||||
Name: "permission-model-built-in",
|
||||
Name: "user-model-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Built-in Model",
|
||||
IsEnabled: true,
|
||||
@ -325,11 +325,14 @@ r = sub, obj, act
|
||||
[policy_definition]
|
||||
p = sub, obj, act
|
||||
|
||||
[role_definition]
|
||||
g = _, _
|
||||
|
||||
[policy_effect]
|
||||
e = some(where (p.eft == allow))
|
||||
|
||||
[matchers]
|
||||
m = r.sub == p.sub && r.obj == p.obj && r.act == p.act`,
|
||||
m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`,
|
||||
}
|
||||
_, err = AddModel(model)
|
||||
if err != nil {
|
||||
@ -347,8 +350,7 @@ func initBuiltInApiModel() bool {
|
||||
return true
|
||||
}
|
||||
|
||||
modelText := `
|
||||
[request_definition]
|
||||
modelText := `[request_definition]
|
||||
r = subOwner, subName, method, urlPath, objOwner, objName
|
||||
|
||||
[policy_definition]
|
||||
@ -367,8 +369,7 @@ m = (r.subOwner == p.subOwner || p.subOwner == "*") && \
|
||||
(r.urlPath == p.urlPath || p.urlPath == "*") && \
|
||||
(r.objOwner == p.objOwner || p.objOwner == "*") && \
|
||||
(r.objName == p.objName || p.objName == "*") || \
|
||||
(r.subOwner == r.objOwner && r.subName == r.objName)
|
||||
`
|
||||
(r.subOwner == r.objOwner && r.subName == r.objName)`
|
||||
|
||||
model = &Model{
|
||||
Owner: "built-in",
|
||||
@ -415,19 +416,19 @@ func initBuiltInPermission() {
|
||||
}
|
||||
}
|
||||
|
||||
func initBuildInPermissionAdapter() {
|
||||
permissionAdapter, err := GetAdapter("built-in/permission-adapter-built-in")
|
||||
func initBuiltInUserAdapter() {
|
||||
adapter, err := GetAdapter("built-in/user-adapter-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if permissionAdapter != nil {
|
||||
if adapter != nil {
|
||||
return
|
||||
}
|
||||
|
||||
permissionAdapter = &Adapter{
|
||||
adapter = &Adapter{
|
||||
Owner: "built-in",
|
||||
Name: "permission-adapter-built-in",
|
||||
Name: "user-adapter-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
Type: "Database",
|
||||
DatabaseType: conf.GetConfigString("driverName"),
|
||||
@ -436,23 +437,23 @@ func initBuildInPermissionAdapter() {
|
||||
Table: "casbin_user_rule",
|
||||
IsEnabled: true,
|
||||
}
|
||||
_, err = AddAdapter(permissionAdapter)
|
||||
_, err = AddAdapter(adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBuildInApiAdapter() {
|
||||
apiAdapter, err := GetAdapter("built-in/api-adapter-built-in")
|
||||
func initBuiltInApiAdapter() {
|
||||
adapter, err := GetAdapter("built-in/api-adapter-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if apiAdapter != nil {
|
||||
if adapter != nil {
|
||||
return
|
||||
}
|
||||
|
||||
apiAdapter = &Adapter{
|
||||
adapter = &Adapter{
|
||||
Owner: "built-in",
|
||||
Name: "api-adapter-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
@ -463,49 +464,49 @@ func initBuildInApiAdapter() {
|
||||
Table: "casbin_api_rule",
|
||||
IsEnabled: true,
|
||||
}
|
||||
_, err = AddAdapter(apiAdapter)
|
||||
_, err = AddAdapter(adapter)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBuiltInPermissionEnforcer() {
|
||||
permissionEnforcer, err := GetEnforcer("built-in/permission-enforcer-built-in")
|
||||
func initBuiltInUserEnforcer() {
|
||||
enforcer, err := GetEnforcer("built-in/user-enforcer-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if permissionEnforcer != nil {
|
||||
if enforcer != nil {
|
||||
return
|
||||
}
|
||||
|
||||
permissionEnforcer = &Enforcer{
|
||||
enforcer = &Enforcer{
|
||||
Owner: "built-in",
|
||||
Name: "permission-enforcer-built-in",
|
||||
Name: "user-enforcer-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
DisplayName: "Permission Enforcer",
|
||||
Model: "built-in/permission-model-built-in",
|
||||
Adapter: "built-in/permission-adapter-built-in",
|
||||
Model: "built-in/user-model-built-in",
|
||||
Adapter: "built-in/user-adapter-built-in",
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
_, err = AddEnforcer(permissionEnforcer)
|
||||
_, err = AddEnforcer(enforcer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
func initBuiltInApiEnforcer() {
|
||||
apiEnforcer, err := GetEnforcer("built-in/api-enforcer-built-in")
|
||||
enforcer, err := GetEnforcer("built-in/api-enforcer-built-in")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if apiEnforcer != nil {
|
||||
if enforcer != nil {
|
||||
return
|
||||
}
|
||||
|
||||
apiEnforcer = &Enforcer{
|
||||
enforcer = &Enforcer{
|
||||
Owner: "built-in",
|
||||
Name: "api-enforcer-built-in",
|
||||
CreatedTime: util.GetCurrentTime(),
|
||||
@ -515,7 +516,7 @@ func initBuiltInApiEnforcer() {
|
||||
IsEnabled: true,
|
||||
}
|
||||
|
||||
_, err = AddEnforcer(apiEnforcer)
|
||||
_, err = AddEnforcer(enforcer)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@ -41,6 +41,7 @@ type LdapUser struct {
|
||||
GidNumber string `json:"gidNumber"`
|
||||
// Gcn string
|
||||
Uuid string `json:"uuid"`
|
||||
UserPrincipalName string `json:"userPrincipalName"`
|
||||
DisplayName string `json:"displayName"`
|
||||
Mail string
|
||||
Email string `json:"email"`
|
||||
@ -51,9 +52,10 @@ type LdapUser struct {
|
||||
RegisteredAddress string
|
||||
PostalAddress string
|
||||
|
||||
GroupId string `json:"groupId"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
GroupId string `json:"groupId"`
|
||||
Phone string `json:"phone"`
|
||||
Address string `json:"address"`
|
||||
MemberOf string `json:"memberOf"`
|
||||
}
|
||||
|
||||
func (ldap *Ldap) GetLdapConn() (c *LdapConn, err error) {
|
||||
@ -168,6 +170,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
user.Uuid = attribute.Values[0]
|
||||
case "objectGUID":
|
||||
user.Uuid = attribute.Values[0]
|
||||
case "userPrincipalName":
|
||||
user.UserPrincipalName = attribute.Values[0]
|
||||
case "displayName":
|
||||
user.DisplayName = attribute.Values[0]
|
||||
case "mail":
|
||||
@ -186,6 +190,8 @@ func (l *LdapConn) GetLdapUsers(ldapServer *Ldap) ([]LdapUser, error) {
|
||||
user.RegisteredAddress = attribute.Values[0]
|
||||
case "postalAddress":
|
||||
user.PostalAddress = attribute.Values[0]
|
||||
case "memberOf":
|
||||
user.MemberOf = attribute.Values[0]
|
||||
}
|
||||
}
|
||||
ldapUsers = append(ldapUsers, user)
|
||||
|
@ -189,7 +189,7 @@ func UpdateOrganization(id string, organization *Organization) (bool, error) {
|
||||
if name != organization.Name {
|
||||
err := organizationChangeTrigger(name, organization.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -432,7 +432,7 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
||||
|
||||
payment := new(Payment)
|
||||
payment.Owner = newName
|
||||
_, err = session.Where("organization=?", oldName).Update(payment)
|
||||
_, err = session.Where("owner=?", oldName).Update(payment)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -186,20 +186,23 @@ func notifyPayment(request *http.Request, body []byte, owner string, paymentName
|
||||
|
||||
notifyResult, err := pProvider.Notify(request, body, cert.AuthorityPublicKey, orderId)
|
||||
if err != nil {
|
||||
return payment, notifyResult, err
|
||||
return payment, nil, err
|
||||
}
|
||||
|
||||
if notifyResult.PaymentStatus != pp.PaymentStatePaid {
|
||||
return payment, notifyResult, nil
|
||||
}
|
||||
// Only check paid payment
|
||||
if notifyResult.ProductDisplayName != "" && notifyResult.ProductDisplayName != product.DisplayName {
|
||||
err = fmt.Errorf("the payment's product name: %s doesn't equal to the expected product name: %s", notifyResult.ProductDisplayName, product.DisplayName)
|
||||
return payment, notifyResult, err
|
||||
return payment, nil, err
|
||||
}
|
||||
|
||||
if notifyResult.Price != product.Price {
|
||||
err = fmt.Errorf("the payment's price: %f doesn't equal to the expected price: %f", notifyResult.Price, product.Price)
|
||||
return payment, notifyResult, err
|
||||
return payment, nil, err
|
||||
}
|
||||
|
||||
return payment, notifyResult, err
|
||||
return payment, notifyResult, nil
|
||||
}
|
||||
|
||||
func NotifyPayment(request *http.Request, body []byte, owner string, paymentName string, orderId string) (*Payment, error) {
|
||||
@ -210,6 +213,7 @@ func NotifyPayment(request *http.Request, body []byte, owner string, paymentName
|
||||
payment.Message = err.Error()
|
||||
} else {
|
||||
payment.State = notifyResult.PaymentStatus
|
||||
payment.Message = notifyResult.NotifyMessage
|
||||
}
|
||||
_, err = UpdatePayment(payment.GetId(), payment)
|
||||
if err != nil {
|
||||
|
@ -58,10 +58,7 @@ type PermissionRule struct {
|
||||
Id string `xorm:"varchar(100) index not null default ''" json:"id"`
|
||||
}
|
||||
|
||||
const (
|
||||
builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||
builtInAdapter = "permission_rule"
|
||||
)
|
||||
const builtInAvailableField = 5 // Casdoor built-in adapter, use V5 to filter permission, so has 5 available field
|
||||
|
||||
func (p *Permission) GetId() string {
|
||||
return util.GetId(p.Owner, p.Name)
|
||||
@ -290,7 +287,7 @@ func GetPermissionsAndRolesByUser(userId string) ([]*Permission, []*Role, error)
|
||||
|
||||
for _, role := range roles {
|
||||
perms := []*Permission{}
|
||||
err := ormer.Engine.Where("roles like ?", "%"+role.Name+"\"%").Find(&perms)
|
||||
err := ormer.Engine.Where("roles like ?", "%"+role.GetId()+"\"%").Find(&perms)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act`
|
||||
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))
|
||||
panic(fmt.Errorf("the maximum policy_definition field number cannot exceed %d, got %d", builtInAvailableField, fieldsNum))
|
||||
}
|
||||
// filled empty field with "" and V5 with "permissionId"
|
||||
for i := builtInAvailableField - fieldsNum; i > 0; i-- {
|
||||
|
@ -203,7 +203,7 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
|
||||
if name != provider.Name {
|
||||
err := providerChangeTrigger(name, provider.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -254,7 +254,8 @@ func DeleteProvider(provider *Provider) (bool, error) {
|
||||
func (p *Provider) getPaymentProvider() (pp.PaymentProvider, *Cert, error) {
|
||||
cert := &Cert{}
|
||||
if p.Cert != "" {
|
||||
cert, err := getCert(p.Owner, p.Cert)
|
||||
var err error
|
||||
cert, err = getCert(p.Owner, p.Cert)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@ -133,7 +133,7 @@ func UpdateRole(id string, role *Role) (bool, error) {
|
||||
if name != role.Name {
|
||||
err := roleChangeTrigger(name, role.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -24,7 +24,7 @@ func getSmsClient(provider *Provider) (sender.SmsClient, error) {
|
||||
var client sender.SmsClient
|
||||
var err error
|
||||
|
||||
if provider.Type == sender.HuaweiCloud {
|
||||
if provider.Type == sender.HuaweiCloud || provider.Type == sender.AzureACS {
|
||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.ProviderUrl, provider.AppId)
|
||||
} else {
|
||||
client, err = sender.NewSmsClient(provider.Type, provider.ClientId, provider.ClientSecret, provider.SignName, provider.TemplateCode, provider.AppId)
|
||||
|
@ -25,6 +25,7 @@ type TableColumn struct {
|
||||
Name string `json:"name"`
|
||||
Type string `json:"type"`
|
||||
CasdoorName string `json:"casdoorName"`
|
||||
IsKey bool `json:"isKey"`
|
||||
IsHashed bool `json:"isHashed"`
|
||||
Values []string `json:"values"`
|
||||
}
|
||||
|
@ -29,6 +29,23 @@ const (
|
||||
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 {
|
||||
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||
@ -230,7 +247,7 @@ func GetUserCount(owner, field, value string, groupName string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
|
||||
if groupName != "" {
|
||||
return GetGroupUserCount(groupName, field, value)
|
||||
return GetGroupUserCount(util.GetId(owner, groupName), field, value)
|
||||
}
|
||||
|
||||
return session.Count(&User{})
|
||||
@ -274,7 +291,7 @@ func GetPaginationUsers(owner string, offset, limit int, field, value, sortField
|
||||
users := []*User{}
|
||||
|
||||
if groupName != "" {
|
||||
return GetPaginationGroupUsers(groupName, offset, limit, field, value, sortField, sortOrder)
|
||||
return GetPaginationGroupUsers(util.GetId(owner, groupName), offset, limit, field, value, sortField, sortOrder)
|
||||
}
|
||||
|
||||
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
@ -531,6 +548,13 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
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)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@ -568,7 +592,7 @@ func UpdateUserForAllFields(id string, user *User) (bool, error) {
|
||||
if name != user.Name {
|
||||
err := userChangeTrigger(name, user.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
@ -778,6 +802,10 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
func DeleteGroupForUser(user string, group string) (bool, error) {
|
||||
return userEnforcer.DeleteGroupForUser(user, group)
|
||||
}
|
||||
|
||||
func userChangeTrigger(oldName string, newName string) error {
|
||||
session := ormer.Engine.NewSession()
|
||||
defer session.Close()
|
||||
|
@ -69,6 +69,8 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
||||
fileExtension = ".ico"
|
||||
case "image/x-icon":
|
||||
fileExtension = ".ico"
|
||||
case "image/svg+xml":
|
||||
fileExtension = ".svg"
|
||||
default:
|
||||
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
|
||||
}
|
||||
|
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
|
||||
}
|
28
pp/paypal.go
28
pp/paypal.go
@ -17,6 +17,7 @@ package pp
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strconv"
|
||||
|
||||
@ -89,14 +90,24 @@ func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, pa
|
||||
}
|
||||
|
||||
func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (*NotifyResult, error) {
|
||||
notifyResult := &NotifyResult{}
|
||||
captureRsp, err := pp.Client.OrderCapture(context.Background(), orderId, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if captureRsp.Code != paypal.Success {
|
||||
errDetail := captureRsp.ErrorResponse.Details[0]
|
||||
switch errDetail.Issue {
|
||||
// If order is already captured, just skip this type of error and check the order detail
|
||||
if !(len(captureRsp.ErrorResponse.Details) == 1 && captureRsp.ErrorResponse.Details[0].Issue == "ORDER_ALREADY_CAPTURED") {
|
||||
return nil, errors.New(captureRsp.ErrorResponse.Message)
|
||||
case "ORDER_ALREADY_CAPTURED":
|
||||
// skip
|
||||
case "ORDER_NOT_APPROVED":
|
||||
notifyResult.PaymentStatus = PaymentStateCanceled
|
||||
notifyResult.NotifyMessage = errDetail.Description
|
||||
return notifyResult, nil
|
||||
default:
|
||||
err = fmt.Errorf(errDetail.Description)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
// Check the order detail
|
||||
@ -105,7 +116,16 @@ func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, auth
|
||||
return nil, err
|
||||
}
|
||||
if captureRsp.Code != paypal.Success {
|
||||
return nil, errors.New(captureRsp.ErrorResponse.Message)
|
||||
errDetail := captureRsp.ErrorResponse.Details[0]
|
||||
switch errDetail.Issue {
|
||||
case "ORDER_NOT_APPROVED":
|
||||
notifyResult.PaymentStatus = PaymentStateCanceled
|
||||
notifyResult.NotifyMessage = errDetail.Description
|
||||
return notifyResult, nil
|
||||
default:
|
||||
err = fmt.Errorf(errDetail.Description)
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
paymentName := detailRsp.Response.Id
|
||||
@ -126,7 +146,7 @@ func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, auth
|
||||
default:
|
||||
paymentStatus = PaymentStateError
|
||||
}
|
||||
notifyResult := &NotifyResult{
|
||||
notifyResult = &NotifyResult{
|
||||
PaymentStatus: paymentStatus,
|
||||
PaymentName: paymentName,
|
||||
|
||||
|
@ -21,16 +21,18 @@ import (
|
||||
type PaymentState string
|
||||
|
||||
const (
|
||||
PaymentStatePaid PaymentState = "Paid"
|
||||
PaymentStateCreated PaymentState = "Created"
|
||||
PaymentStateError PaymentState = "Error"
|
||||
PaymentStatePaid PaymentState = "Paid"
|
||||
PaymentStateCreated PaymentState = "Created"
|
||||
PaymentStateCanceled PaymentState = "Canceled"
|
||||
PaymentStateError PaymentState = "Error"
|
||||
)
|
||||
|
||||
type NotifyResult struct {
|
||||
PaymentName string
|
||||
PaymentStatus PaymentState
|
||||
ProviderName string
|
||||
NotifyMessage string
|
||||
|
||||
ProviderName string
|
||||
ProductName string
|
||||
ProductDisplayName string
|
||||
Price float64
|
||||
|
@ -55,12 +55,6 @@ func StaticFilter(ctx *context.Context) {
|
||||
path += urlPath
|
||||
}
|
||||
|
||||
path2 := strings.TrimPrefix(path, "web/build/images/")
|
||||
if util.FileExist(path2) {
|
||||
makeGzipResponse(ctx.ResponseWriter, ctx.Request, path2)
|
||||
return
|
||||
}
|
||||
|
||||
if !util.FileExist(path) {
|
||||
path = "web/build/index.html"
|
||||
}
|
||||
|
31
storage/google_cloud.go
Normal file
31
storage/google_cloud.go
Normal file
@ -0,0 +1,31 @@
|
||||
// Copyright 2023 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 storage
|
||||
|
||||
import (
|
||||
"github.com/casdoor/oss"
|
||||
"github.com/casdoor/oss/googlecloud"
|
||||
)
|
||||
|
||||
func NewGoogleCloudStorageProvider(clientId string, clientSecret string, bucket string, endpoint string) oss.StorageInterface {
|
||||
sp, _ := googlecloud.New(&googlecloud.Config{
|
||||
AccessID: clientId,
|
||||
AccessKey: clientSecret,
|
||||
Bucket: bucket,
|
||||
Endpoint: endpoint,
|
||||
})
|
||||
|
||||
return sp
|
||||
}
|
32
storage/qiniu_cloud.go
Normal file
32
storage/qiniu_cloud.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2023 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 storage
|
||||
|
||||
import (
|
||||
"github.com/casdoor/oss"
|
||||
"github.com/casdoor/oss/qiniu"
|
||||
)
|
||||
|
||||
func NewQiniuCloudKodoStorageProvider(clientId string, clientSecret string, region string, bucket string, endpoint string) oss.StorageInterface {
|
||||
sp := qiniu.New(&qiniu.Config{
|
||||
AccessID: clientId,
|
||||
AccessKey: clientSecret,
|
||||
Region: region,
|
||||
Bucket: bucket,
|
||||
Endpoint: endpoint,
|
||||
})
|
||||
|
||||
return sp
|
||||
}
|
@ -30,6 +30,10 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
|
||||
return NewTencentCloudCosStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||
case "Azure Blob":
|
||||
return NewAzureBlobStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||
case "Qiniu Cloud Kodo":
|
||||
return NewQiniuCloudKodoStorageProvider(clientId, clientSecret, region, bucket, endpoint)
|
||||
case "Google Cloud Storage":
|
||||
return NewGoogleCloudStorageProvider(clientId, clientSecret, bucket, endpoint)
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -25,5 +25,12 @@ func CasbinToSlice(casbinRule xormadapter.CasbinRule) []string {
|
||||
casbinRule.V4,
|
||||
casbinRule.V5,
|
||||
}
|
||||
// remove empty strings from end, for update model policy map
|
||||
for i := len(s) - 1; i >= 0; i-- {
|
||||
if s[i] != "" {
|
||||
s = s[:i+1]
|
||||
break
|
||||
}
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
@ -3,7 +3,7 @@
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@ant-design/cssinjs": "^1.8.1",
|
||||
"@ant-design/cssinjs": "1.16.1",
|
||||
"@ant-design/icons": "^4.7.0",
|
||||
"@craco/craco": "^6.4.5",
|
||||
"@crowdin/cli": "^3.7.10",
|
||||
|
@ -13,7 +13,7 @@
|
||||
// limitations under the License.
|
||||
|
||||
import React from "react";
|
||||
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
|
||||
import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
|
||||
import * as AdapterBackend from "./backend/AdapterBackend";
|
||||
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||
import * as Setting from "./Setting";
|
||||
@ -107,8 +107,8 @@ class AdapterEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("provider:Port"), i18next.t("provider:Port - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.adapter.port} onChange={e => {
|
||||
this.updateAdapterField("port", e.target.value);
|
||||
<InputNumber value={this.state.adapter.port} min={0} max={65535} onChange={value => {
|
||||
this.updateAdapterField("port", value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
|
@ -32,7 +32,7 @@ class AdapterListPage extends BaseListPage {
|
||||
createdTime: moment().format(),
|
||||
type: "Database",
|
||||
host: "localhost",
|
||||
port: "3306",
|
||||
port: 3306,
|
||||
user: "root",
|
||||
password: "123456",
|
||||
databaseType: "mysql",
|
||||
|
203
web/src/App.js
203
web/src/App.js
@ -18,7 +18,7 @@ import {Helmet} from "react-helmet";
|
||||
import {MfaRuleRequired} from "./Setting";
|
||||
import * as Setting from "./Setting";
|
||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||
import {BarsOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
||||
import {AppstoreTwoTone, BarsOutlined, DollarTwoTone, DownOutlined, HomeTwoTone, InfoCircleFilled, LockTwoTone, LogoutOutlined, SafetyCertificateTwoTone, SettingOutlined, SettingTwoTone, WalletTwoTone} from "@ant-design/icons";
|
||||
import {Alert, Avatar, Button, Card, ConfigProvider, Drawer, Dropdown, FloatButton, Layout, Menu, Result} from "antd";
|
||||
import {Link, Redirect, Route, Switch, withRouter} from "react-router-dom";
|
||||
import OrganizationListPage from "./OrganizationListPage";
|
||||
@ -91,6 +91,10 @@ import AccountAvatar from "./account/AccountAvatar";
|
||||
|
||||
const {Header, Footer, Content} = Layout;
|
||||
|
||||
import {setTwoToneColor} from "@ant-design/icons";
|
||||
|
||||
setTwoToneColor("rgb(87,52,211)");
|
||||
|
||||
class App extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
@ -147,58 +151,24 @@ class App extends Component {
|
||||
});
|
||||
if (uri === "/") {
|
||||
this.setState({selectedMenuKey: "/"});
|
||||
} else if (uri.includes("/organizations") || uri.includes("/trees")) {
|
||||
this.setState({selectedMenuKey: "/organizations"});
|
||||
} else if (uri.includes("/users")) {
|
||||
this.setState({selectedMenuKey: "/users"});
|
||||
} else if (uri.includes("/groups")) {
|
||||
this.setState({selectedMenuKey: "/groups"});
|
||||
} else if (uri.includes("/roles")) {
|
||||
this.setState({selectedMenuKey: "/roles"});
|
||||
} else if (uri.includes("/permissions")) {
|
||||
this.setState({selectedMenuKey: "/permissions"});
|
||||
} else if (uri.includes("/models")) {
|
||||
this.setState({selectedMenuKey: "/models"});
|
||||
} else if (uri.includes("/adapters")) {
|
||||
this.setState({selectedMenuKey: "/adapters"});
|
||||
} else if (uri.includes("/enforcers")) {
|
||||
this.setState({selectedMenuKey: "/enforcers"});
|
||||
} else if (uri.includes("/providers")) {
|
||||
this.setState({selectedMenuKey: "/providers"});
|
||||
} else if (uri.includes("/applications")) {
|
||||
this.setState({selectedMenuKey: "/applications"});
|
||||
} else if (uri.includes("/resources")) {
|
||||
this.setState({selectedMenuKey: "/resources"});
|
||||
} else if (uri.includes("/records")) {
|
||||
this.setState({selectedMenuKey: "/records"});
|
||||
} else if (uri.includes("/tokens")) {
|
||||
this.setState({selectedMenuKey: "/tokens"});
|
||||
} else if (uri.includes("/sessions")) {
|
||||
this.setState({selectedMenuKey: "/sessions"});
|
||||
} else if (uri.includes("/webhooks")) {
|
||||
this.setState({selectedMenuKey: "/webhooks"});
|
||||
} else if (uri.includes("/syncers")) {
|
||||
this.setState({selectedMenuKey: "/syncers"});
|
||||
} else if (uri.includes("/certs")) {
|
||||
this.setState({selectedMenuKey: "/certs"});
|
||||
} else if (uri.includes("/products")) {
|
||||
this.setState({selectedMenuKey: "/products"});
|
||||
} else if (uri.includes("/payments")) {
|
||||
this.setState({selectedMenuKey: "/payments"});
|
||||
} else if (uri.includes("/organizations") || uri.includes("/trees") || uri.includes("/users") || uri.includes("/groups")) {
|
||||
this.setState({selectedMenuKey: "/orgs"});
|
||||
} else if (uri.includes("/roles") || uri.includes("/permissions") || uri.includes("/models") || uri.includes("/adapters") || uri.includes("/enforcers")) {
|
||||
this.setState({selectedMenuKey: "/auth"});
|
||||
} else if (uri.includes("/applications") || uri.includes("/providers") || uri.includes("/resources") || uri.includes("/certs")) {
|
||||
this.setState({selectedMenuKey: "/identity"});
|
||||
} else if (uri.includes("/records") || uri.includes("/tokens") || uri.includes("/sessions")) {
|
||||
this.setState({selectedMenuKey: "/logs"});
|
||||
} else if (uri.includes("/products") || uri.includes("/payments") || uri.includes("/plans") || uri.includes("/pricings") || uri.includes("/subscriptions")) {
|
||||
this.setState({selectedMenuKey: "/business"});
|
||||
} else if (uri.includes("/sysinfo") || uri.includes("/syncers") || uri.includes("/webhooks")) {
|
||||
this.setState({selectedMenuKey: "/admin"});
|
||||
} else if (uri.includes("/signup")) {
|
||||
this.setState({selectedMenuKey: "/signup"});
|
||||
} else if (uri.includes("/login")) {
|
||||
this.setState({selectedMenuKey: "/login"});
|
||||
} else if (uri.includes("/result")) {
|
||||
this.setState({selectedMenuKey: "/result"});
|
||||
} else if (uri.includes("/sysinfo")) {
|
||||
this.setState({selectedMenuKey: "/sysinfo"});
|
||||
} else if (uri.includes("/subscriptions")) {
|
||||
this.setState({selectedMenuKey: "/subscriptions"});
|
||||
} else if (uri.includes("/plans")) {
|
||||
this.setState({selectedMenuKey: "/plans"});
|
||||
} else if (uri.includes("/pricings")) {
|
||||
this.setState({selectedMenuKey: "/pricings"});
|
||||
} else {
|
||||
this.setState({selectedMenuKey: -1});
|
||||
}
|
||||
@ -431,7 +401,7 @@ class App extends Component {
|
||||
return [];
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/", <HomeTwoTone />));
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
if (Conf.ShowGithubCorner) {
|
||||
@ -442,109 +412,54 @@ class App extends Component {
|
||||
</a>, "#"));
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
||||
"/organizations"));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/organizations">{i18next.t("general:User Management")}</Link>, "/orgs", <AppstoreTwoTone />, [
|
||||
Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>, "/organizations"),
|
||||
Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>, "/groups"),
|
||||
Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>, "/users"),
|
||||
]));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/groups">{i18next.t("general:Groups")}</Link>,
|
||||
"/groups"));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>,
|
||||
"/users"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>,
|
||||
"/roles"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>,
|
||||
"/permissions"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/roles">{i18next.t("general:Authorization")}</Link>, "/auth", <SafetyCertificateTwoTone />, [
|
||||
Setting.getItem(<Link to="/roles">{i18next.t("general:Roles")}</Link>, "/roles"),
|
||||
Setting.getItem(<Link to="/permissions">{i18next.t("general:Permissions")}</Link>, "/permissions"),
|
||||
Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>, "/models"),
|
||||
Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>, "/adapters"),
|
||||
Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>, "/enforcers"),
|
||||
].filter(item => {
|
||||
if (!Setting.isLocalAdminUser(this.state.account) && ["/models", "/adapters", "/enforcers"].includes(item.key)) {
|
||||
return false;
|
||||
} else {
|
||||
return true;
|
||||
}
|
||||
})));
|
||||
}
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/models">{i18next.t("general:Models")}</Link>,
|
||||
"/models"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/applications">{i18next.t("general:Identity")}</Link>, "/identity", <LockTwoTone />, [
|
||||
Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>, "/applications"),
|
||||
Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>, "/providers"),
|
||||
Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>, "/resources"),
|
||||
Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>, "/certs"),
|
||||
]));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/adapters">{i18next.t("general:Adapters")}</Link>,
|
||||
"/adapters"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/records">{i18next.t("general:Logging & Auditing")}</Link>, "/logs", <WalletTwoTone />, [
|
||||
Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>, "/records"),
|
||||
Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>, "/tokens"),
|
||||
Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>, "/sessions"),
|
||||
]));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/enforcers">{i18next.t("general:Enforcers")}</Link>,
|
||||
"/enforcers"
|
||||
));
|
||||
}
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/products">{i18next.t("general:Business & Payments")}</Link>, "/business", <DollarTwoTone />, [
|
||||
Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>, "/products"),
|
||||
Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>, "/payments"),
|
||||
Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>, "/plans"),
|
||||
Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>, "/pricings"),
|
||||
Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>, "/subscriptions"),
|
||||
]));
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/applications">{i18next.t("general:Applications")}</Link>,
|
||||
"/applications"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/providers">{i18next.t("general:Providers")}</Link>,
|
||||
"/providers"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>,
|
||||
"/resources"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/records">{i18next.t("general:Records")}</Link>,
|
||||
"/records"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/plans">{i18next.t("general:Plans")}</Link>,
|
||||
"/plans"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/pricings">{i18next.t("general:Pricings")}</Link>,
|
||||
"/pricings"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>,
|
||||
"/subscriptions"
|
||||
));
|
||||
}
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/tokens">{i18next.t("general:Tokens")}</Link>,
|
||||
"/tokens"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/sessions">{i18next.t("general:Sessions")}</Link>,
|
||||
"/sessions"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>,
|
||||
"/webhooks"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>,
|
||||
"/syncers"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/certs">{i18next.t("general:Certs")}</Link>,
|
||||
"/certs"
|
||||
));
|
||||
|
||||
if (Conf.EnableExtraPages) {
|
||||
res.push(Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>,
|
||||
"/products"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/payments">{i18next.t("general:Payments")}</Link>,
|
||||
"/payments"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>,
|
||||
"/sysinfo"
|
||||
));
|
||||
res.push(Setting.getItem(<a target="_blank" rel="noreferrer"
|
||||
href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>,
|
||||
"/swagger"
|
||||
));
|
||||
res.push(Setting.getItem(<Link style={{color: "black"}} to="/sysinfo">{i18next.t("general:Admin")}</Link>, "/admin", <SettingTwoTone />, [
|
||||
Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>, "/sysinfo"),
|
||||
Setting.getItem(<Link to="/syncers">{i18next.t("general:Syncers")}</Link>, "/syncers"),
|
||||
Setting.getItem(<Link to="/webhooks">{i18next.t("general:Webhooks")}</Link>, "/webhooks"),
|
||||
Setting.getItem(<a target="_blank" rel="noreferrer" href={Setting.isLocalhost() ? `${Setting.ServerUrl}/swagger` : "/swagger"}>{i18next.t("general:Swagger")}</a>, "/swagger")]));
|
||||
}
|
||||
|
||||
return res;
|
||||
|
@ -20,8 +20,6 @@ export const IsDemoMode = false;
|
||||
export const ForceLanguage = "";
|
||||
export const DefaultLanguage = "en";
|
||||
|
||||
export const EnableExtraPages = true;
|
||||
|
||||
export const InitThemeAlgorithm = true;
|
||||
export const ThemeDefault = {
|
||||
themeType: "default",
|
||||
|
@ -221,6 +221,7 @@ class GroupTreePage extends React.Component {
|
||||
onChange={(value) => {
|
||||
this.setState({
|
||||
organizationName: value,
|
||||
groupName: "",
|
||||
});
|
||||
this.props.history.push(`/trees/${value}`);
|
||||
}}
|
||||
|
@ -163,7 +163,7 @@ class PaymentEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input disabled={true} value={this.state.payment.organization} onChange={e => {
|
||||
<Input disabled={true} value={this.state.payment.owner} onChange={e => {
|
||||
// this.updatePaymentField('organization', e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
|
@ -115,6 +115,26 @@ class PaymentResultPage extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else if (payment.state === "Canceled") {
|
||||
return (
|
||||
<div>
|
||||
{
|
||||
Setting.renderHelmet(payment)
|
||||
}
|
||||
<Result
|
||||
status="warning"
|
||||
title={`${i18next.t("payment:The payment has been canceled")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
|
||||
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
|
||||
extra={[
|
||||
<Button type="primary" key="returnUrl" onClick={() => {
|
||||
this.goToPaymentUrl(payment);
|
||||
}}>
|
||||
{i18next.t("payment:Return to Website")}
|
||||
</Button>,
|
||||
]}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
return (
|
||||
<div>
|
||||
@ -124,7 +144,7 @@ class PaymentResultPage extends React.Component {
|
||||
<Result
|
||||
status="error"
|
||||
title={`${i18next.t("payment:The payment has failed")}: ${payment.productDisplayName}, ${i18next.t("payment:the current state is")}: ${payment.state}`}
|
||||
subTitle={i18next.t("payment:Please click the below button to return to the original website")}
|
||||
subTitle={`${i18next.t("payment:Failed reason")}: ${payment.message}`}
|
||||
extra={[
|
||||
<Button type="primary" key="returnUrl" onClick={() => {
|
||||
this.goToPaymentUrl(payment);
|
||||
|
@ -134,10 +134,14 @@ class ProviderEditPage extends React.Component {
|
||||
case "Email":
|
||||
return Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"));
|
||||
case "SMS":
|
||||
if (provider.type === "Volc Engine SMS") {
|
||||
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Access key"), i18next.t("provider:Access key - Tooltip"));
|
||||
} else if (provider.type === "Huawei Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:App key"), i18next.t("provider:App key - Tooltip"));
|
||||
} else if (provider.type === "UCloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Public key"), i18next.t("provider:Public key - Tooltip"));
|
||||
} else if (provider.type === "Msg91 SMS" || provider.type === "Infobip SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Sender Id"), i18next.t("provider:Sender Id - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client ID"), i18next.t("provider:Client ID - Tooltip"));
|
||||
}
|
||||
@ -157,10 +161,16 @@ class ProviderEditPage extends React.Component {
|
||||
case "Email":
|
||||
return Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"));
|
||||
case "SMS":
|
||||
if (provider.type === "Volc Engine SMS") {
|
||||
if (provider.type === "Volc Engine SMS" || provider.type === "Amazon SNS" || provider.type === "Baidu Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Secret access key"), i18next.t("provider:Secret access key - Tooltip"));
|
||||
} else if (provider.type === "Huawei Cloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:App secret"), i18next.t("provider:AppSecret - Tooltip"));
|
||||
} else if (provider.type === "UCloud SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Private Key"), i18next.t("provider:Private Key - Tooltip"));
|
||||
} else if (provider.type === "Msg91 SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Auth Key"), i18next.t("provider:Auth Key - Tooltip"));
|
||||
} else if (provider.type === "Infobip SMS") {
|
||||
return Setting.getLabel(i18next.t("provider:Api Key"), i18next.t("provider:Api Key - Tooltip"));
|
||||
} else {
|
||||
return Setting.getLabel(i18next.t("provider:Client secret"), i18next.t("provider:Client secret - Tooltip"));
|
||||
}
|
||||
@ -234,7 +244,7 @@ class ProviderEditPage extends React.Component {
|
||||
tooltip = i18next.t("provider:Agent ID - Tooltip");
|
||||
}
|
||||
} else if (provider.category === "SMS") {
|
||||
if (provider.type === "Twilio SMS") {
|
||||
if (provider.type === "Twilio SMS" || provider.type === "Azure ACS") {
|
||||
text = i18next.t("provider:Sender number");
|
||||
tooltip = i18next.t("provider:Sender number - Tooltip");
|
||||
} else if (provider.type === "Tencent Cloud SMS") {
|
||||
@ -246,6 +256,18 @@ class ProviderEditPage extends React.Component {
|
||||
} else if (provider.type === "Huawei Cloud SMS") {
|
||||
text = i18next.t("provider:Channel No.");
|
||||
tooltip = i18next.t("provider:Channel No. - Tooltip");
|
||||
} else if (provider.type === "Amazon SNS") {
|
||||
text = i18next.t("provider:Region");
|
||||
tooltip = i18next.t("provider:Region - Tooltip");
|
||||
} else if (provider.type === "Baidu Cloud SMS") {
|
||||
text = i18next.t("provider:Endpoint");
|
||||
tooltip = i18next.t("provider:Endpoint - Tooltip");
|
||||
} else if (provider.type === "Infobip SMS") {
|
||||
text = i18next.t("provider:Base URL");
|
||||
tooltip = i18next.t("provider:Base URL - Tooltip");
|
||||
} else if (provider.type === "UCloud SMS") {
|
||||
text = i18next.t("provider:Project Id");
|
||||
tooltip = i18next.t("provider:Project Id - Tooltip");
|
||||
}
|
||||
} else if (provider.category === "Email") {
|
||||
if (provider.type === "SUBMAIL") {
|
||||
@ -622,7 +644,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["Local File System", "MinIO", "Tencent Cloud COS"].includes(this.state.provider.type) ? null : (
|
||||
{["Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
|
||||
@ -656,7 +678,7 @@ class ProviderEditPage extends React.Component {
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{["MinIO"].includes(this.state.provider.type) ? null : (
|
||||
{["MinIO", "Google Cloud Storage", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? null : (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
|
||||
@ -668,7 +690,7 @@ class ProviderEditPage extends React.Component {
|
||||
</Col>
|
||||
</Row>
|
||||
)}
|
||||
{["AWS S3", "Tencent Cloud COS"].includes(this.state.provider.type) ? (
|
||||
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? (
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={2}>
|
||||
{Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
|
||||
@ -757,7 +779,7 @@ class ProviderEditPage extends React.Component {
|
||||
</React.Fragment>
|
||||
) : this.state.provider.category === "SMS" ? (
|
||||
<React.Fragment>
|
||||
{this.state.provider.type === "Twilio SMS" ?
|
||||
{["Twilio SMS", "Amazon SNS", "Azure ACS", "Msg91 SMS", "Infobip SMS"].includes(this.state.provider.type) ?
|
||||
null :
|
||||
(<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
@ -771,16 +793,20 @@ class ProviderEditPage extends React.Component {
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Template code"), i18next.t("provider:Template code - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.templateCode} onChange={e => {
|
||||
this.updateProviderField("templateCode", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
{["Infobip SMS"].includes(this.state.provider.type) ?
|
||||
null :
|
||||
(<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:Template code"), i18next.t("provider:Template code - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Input value={this.state.provider.templateCode} onChange={e => {
|
||||
this.updateProviderField("templateCode", e.target.value);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
)
|
||||
}
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||
{Setting.getLabel(i18next.t("provider:SMS Test"), i18next.t("provider:SMS Test - Tooltip"))} :
|
||||
|
@ -37,7 +37,7 @@ class ProviderListPage extends BaseListPage {
|
||||
|
||||
newProvider() {
|
||||
const randomName = Setting.getRandomName();
|
||||
const owner = Setting.isDefaultOrganizationSelected(this.props.account) ? this.state.owner : Setting.getRequestOrganization();
|
||||
const owner = Setting.isDefaultOrganizationSelected(this.props.account) ? this.state.owner : Setting.getRequestOrganization(this.props.account);
|
||||
return {
|
||||
owner: owner,
|
||||
name: `provider_${randomName}`,
|
||||
|
@ -85,10 +85,26 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_aliyun.png`,
|
||||
url: "https://aliyun.com/product/sms",
|
||||
},
|
||||
"Amazon SNS": {
|
||||
logo: `${StaticBaseUrl}/img/social_aws.png`,
|
||||
url: "https://aws.amazon.com/cn/sns/",
|
||||
},
|
||||
"Azure ACS": {
|
||||
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
||||
url: "https://azure.microsoft.com/en-us/products/communication-services",
|
||||
},
|
||||
"Infobip SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_infobip.png`,
|
||||
url: "https://portal.infobip.com/homepage/",
|
||||
},
|
||||
"Tencent Cloud SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_tencent_cloud.jpg`,
|
||||
url: "https://cloud.tencent.com/product/sms",
|
||||
},
|
||||
"Baidu Cloud SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_baidu_cloud.png`,
|
||||
url: "https://cloud.baidu.com/product/sms.html",
|
||||
},
|
||||
"Volc Engine SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_volc_engine.jpg`,
|
||||
url: "https://www.volcengine.com/products/cloud-sms",
|
||||
@ -97,6 +113,10 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_huawei.png`,
|
||||
url: "https://www.huaweicloud.com/product/msgsms.html",
|
||||
},
|
||||
"UCloud SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_ucloud.png`,
|
||||
url: "https://www.ucloud.cn/site/product/usms.html",
|
||||
},
|
||||
"Twilio SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_twilio.svg`,
|
||||
url: "https://www.twilio.com/messaging",
|
||||
@ -109,6 +129,10 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_submail.svg`,
|
||||
url: "https://www.mysubmail.com",
|
||||
},
|
||||
"Msg91 SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_msg91.ico`,
|
||||
url: "https://control.msg91.com/app/",
|
||||
},
|
||||
"Mock SMS": {
|
||||
logo: `${StaticBaseUrl}/img/social_default.png`,
|
||||
url: "",
|
||||
@ -153,6 +177,14 @@ export const OtherProviderInfo = {
|
||||
logo: `${StaticBaseUrl}/img/social_azure.png`,
|
||||
url: "https://azure.microsoft.com/en-us/services/storage/blobs/",
|
||||
},
|
||||
"Qiniu Cloud Kodo": {
|
||||
logo: `${StaticBaseUrl}/img/social_qiniu_cloud.png`,
|
||||
url: "https://www.qiniu.com/solutions/storage",
|
||||
},
|
||||
"Google Cloud Storage": {
|
||||
logo: `${StaticBaseUrl}/img/social_google_cloud.png`,
|
||||
url: "https://cloud.google.com/storage",
|
||||
},
|
||||
},
|
||||
SAML: {
|
||||
"Aliyun IDaaS": {
|
||||
@ -853,13 +885,19 @@ export function getProviderTypeOptions(category) {
|
||||
} else if (category === "SMS") {
|
||||
return (
|
||||
[
|
||||
{id: "Aliyun SMS", name: "Aliyun SMS"},
|
||||
{id: "Aliyun SMS", name: "Alibaba Cloud SMS"},
|
||||
{id: "Amazon SNS", name: "Amazon SNS"},
|
||||
{id: "Azure ACS", name: "Azure ACS"},
|
||||
{id: "Infobip SMS", name: "Infobip SMS"},
|
||||
{id: "Tencent Cloud SMS", name: "Tencent Cloud SMS"},
|
||||
{id: "Baidu Cloud SMS", name: "Baidu Cloud SMS"},
|
||||
{id: "Volc Engine SMS", name: "Volc Engine SMS"},
|
||||
{id: "Huawei Cloud SMS", name: "Huawei Cloud SMS"},
|
||||
{id: "UCloud SMS", name: "UCloud SMS"},
|
||||
{id: "Twilio SMS", name: "Twilio SMS"},
|
||||
{id: "SmsBao SMS", name: "SmsBao SMS"},
|
||||
{id: "SUBMAIL SMS", name: "SUBMAIL SMS"},
|
||||
{id: "Msg91 SMS", name: "Msg91 SMS"},
|
||||
]
|
||||
);
|
||||
} else if (category === "Storage") {
|
||||
@ -871,6 +909,8 @@ export function getProviderTypeOptions(category) {
|
||||
{id: "Aliyun OSS", name: "Aliyun OSS"},
|
||||
{id: "Tencent Cloud COS", name: "Tencent Cloud COS"},
|
||||
{id: "Azure Blob", name: "Azure Blob"},
|
||||
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
|
||||
{id: "Google Cloud Storage", name: "Google Cloud Storage"},
|
||||
]
|
||||
);
|
||||
} else if (category === "SAML") {
|
||||
@ -1043,13 +1083,7 @@ export function getLabel(text, tooltip) {
|
||||
}
|
||||
|
||||
export function getItem(label, key, icon, children, type) {
|
||||
return {
|
||||
key,
|
||||
icon,
|
||||
children,
|
||||
label,
|
||||
type,
|
||||
};
|
||||
return {label: label, key: key, icon: icon, children: children, type: type};
|
||||
}
|
||||
|
||||
export function getOption(label, value) {
|
||||
@ -1179,11 +1213,11 @@ export function isDefaultOrganizationSelected(account) {
|
||||
|
||||
const BuiltInObjects = [
|
||||
"api-enforcer-built-in",
|
||||
"permission-enforcer-built-in",
|
||||
"user-enforcer-built-in",
|
||||
"api-model-built-in",
|
||||
"permission-model-built-in",
|
||||
"user-model-built-in",
|
||||
"api-adapter-built-in",
|
||||
"permission-adapter-built-in",
|
||||
"user-adapter-built-in",
|
||||
];
|
||||
|
||||
export function builtInObject(obj) {
|
||||
|
@ -133,13 +133,25 @@ class UserEditPage extends React.Component {
|
||||
|
||||
this.setState({
|
||||
application: res.data,
|
||||
isGroupsVisible: res.data?.organizationObj.accountItems?.some((item) => item.name === "Groups" && item.visible),
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
getUserOrganization() {
|
||||
return this.state.organizations.filter(organization => organization.name === this.state.user.owner)[0];
|
||||
}
|
||||
|
||||
isGroupsVisible() {
|
||||
const organization = this.getUserOrganization();
|
||||
if (!organization) {
|
||||
return false;
|
||||
} else {
|
||||
return organization.accountItems?.some((item) => item.name === "Groups" && item.visible);
|
||||
}
|
||||
}
|
||||
|
||||
getGroups(organizationName) {
|
||||
if (this.state.isGroupsVisible) {
|
||||
if (this.isGroupsVisible()) {
|
||||
GroupBackend.getGroups(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@ -319,7 +331,7 @@ class UserEditPage extends React.Component {
|
||||
})}
|
||||
>
|
||||
{
|
||||
this.state.groups?.map((group) => <Option key={group.name} value={group.name}>
|
||||
this.state.groups?.map((group) => <Option key={group.name} value={`${group.owner}/${group.name}`}>
|
||||
<Space>
|
||||
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||
{group.displayName}
|
||||
@ -401,7 +413,7 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PasswordModal user={this.state.user} organization={this.state.application?.organizationObj} account={this.props.account} disabled={disabled} />
|
||||
<PasswordModal user={this.state.user} organization={this.getUserOrganization()} account={this.props.account} disabled={disabled} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
@ -442,7 +454,7 @@ class UserEditPage extends React.Component {
|
||||
onChange={(value) => {
|
||||
this.updateUserField("countryCode", value);
|
||||
}}
|
||||
countryCodes={this.state.application?.organizationObj.countryCodes}
|
||||
countryCodes={this.getUserOrganization()?.countryCodes}
|
||||
/>
|
||||
<Input value={this.state.user.phone}
|
||||
style={{width: "70%"}}
|
||||
@ -599,10 +611,10 @@ class UserEditPage extends React.Component {
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
{
|
||||
this.state.application?.organizationObj.tags?.length > 0 ? (
|
||||
this.getUserOrganization()?.tags?.length > 0 ? (
|
||||
<Select virtual={false} style={{width: "100%"}} value={this.state.user.tag}
|
||||
onChange={(value => {this.updateUserField("tag", value);})}
|
||||
options={this.state.application.organizationObj.tags?.map((tag) => {
|
||||
options={this.getUserOrganization()?.tags?.map((tag) => {
|
||||
const tokens = tag.split("|");
|
||||
const value = tokens[0];
|
||||
const displayValue = Setting.getLanguage() !== "zh" ? tokens[0] : tokens[1];
|
||||
@ -888,7 +900,7 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("mfa:Multi-factor authentication"), i18next.t("mfa:Multi-factor authentication - Tooltip "))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Card title={i18next.t("mfa:Multi-factor methods")}
|
||||
<Card size="small" title={i18next.t("mfa:Multi-factor methods")}
|
||||
extra={this.state.multiFactorAuths?.some(mfaProps => mfaProps.enabled) ?
|
||||
<PopconfirmModal
|
||||
text={i18next.t("general:Disable")}
|
||||
@ -1008,7 +1020,7 @@ class UserEditPage extends React.Component {
|
||||
</div>
|
||||
} style={(Setting.isMobile()) ? {margin: "5px"} : {}} type="inner">
|
||||
{
|
||||
this.state.application?.organizationObj.accountItems?.map(accountItem => {
|
||||
this.getUserOrganization()?.accountItems?.map(accountItem => {
|
||||
return (
|
||||
<React.Fragment key={accountItem.name}>
|
||||
{
|
||||
|
@ -76,7 +76,7 @@ class UserListPage extends BaseListPage {
|
||||
phone: Setting.getRandomNumber(),
|
||||
countryCode: this.state.organization.countryCodes?.length > 0 ? this.state.organization.countryCodes[0] : "",
|
||||
address: [],
|
||||
groups: this.props.groupName ? [this.props.groupName] : [],
|
||||
groups: this.props.groupName ? [`${owner}/${this.props.groupName}`] : [],
|
||||
affiliation: "Example Inc.",
|
||||
tag: "staff",
|
||||
region: "",
|
||||
|
@ -287,10 +287,10 @@ class PolicyTable extends React.Component {
|
||||
) : (
|
||||
<div>
|
||||
<Tooltip placement="topLeft" title="Edit">
|
||||
<Button disabled={this.state.editingIndex !== ""} style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => this.edit(record, index)} />
|
||||
<Button disabled={this.state.editingIndex !== "" || Setting.builtInObject({owner: this.props.owner, name: this.props.name})} style={{marginRight: "5px"}} icon={<EditOutlined />} size="small" onClick={() => this.edit(record, index)} />
|
||||
</Tooltip>
|
||||
<Tooltip placement="topLeft" title="Delete">
|
||||
<Button disabled={this.state.editingIndex !== ""} style={{marginRight: "5px"}} icon={<DeleteOutlined />} size="small" onClick={() => this.deletePolicy(table, index)} />
|
||||
<Button disabled={this.state.editingIndex !== "" || Setting.builtInObject({owner: this.props.owner, name: this.props.name})} style={{marginRight: "5px"}} icon={<DeleteOutlined />} size="small" onClick={() => this.deletePolicy(table, index)} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
@ -304,14 +304,14 @@ class PolicyTable extends React.Component {
|
||||
onChange: (page) => this.setState({
|
||||
page: page,
|
||||
}),
|
||||
disabled: this.state.editingIndex !== "",
|
||||
disabled: this.state.editingIndex !== "" || Setting.builtInObject({owner: this.props.owner, name: this.props.name}),
|
||||
current: this.state.page,
|
||||
}}
|
||||
columns={columns} dataSource={table} rowKey="key" size="middle" bordered
|
||||
loading={this.state.loading}
|
||||
title={() => (
|
||||
<div>
|
||||
<Button disabled={this.state.editingIndex !== ""} style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
|
||||
<Button disabled={this.state.editingIndex !== "" || Setting.builtInObject({owner: this.props.owner, name: this.props.name})} style={{marginRight: "5px"}} type="primary" size="small" onClick={() => this.addRow(table)}>{i18next.t("general:Add")}</Button>
|
||||
</div>
|
||||
)}
|
||||
/>
|
||||
|
@ -38,7 +38,7 @@ class SyncerTableColumnTable extends React.Component {
|
||||
}
|
||||
|
||||
addRow(table) {
|
||||
const row = {name: `column${table.length}`, type: "string", values: []};
|
||||
const row = {name: `column${table.length}`, type: "string", values: [], isKey: table.filter(row => row.isKey).length === 0};
|
||||
if (table === undefined) {
|
||||
table = [];
|
||||
}
|
||||
@ -107,6 +107,26 @@ class SyncerTableColumnTable extends React.Component {
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("syncer:Is key"),
|
||||
dataIndex: "isKey",
|
||||
key: "isKey",
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Switch checked={text} onChange={checked => {
|
||||
if (!record.isKey && checked) {
|
||||
table.forEach((row, i) => {
|
||||
this.updateField(table, i, "isKey", false);
|
||||
});
|
||||
} else if (record.isKey && !checked) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.updateField(table, index, "isKey", checked);
|
||||
}} />
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: i18next.t("syncer:Is hashed"),
|
||||
dataIndex: "isHashed",
|
||||
@ -133,7 +153,7 @@ class SyncerTableColumnTable extends React.Component {
|
||||
<Button style={{marginRight: "5px"}} disabled={index === table.length - 1} icon={<DownOutlined />} size="small" onClick={() => this.downRow(table, index)} />
|
||||
</Tooltip>
|
||||
<Tooltip placement="topLeft" title={i18next.t("general:Delete")}>
|
||||
<Button icon={<DeleteOutlined />} size="small" onClick={() => this.deleteRow(table, index)} />
|
||||
<Button icon={<DeleteOutlined />} disabled={record.isKey && table.length > 1} size="small" onClick={() => this.deleteRow(table, index)} />
|
||||
</Tooltip>
|
||||
</div>
|
||||
);
|
||||
|
@ -29,7 +29,20 @@
|
||||
dependencies:
|
||||
"@ctrl/tinycolor" "^3.4.0"
|
||||
|
||||
"@ant-design/cssinjs@^1", "@ant-design/cssinjs@^1.10.1", "@ant-design/cssinjs@^1.5.6", "@ant-design/cssinjs@^1.8.1":
|
||||
"@ant-design/cssinjs@1.16.1":
|
||||
version "1.16.1"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.16.1.tgz#0032044db5678dd25ac12def1abb1d52e6a4d583"
|
||||
integrity sha512-KKVB5Or6BDC1Bo3Y4KMlOkyQU0P+6GTodubrQ9YfrtXG1TgO4wpaEfg9I4ZA49R7M+Ij2KKNwb+5abvmXy6K8w==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.11.1"
|
||||
"@emotion/hash" "^0.8.0"
|
||||
"@emotion/unitless" "^0.7.5"
|
||||
classnames "^2.3.1"
|
||||
csstype "^3.0.10"
|
||||
rc-util "^5.35.0"
|
||||
stylis "^4.0.13"
|
||||
|
||||
"@ant-design/cssinjs@^1", "@ant-design/cssinjs@^1.10.1", "@ant-design/cssinjs@^1.5.6":
|
||||
version "1.10.1"
|
||||
resolved "https://registry.yarnpkg.com/@ant-design/cssinjs/-/cssinjs-1.10.1.tgz#c9173f38e3d61f0883ca3c17d7cf1e30784e0dd7"
|
||||
integrity sha512-PSoJS8RMzn95ZRg007dJGr6AU0Zim/O+tTN0xmXmh9CkIl4y3wuOr2Zhehaj7s130wPSYDVvahf3DKT50w/Zhw==
|
||||
@ -10100,6 +10113,14 @@ rc-util@^5.0.1, rc-util@^5.0.6, rc-util@^5.15.0, rc-util@^5.16.0, rc-util@^5.16.
|
||||
"@babel/runtime" "^7.18.3"
|
||||
react-is "^16.12.0"
|
||||
|
||||
rc-util@^5.35.0:
|
||||
version "5.35.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-5.35.0.tgz#bed1986248b7be525cc0894109e609ac60207f29"
|
||||
integrity sha512-MTXlixb3EoSTEchsOc7XWsVyoUQqoCsh2Z1a2IptwNgqleMF6ZgQeY52UzUbNj5CcVBg9YljOWjuOV07jSSm4Q==
|
||||
dependencies:
|
||||
"@babel/runtime" "^7.18.3"
|
||||
react-is "^16.12.0"
|
||||
|
||||
rc-virtual-list@^3.4.13, rc-virtual-list@^3.5.1, rc-virtual-list@^3.5.2:
|
||||
version "3.5.2"
|
||||
resolved "https://registry.yarnpkg.com/rc-virtual-list/-/rc-virtual-list-3.5.2.tgz#5e1028869bae900eacbae6788d4eca7210736006"
|
||||
|
Reference in New Issue
Block a user