mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-28 15:11:35 +08:00
Compare commits
59 Commits
v1.326.1
...
revert-181
Author | SHA1 | Date | |
---|---|---|---|
![]() |
138ca9d0c9 | ||
![]() |
7884e10ca3 | ||
![]() |
12dee8afd3 | ||
![]() |
ac4b870309 | ||
![]() |
b9140e2d5a | ||
![]() |
501f0dc74f | ||
![]() |
a932b76fba | ||
![]() |
0f57ac297b | ||
![]() |
edc6aa0d50 | ||
![]() |
ebc0e0f2c9 | ||
![]() |
63dd2e781e | ||
![]() |
b01ba792bb | ||
![]() |
98fb9f25b0 | ||
![]() |
cc456f265f | ||
![]() |
7058a34f87 | ||
![]() |
8e6755845f | ||
![]() |
967fa4be68 | ||
![]() |
805cf20d04 | ||
![]() |
2a8001f490 | ||
![]() |
451fc9034f | ||
![]() |
0e14a2597e | ||
![]() |
ff87c4ea33 | ||
![]() |
4f5396c70e | ||
![]() |
3c30222fce | ||
![]() |
2d04731622 | ||
![]() |
e0d2bc3dc9 | ||
![]() |
0bda29f143 | ||
![]() |
05703720c5 | ||
![]() |
cc566bf31f | ||
![]() |
e93d8c19d9 | ||
![]() |
f2e3182a69 | ||
![]() |
f934531083 | ||
![]() |
e1c0af345f | ||
![]() |
3b3bfe39f9 | ||
![]() |
18cc952f8e | ||
![]() |
43439bc8c6 | ||
![]() |
9a2800e3b3 | ||
![]() |
fdaad2b608 | ||
![]() |
2d43fe0b39 | ||
![]() |
5d776a3ce6 | ||
![]() |
5ec7a54bf8 | ||
![]() |
0c118477e8 | ||
![]() |
c858d0e0b0 | ||
![]() |
9cffb43265 | ||
![]() |
51a76518ad | ||
![]() |
08dbbab70e | ||
![]() |
0ec22ae6ff | ||
![]() |
ec3c24ba68 | ||
![]() |
ed688efdbb | ||
![]() |
06543a01d3 | ||
![]() |
70c372c3f7 | ||
![]() |
b1b3184e75 | ||
![]() |
5349fa7ff3 | ||
![]() |
9147225956 | ||
![]() |
11f3af1ede | ||
![]() |
0aa4df40c6 | ||
![]() |
7caa885131 | ||
![]() |
f4b69cad9b | ||
![]() |
fb1db7823b |
@@ -124,7 +124,6 @@ p, *, *, GET, /api/get-release, *, *
|
|||||||
p, *, *, GET, /api/get-default-application, *, *
|
p, *, *, GET, /api/get-default-application, *, *
|
||||||
p, *, *, GET, /api/get-prometheus-info, *, *
|
p, *, *, GET, /api/get-prometheus-info, *, *
|
||||||
p, *, *, *, /api/metrics, *, *
|
p, *, *, *, /api/metrics, *, *
|
||||||
p, *, *, GET, /api/get-subscriptions, *, *
|
|
||||||
p, *, *, GET, /api/get-pricing, *, *
|
p, *, *, GET, /api/get-pricing, *, *
|
||||||
p, *, *, GET, /api/get-plan, *, *
|
p, *, *, GET, /api/get-plan, *, *
|
||||||
p, *, *, GET, /api/get-organization-names, *, *
|
p, *, *, GET, /api/get-organization-names, *, *
|
||||||
|
@@ -21,4 +21,5 @@ isDemoMode = false
|
|||||||
batchSize = 100
|
batchSize = 100
|
||||||
ldapServerPort = 389
|
ldapServerPort = 389
|
||||||
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
|
||||||
logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
|
logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
|
||||||
|
initDataFile = "./init_data.json"
|
@@ -66,6 +66,8 @@ func GetConfigString(key string) string {
|
|||||||
if res == "" {
|
if res == "" {
|
||||||
if key == "staticBaseUrl" {
|
if key == "staticBaseUrl" {
|
||||||
res = "https://cdn.casbin.org"
|
res = "https://cdn.casbin.org"
|
||||||
|
} else if key == "logConfig" {
|
||||||
|
res = "{\"filename\": \"logs/casdoor.log\", \"maxdays\":99999, \"perm\":\"0770\"}"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,10 +110,10 @@ func GetLanguage(language string) string {
|
|||||||
return "en"
|
return "en"
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(language) < 2 {
|
if len(language) != 2 {
|
||||||
return "en"
|
return "en"
|
||||||
} else {
|
} else {
|
||||||
return language[0:2]
|
return language
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -140,6 +140,13 @@ func (c *ApiController) Signup() {
|
|||||||
username = id
|
username = id
|
||||||
}
|
}
|
||||||
|
|
||||||
|
password := authForm.Password
|
||||||
|
msg = object.CheckPasswordComplexityByOrg(organization, password)
|
||||||
|
if msg != "" {
|
||||||
|
c.ResponseError(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
initScore, err := organization.GetInitScore()
|
initScore, err := organization.GetInitScore()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||||
|
@@ -528,7 +528,7 @@ func (c *ApiController) Login() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
properties := map[string]string{}
|
properties := map[string]string{}
|
||||||
count, err := object.GetUserCount(application.Organization, "", "")
|
count, err := object.GetUserCount(application.Organization, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@@ -23,6 +23,13 @@ import (
|
|||||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// GetCasbinAdapters
|
||||||
|
// @Title GetCasbinAdapters
|
||||||
|
// @Tag Adapter API
|
||||||
|
// @Description get adapters
|
||||||
|
// @Param owner query string true "The owner of adapters"
|
||||||
|
// @Success 200 {array} object.Adapter The Response object
|
||||||
|
// @router /get-adapters [get]
|
||||||
func (c *ApiController) GetCasbinAdapters() {
|
func (c *ApiController) GetCasbinAdapters() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
limit := c.Input().Get("pageSize")
|
limit := c.Input().Get("pageSize")
|
||||||
@@ -31,9 +38,9 @@ func (c *ApiController) GetCasbinAdapters() {
|
|||||||
value := c.Input().Get("value")
|
value := c.Input().Get("value")
|
||||||
sortField := c.Input().Get("sortField")
|
sortField := c.Input().Get("sortField")
|
||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
organization := c.Input().Get("organization")
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
adapters, err := object.GetCasbinAdapters(owner, organization)
|
adapters, err := object.GetCasbinAdapters(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -42,14 +49,14 @@ func (c *ApiController) GetCasbinAdapters() {
|
|||||||
c.ResponseOk(adapters)
|
c.ResponseOk(adapters)
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetCasbinAdapterCount(owner, organization, field, value)
|
count, err := object.GetCasbinAdapterCount(owner, field, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
adapters, err := object.GetPaginationCasbinAdapters(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
adapters, err := object.GetPaginationCasbinAdapters(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -59,8 +66,16 @@ func (c *ApiController) GetCasbinAdapters() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetCasbinAdapter
|
||||||
|
// @Title GetCasbinAdapter
|
||||||
|
// @Tag Adapter API
|
||||||
|
// @Description get adapter
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the adapter"
|
||||||
|
// @Success 200 {object} object.Adapter The Response object
|
||||||
|
// @router /get-adapter [get]
|
||||||
func (c *ApiController) GetCasbinAdapter() {
|
func (c *ApiController) GetCasbinAdapter() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
adapter, err := object.GetCasbinAdapter(id)
|
adapter, err := object.GetCasbinAdapter(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
@@ -70,6 +85,14 @@ func (c *ApiController) GetCasbinAdapter() {
|
|||||||
c.ResponseOk(adapter)
|
c.ResponseOk(adapter)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UpdateCasbinAdapter
|
||||||
|
// @Title UpdateCasbinAdapter
|
||||||
|
// @Tag Adapter API
|
||||||
|
// @Description update adapter
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the adapter"
|
||||||
|
// @Param body body object.Adapter true "The details of the adapter"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /update-adapter [post]
|
||||||
func (c *ApiController) UpdateCasbinAdapter() {
|
func (c *ApiController) UpdateCasbinAdapter() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
@@ -84,6 +107,13 @@ func (c *ApiController) UpdateCasbinAdapter() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddCasbinAdapter
|
||||||
|
// @Title AddCasbinAdapter
|
||||||
|
// @Tag Adapter API
|
||||||
|
// @Description add adapter
|
||||||
|
// @Param body body object.Adapter true "The details of the adapter"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /add-adapter [post]
|
||||||
func (c *ApiController) AddCasbinAdapter() {
|
func (c *ApiController) AddCasbinAdapter() {
|
||||||
var casbinAdapter object.CasbinAdapter
|
var casbinAdapter object.CasbinAdapter
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
|
||||||
@@ -96,6 +126,13 @@ func (c *ApiController) AddCasbinAdapter() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DeleteCasbinAdapter
|
||||||
|
// @Title DeleteCasbinAdapter
|
||||||
|
// @Tag Adapter API
|
||||||
|
// @Description delete adapter
|
||||||
|
// @Param body body object.Adapter true "The details of the adapter"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /delete-adapter [post]
|
||||||
func (c *ApiController) DeleteCasbinAdapter() {
|
func (c *ApiController) DeleteCasbinAdapter() {
|
||||||
var casbinAdapter object.CasbinAdapter
|
var casbinAdapter object.CasbinAdapter
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
|
||||||
|
@@ -21,6 +21,16 @@ import (
|
|||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// Enforce
|
||||||
|
// @Title Enforce
|
||||||
|
// @Tag Enforce API
|
||||||
|
// @Description Call Casbin Enforce API
|
||||||
|
// @Param body body object.CasbinRequest true "Casbin request"
|
||||||
|
// @Param permissionId query string false "permission id"
|
||||||
|
// @Param modelId query string false "model id"
|
||||||
|
// @Param resourceId query string false "resource id"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /enforce [post]
|
||||||
func (c *ApiController) Enforce() {
|
func (c *ApiController) Enforce() {
|
||||||
permissionId := c.Input().Get("permissionId")
|
permissionId := c.Input().Get("permissionId")
|
||||||
modelId := c.Input().Get("modelId")
|
modelId := c.Input().Get("modelId")
|
||||||
@@ -34,33 +44,59 @@ func (c *ApiController) Enforce() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if permissionId != "" {
|
if permissionId != "" {
|
||||||
c.ResponseOk(object.Enforce(permissionId, &request))
|
enforceResult, err := object.Enforce(permissionId, &request)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res := []bool{}
|
||||||
|
res = append(res, enforceResult)
|
||||||
|
c.ResponseOk(res)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
permissions := make([]*object.Permission, 0)
|
permissions := []*object.Permission{}
|
||||||
res := []bool{}
|
|
||||||
|
|
||||||
if modelId != "" {
|
if modelId != "" {
|
||||||
owner, modelName := util.GetOwnerAndNameFromId(modelId)
|
owner, modelName := util.GetOwnerAndNameFromId(modelId)
|
||||||
permissions, err = object.GetPermissionsByModel(owner, modelName)
|
permissions, err = object.GetPermissionsByModel(owner, modelName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else if resourceId != "" {
|
||||||
permissions, err = object.GetPermissionsByResource(resourceId)
|
permissions, err = object.GetPermissionsByResource(resourceId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
c.ResponseError(c.T("general:Missing parameter"))
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res := []bool{}
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
res = append(res, object.Enforce(permission.GetId(), &request))
|
enforceResult, err := object.Enforce(permission.GetId(), &request)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res = append(res, enforceResult)
|
||||||
}
|
}
|
||||||
c.Data["json"] = res
|
c.ResponseOk(res)
|
||||||
c.ServeJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BatchEnforce
|
||||||
|
// @Title BatchEnforce
|
||||||
|
// @Tag Enforce API
|
||||||
|
// @Description Call Casbin BatchEnforce API
|
||||||
|
// @Param body body object.CasbinRequest true "array of casbin requests"
|
||||||
|
// @Param permissionId query string false "permission id"
|
||||||
|
// @Param modelId query string false "model id"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /batch-enforce [post]
|
||||||
func (c *ApiController) BatchEnforce() {
|
func (c *ApiController) BatchEnforce() {
|
||||||
permissionId := c.Input().Get("permissionId")
|
permissionId := c.Input().Get("permissionId")
|
||||||
modelId := c.Input().Get("modelId")
|
modelId := c.Input().Get("modelId")
|
||||||
@@ -68,26 +104,47 @@ func (c *ApiController) BatchEnforce() {
|
|||||||
var requests []object.CasbinRequest
|
var requests []object.CasbinRequest
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &requests)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if permissionId != "" {
|
if permissionId != "" {
|
||||||
c.Data["json"] = object.BatchEnforce(permissionId, &requests)
|
enforceResult, err := object.BatchEnforce(permissionId, &requests)
|
||||||
c.ServeJSON()
|
|
||||||
} else {
|
|
||||||
owner, modelName := util.GetOwnerAndNameFromId(modelId)
|
|
||||||
permissions, err := object.GetPermissionsByModel(owner, modelName)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
res := [][]bool{}
|
res := [][]bool{}
|
||||||
for _, permission := range permissions {
|
res = append(res, enforceResult)
|
||||||
res = append(res, object.BatchEnforce(permission.GetId(), &requests))
|
c.ResponseOk(res)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
permissions := []*object.Permission{}
|
||||||
|
if modelId != "" {
|
||||||
|
owner, modelName := util.GetOwnerAndNameFromId(modelId)
|
||||||
|
permissions, err = object.GetPermissionsByModel(owner, modelName)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
c.ResponseError(c.T("general:Missing parameter"))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
res := [][]bool{}
|
||||||
|
for _, permission := range permissions {
|
||||||
|
enforceResult, err := object.BatchEnforce(permission.GetId(), &requests)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(res)
|
res = append(res, enforceResult)
|
||||||
}
|
}
|
||||||
|
c.ResponseOk(res)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetAllObjects() {
|
func (c *ApiController) GetAllObjects() {
|
||||||
|
148
controllers/group.go
Normal file
148
controllers/group.go
Normal file
@@ -0,0 +1,148 @@
|
|||||||
|
// 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
|
||||||
|
|
||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
|
||||||
|
"github.com/beego/beego/utils/pagination"
|
||||||
|
"github.com/casdoor/casdoor/object"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// GetGroups
|
||||||
|
// @Title GetGroups
|
||||||
|
// @Tag Group API
|
||||||
|
// @Description get groups
|
||||||
|
// @Param owner query string true "The owner of groups"
|
||||||
|
// @Success 200 {array} object.Group The Response object
|
||||||
|
// @router /get-groups [get]
|
||||||
|
func (c *ApiController) GetGroups() {
|
||||||
|
owner := c.Input().Get("owner")
|
||||||
|
limit := c.Input().Get("pageSize")
|
||||||
|
page := c.Input().Get("p")
|
||||||
|
field := c.Input().Get("field")
|
||||||
|
value := c.Input().Get("value")
|
||||||
|
sortField := c.Input().Get("sortField")
|
||||||
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
withTree := c.Input().Get("withTree")
|
||||||
|
|
||||||
|
if limit == "" || page == "" {
|
||||||
|
groups, err := object.GetGroups(owner)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
if withTree == "true" {
|
||||||
|
c.ResponseOk(object.ConvertToTreeData(groups, owner))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.ResponseOk(groups)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
count, err := object.GetGroupCount(owner, field, value)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
|
groups, err := object.GetPaginationGroups(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
c.ResponseOk(groups, paginator.Nums())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetGroup
|
||||||
|
// @Title GetGroup
|
||||||
|
// @Tag Group API
|
||||||
|
// @Description get group
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the group"
|
||||||
|
// @Success 200 {object} object.Group The Response object
|
||||||
|
// @router /get-group [get]
|
||||||
|
func (c *ApiController) GetGroup() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
group, err := object.GetGroup(id)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
} else {
|
||||||
|
c.ResponseOk(group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// UpdateGroup
|
||||||
|
// @Title UpdateGroup
|
||||||
|
// @Tag Group API
|
||||||
|
// @Description update group
|
||||||
|
// @Param id query string true "The id ( owner/name ) of the group"
|
||||||
|
// @Param body body object.Group true "The details of the group"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /update-group [post]
|
||||||
|
func (c *ApiController) UpdateGroup() {
|
||||||
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
|
var group object.Group
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &group)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.UpdateGroup(id, &group))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddGroup
|
||||||
|
// @Title AddGroup
|
||||||
|
// @Tag Group API
|
||||||
|
// @Description add group
|
||||||
|
// @Param body body object.Group true "The details of the group"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /add-group [post]
|
||||||
|
func (c *ApiController) AddGroup() {
|
||||||
|
var group object.Group
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &group)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.AddGroup(&group))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
|
||||||
|
// DeleteGroup
|
||||||
|
// @Title DeleteGroup
|
||||||
|
// @Tag Group API
|
||||||
|
// @Description delete group
|
||||||
|
// @Param body body object.Group true "The details of the group"
|
||||||
|
// @Success 200 {object} controllers.Response The Response object
|
||||||
|
// @router /delete-group [post]
|
||||||
|
func (c *ApiController) DeleteGroup() {
|
||||||
|
var group object.Group
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &group)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.DeleteGroup(&group))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
@@ -34,6 +34,7 @@ import (
|
|||||||
// @router /get-messages [get]
|
// @router /get-messages [get]
|
||||||
func (c *ApiController) GetMessages() {
|
func (c *ApiController) GetMessages() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
organization := c.Input().Get("organization")
|
||||||
limit := c.Input().Get("pageSize")
|
limit := c.Input().Get("pageSize")
|
||||||
page := c.Input().Get("p")
|
page := c.Input().Get("p")
|
||||||
field := c.Input().Get("field")
|
field := c.Input().Get("field")
|
||||||
@@ -41,7 +42,7 @@ func (c *ApiController) GetMessages() {
|
|||||||
sortField := c.Input().Get("sortField")
|
sortField := c.Input().Get("sortField")
|
||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
chat := c.Input().Get("chat")
|
chat := c.Input().Get("chat")
|
||||||
organization := c.Input().Get("organization")
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
var messages []*object.Message
|
var messages []*object.Message
|
||||||
var err error
|
var err error
|
||||||
|
@@ -47,21 +47,31 @@ func (c *ApiController) GetOrganizations() {
|
|||||||
c.Data["json"] = maskedOrganizations
|
c.Data["json"] = maskedOrganizations
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
isGlobalAdmin := c.IsGlobalAdmin()
|
||||||
count, err := object.GetOrganizationCount(owner, field, value)
|
if !isGlobalAdmin {
|
||||||
if err != nil {
|
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner, c.getCurrentUser().Owner))
|
||||||
c.ResponseError(err.Error())
|
if err != nil {
|
||||||
return
|
c.ResponseError(err.Error())
|
||||||
}
|
return
|
||||||
|
}
|
||||||
|
c.ResponseOk(maskedOrganizations)
|
||||||
|
} else {
|
||||||
|
limit := util.ParseInt(limit)
|
||||||
|
count, err := object.GetOrganizationCount(owner, field, value)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
organizations, err := object.GetMaskedOrganizations(object.GetPaginationOrganizations(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(organizations, paginator.Nums())
|
c.ResponseOk(organizations, paginator.Nums())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -74,14 +84,13 @@ func (c *ApiController) GetOrganizations() {
|
|||||||
// @router /get-organization [get]
|
// @router /get-organization [get]
|
||||||
func (c *ApiController) GetOrganization() {
|
func (c *ApiController) GetOrganization() {
|
||||||
id := c.Input().Get("id")
|
id := c.Input().Get("id")
|
||||||
|
|
||||||
maskedOrganization, err := object.GetMaskedOrganization(object.GetOrganization(id))
|
maskedOrganization, err := object.GetMaskedOrganization(object.GetOrganization(id))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = maskedOrganization
|
c.ResponseOk(maskedOrganization)
|
||||||
c.ServeJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateOrganization ...
|
// UpdateOrganization ...
|
||||||
@@ -180,12 +189,12 @@ func (c *ApiController) GetDefaultApplication() {
|
|||||||
// @Title GetOrganizationNames
|
// @Title GetOrganizationNames
|
||||||
// @Tag Organization API
|
// @Tag Organization API
|
||||||
// @Param owner query string true "owner"
|
// @Param owner query string true "owner"
|
||||||
// @Description get all organization names
|
// @Description get all organization name and displayName
|
||||||
// @Success 200 {array} object.Organization The Response object
|
// @Success 200 {array} object.Organization The Response object
|
||||||
// @router /get-organization-names [get]
|
// @router /get-organization-names [get]
|
||||||
func (c *ApiController) GetOrganizationNames() {
|
func (c *ApiController) GetOrganizationNames() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
organizationNames, err := object.GetOrganizationsByFields(owner, "name")
|
organizationNames, err := object.GetOrganizationsByFields(owner, []string{"name", "display_name"}...)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@@ -31,6 +31,7 @@ import (
|
|||||||
// @router /get-payments [get]
|
// @router /get-payments [get]
|
||||||
func (c *ApiController) GetPayments() {
|
func (c *ApiController) GetPayments() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
organization := c.Input().Get("organization")
|
||||||
limit := c.Input().Get("pageSize")
|
limit := c.Input().Get("pageSize")
|
||||||
page := c.Input().Get("p")
|
page := c.Input().Get("p")
|
||||||
field := c.Input().Get("field")
|
field := c.Input().Get("field")
|
||||||
@@ -48,13 +49,13 @@ func (c *ApiController) GetPayments() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetPaymentCount(owner, field, value)
|
count, err := object.GetPaymentCount(owner, organization, field, value)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
payments, err := object.GetPaginationPayments(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
payments, err := object.GetPaginationPayments(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
@@ -177,10 +178,11 @@ func (c *ApiController) NotifyPayment() {
|
|||||||
providerName := c.Ctx.Input.Param(":provider")
|
providerName := c.Ctx.Input.Param(":provider")
|
||||||
productName := c.Ctx.Input.Param(":product")
|
productName := c.Ctx.Input.Param(":product")
|
||||||
paymentName := c.Ctx.Input.Param(":payment")
|
paymentName := c.Ctx.Input.Param(":payment")
|
||||||
|
orderId := c.Ctx.Input.Param("order")
|
||||||
|
|
||||||
body := c.Ctx.Input.RequestBody
|
body := c.Ctx.Input.RequestBody
|
||||||
|
|
||||||
err, errorResponse := object.NotifyPayment(c.Ctx.Request, body, owner, providerName, productName, paymentName)
|
err, errorResponse := object.NotifyPayment(c.Ctx.Request, body, owner, providerName, productName, paymentName, orderId)
|
||||||
|
|
||||||
_, err2 := c.Ctx.ResponseWriter.Write([]byte(errorResponse))
|
_, err2 := c.Ctx.ResponseWriter.Write([]byte(errorResponse))
|
||||||
if err2 != nil {
|
if err2 != nil {
|
||||||
|
@@ -180,11 +180,11 @@ func (c *ApiController) BuyProduct() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
payUrl, err := object.BuyProduct(id, providerName, user, host)
|
payUrl, orderId, err := object.BuyProduct(id, providerName, user, host)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.ResponseOk(payUrl)
|
c.ResponseOk(payUrl, orderId)
|
||||||
}
|
}
|
||||||
|
@@ -49,8 +49,7 @@ func (c *ApiController) GetProviders() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.GetMaskedProviders(providers, isMaskEnabled)
|
c.ResponseOk(object.GetMaskedProviders(providers, isMaskEnabled))
|
||||||
c.ServeJSON()
|
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetProviderCount(owner, field, value)
|
count, err := object.GetProviderCount(owner, field, value)
|
||||||
@@ -96,8 +95,7 @@ func (c *ApiController) GetGlobalProviders() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.GetMaskedProviders(globalProviders, isMaskEnabled)
|
c.ResponseOk(object.GetMaskedProviders(globalProviders, isMaskEnabled))
|
||||||
c.ServeJSON()
|
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetGlobalProviderCount(field, value)
|
count, err := object.GetGlobalProviderCount(field, value)
|
||||||
@@ -138,8 +136,7 @@ func (c *ApiController) GetProvider() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Data["json"] = object.GetMaskedProvider(provider, isMaskEnabled)
|
c.ResponseOk(object.GetMaskedProvider(provider, isMaskEnabled))
|
||||||
c.ServeJSON()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// UpdateProvider
|
// UpdateProvider
|
||||||
|
@@ -139,8 +139,9 @@ func (c *ApiController) DeleteResource() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, _, ok := c.GetProviderFromContext("Storage")
|
provider, err := c.GetProviderFromContext("Storage")
|
||||||
if !ok {
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -187,8 +188,9 @@ func (c *ApiController) UploadResource() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, _, ok := c.GetProviderFromContext("Storage")
|
provider, err := c.GetProviderFromContext("Storage")
|
||||||
if !ok {
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -69,9 +69,9 @@ func (c *ApiController) SendEmail() {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
|
// called by Casdoor SDK via Client ID & Client Secret, so the used Email provider will be the application' Email provider or the default Email provider
|
||||||
var ok bool
|
provider, err = c.GetProviderFromContext("Email")
|
||||||
provider, _, ok = c.GetProviderFromContext("Email")
|
if err != nil {
|
||||||
if !ok {
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -127,13 +127,14 @@ func (c *ApiController) SendEmail() {
|
|||||||
// @Success 200 {object} Response object
|
// @Success 200 {object} Response object
|
||||||
// @router /api/send-sms [post]
|
// @router /api/send-sms [post]
|
||||||
func (c *ApiController) SendSms() {
|
func (c *ApiController) SendSms() {
|
||||||
provider, _, ok := c.GetProviderFromContext("SMS")
|
provider, err := c.GetProviderFromContext("SMS")
|
||||||
if !ok {
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
var smsForm SmsForm
|
var smsForm SmsForm
|
||||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &smsForm)
|
err = json.Unmarshal(c.Ctx.Input.RequestBody, &smsForm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
|
@@ -80,6 +80,7 @@ func (c *ApiController) GetGlobalUsers() {
|
|||||||
// @router /get-users [get]
|
// @router /get-users [get]
|
||||||
func (c *ApiController) GetUsers() {
|
func (c *ApiController) GetUsers() {
|
||||||
owner := c.Input().Get("owner")
|
owner := c.Input().Get("owner")
|
||||||
|
groupId := c.Input().Get("groupId")
|
||||||
limit := c.Input().Get("pageSize")
|
limit := c.Input().Get("pageSize")
|
||||||
page := c.Input().Get("p")
|
page := c.Input().Get("p")
|
||||||
field := c.Input().Get("field")
|
field := c.Input().Get("field")
|
||||||
@@ -88,6 +89,16 @@ func (c *ApiController) GetUsers() {
|
|||||||
sortOrder := c.Input().Get("sortOrder")
|
sortOrder := c.Input().Get("sortOrder")
|
||||||
|
|
||||||
if limit == "" || page == "" {
|
if limit == "" || page == "" {
|
||||||
|
if groupId != "" {
|
||||||
|
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupId))
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
c.ResponseOk(maskedUsers)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
|
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -97,14 +108,14 @@ func (c *ApiController) GetUsers() {
|
|||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
} else {
|
} else {
|
||||||
limit := util.ParseInt(limit)
|
limit := util.ParseInt(limit)
|
||||||
count, err := object.GetUserCount(owner, field, value)
|
count, err := object.GetUserCount(owner, field, value, groupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||||
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -287,7 +298,7 @@ func (c *ApiController) AddUser() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := object.GetUserCount("", "", "")
|
count, err := object.GetUserCount("", "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.ResponseError(err.Error())
|
c.ResponseError(err.Error())
|
||||||
return
|
return
|
||||||
@@ -399,10 +410,6 @@ func (c *ApiController) SetPassword() {
|
|||||||
c.ResponseError(c.T("user:New password cannot contain blank space."))
|
c.ResponseError(c.T("user:New password cannot contain blank space."))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(newPassword) <= 5 {
|
|
||||||
c.ResponseError(c.T("user:New password must have at least 6 characters"))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
userId := util.GetId(userOwner, userName)
|
userId := util.GetId(userOwner, userName)
|
||||||
|
|
||||||
@@ -437,6 +444,12 @@ func (c *ApiController) SetPassword() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg := object.CheckPasswordComplexity(targetUser, newPassword)
|
||||||
|
if msg != "" {
|
||||||
|
c.ResponseError(msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
targetUser.Password = newPassword
|
targetUser.Password = newPassword
|
||||||
_, err = object.SetUserField(targetUser, "password", targetUser.Password)
|
_, err = object.SetUserField(targetUser, "password", targetUser.Password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -505,7 +518,7 @@ func (c *ApiController) GetUserCount() {
|
|||||||
var count int64
|
var count int64
|
||||||
var err error
|
var err error
|
||||||
if isOnline == "" {
|
if isOnline == "" {
|
||||||
count, err = object.GetUserCount(owner, "", "")
|
count, err = object.GetUserCount(owner, "", "", "")
|
||||||
} else {
|
} else {
|
||||||
count, err = object.GetOnlineUserCount(owner, util.ParseInt(isOnline))
|
count, err = object.GetOnlineUserCount(owner, util.ParseInt(isOnline))
|
||||||
}
|
}
|
||||||
@@ -517,3 +530,34 @@ func (c *ApiController) GetUserCount() {
|
|||||||
c.Data["json"] = count
|
c.Data["json"] = count
|
||||||
c.ServeJSON()
|
c.ServeJSON()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// AddUserkeys
|
||||||
|
// @Title AddUserkeys
|
||||||
|
// @router /add-user-keys [post]
|
||||||
|
// @Tag User API
|
||||||
|
func (c *ApiController) AddUserkeys() {
|
||||||
|
var user object.User
|
||||||
|
err := json.Unmarshal(c.Ctx.Input.RequestBody, &user)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isAdmin := c.IsAdmin()
|
||||||
|
affected, err := object.AddUserkeys(&user, isAdmin)
|
||||||
|
if err != nil {
|
||||||
|
c.ResponseError(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
c.ResponseOk(affected)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c *ApiController) RemoveUserFromGroup() {
|
||||||
|
owner := c.Ctx.Request.Form.Get("owner")
|
||||||
|
name := c.Ctx.Request.Form.Get("name")
|
||||||
|
groupId := c.Ctx.Request.Form.Get("groupId")
|
||||||
|
|
||||||
|
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupId))
|
||||||
|
c.ServeJSON()
|
||||||
|
}
|
||||||
|
@@ -139,47 +139,46 @@ func (c *ApiController) IsMaskedEnabled() (bool, bool) {
|
|||||||
return true, isMaskEnabled
|
return true, isMaskEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, *object.User, bool) {
|
func (c *ApiController) GetProviderFromContext(category string) (*object.Provider, error) {
|
||||||
providerName := c.Input().Get("provider")
|
providerName := c.Input().Get("provider")
|
||||||
if providerName != "" {
|
if providerName != "" {
|
||||||
provider, err := object.GetProvider(util.GetId("admin", providerName))
|
provider, err := object.GetProvider(util.GetId("admin", providerName))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("util:The provider: %s is not found"), providerName))
|
err = fmt.Errorf(c.T("util:The provider: %s is not found"), providerName)
|
||||||
return nil, nil, false
|
return nil, err
|
||||||
}
|
}
|
||||||
return provider, nil, true
|
|
||||||
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
userId, ok := c.RequireSignedIn()
|
userId, ok := c.RequireSignedIn()
|
||||||
if !ok {
|
if !ok {
|
||||||
return nil, nil, false
|
return nil, fmt.Errorf(c.T("general:Please login first"))
|
||||||
}
|
}
|
||||||
|
|
||||||
application, user, err := object.GetApplicationByUserId(userId)
|
application, err := object.GetApplicationByUserId(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if application == nil {
|
if application == nil {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("util:No application is found for userId: %s"), userId))
|
return nil, fmt.Errorf(c.T("util:No application is found for userId: %s"), userId)
|
||||||
return nil, nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := application.GetProviderByCategory(category)
|
provider, err := application.GetProviderByCategory(category)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if provider == nil {
|
if provider == nil {
|
||||||
c.ResponseError(fmt.Sprintf(c.T("util:No provider for category: %s is found for application: %s"), category, application.Name))
|
return nil, fmt.Errorf(c.T("util:No provider for category: %s is found for application: %s"), category, application.Name)
|
||||||
return nil, nil, false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider, user, true
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func checkQuotaForApplication(count int) error {
|
func checkQuotaForApplication(count int) error {
|
||||||
|
1
go.mod
1
go.mod
@@ -59,6 +59,7 @@ require (
|
|||||||
github.com/tealeg/xlsx v1.0.5
|
github.com/tealeg/xlsx v1.0.5
|
||||||
github.com/thanhpk/randstr v1.0.4
|
github.com/thanhpk/randstr v1.0.4
|
||||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||||
|
github.com/xorm-io/builder v0.3.13 // indirect
|
||||||
github.com/xorm-io/core v0.7.4
|
github.com/xorm-io/core v0.7.4
|
||||||
github.com/xorm-io/xorm v1.1.6
|
github.com/xorm-io/xorm v1.1.6
|
||||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Fehlender Parameter",
|
"Missing parameter": "Fehlender Parameter",
|
||||||
"Please login first": "Bitte zuerst einloggen",
|
"Please login first": "Bitte zuerst einloggen",
|
||||||
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
|
"The user: %s doesn't exist": "Der Benutzer %s existiert nicht",
|
||||||
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:"
|
"don't support captchaProvider: ": "Unterstütze captchaProvider nicht:",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "Es gibt einen LDAP-Server"
|
"Ldap server exist": "Es gibt einen LDAP-Server"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "Anzeigename darf nicht leer sein",
|
"Display name cannot be empty": "Anzeigename darf nicht leer sein",
|
||||||
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten.",
|
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten."
|
||||||
"New password must have at least 6 characters": "Das neue Passwort muss mindestens 6 Zeichen haben"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Fehler beim Importieren von Benutzern"
|
"Failed to import users": "Fehler beim Importieren von Benutzern"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Missing parameter",
|
"Missing parameter": "Missing parameter",
|
||||||
"Please login first": "Please login first",
|
"Please login first": "Please login first",
|
||||||
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||||
"don't support captchaProvider: ": "don't support captchaProvider: "
|
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "Ldap server exist"
|
"Ldap server exist": "Ldap server exist"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "Display name cannot be empty",
|
"Display name cannot be empty": "Display name cannot be empty",
|
||||||
"New password cannot contain blank space.": "New password cannot contain blank space.",
|
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||||
"New password must have at least 6 characters": "New password must have at least 6 characters"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Failed to import users"
|
"Failed to import users": "Failed to import users"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Parámetro faltante",
|
"Missing parameter": "Parámetro faltante",
|
||||||
"Please login first": "Por favor, inicia sesión primero",
|
"Please login first": "Por favor, inicia sesión primero",
|
||||||
"The user: %s doesn't exist": "El usuario: %s no existe",
|
"The user: %s doesn't exist": "El usuario: %s no existe",
|
||||||
"don't support captchaProvider: ": "No apoyo a captchaProvider"
|
"don't support captchaProvider: ": "No apoyo a captchaProvider",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "El servidor LDAP existe"
|
"Ldap server exist": "El servidor LDAP existe"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "El nombre de pantalla no puede estar vacío",
|
"Display name cannot be empty": "El nombre de pantalla no puede estar vacío",
|
||||||
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco.",
|
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco."
|
||||||
"New password must have at least 6 characters": "La nueva contraseña debe tener al menos 6 caracteres"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Error al importar usuarios"
|
"Failed to import users": "Error al importar usuarios"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Paramètre manquant",
|
"Missing parameter": "Paramètre manquant",
|
||||||
"Please login first": "Veuillez d'abord vous connecter",
|
"Please login first": "Veuillez d'abord vous connecter",
|
||||||
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
|
"The user: %s doesn't exist": "L'utilisateur : %s n'existe pas",
|
||||||
"don't support captchaProvider: ": "Ne pas prendre en charge la captchaProvider"
|
"don't support captchaProvider: ": "Ne pas prendre en charge la captchaProvider",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "Le serveur LDAP existe"
|
"Ldap server exist": "Le serveur LDAP existe"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "Le nom d'affichage ne peut pas être vide",
|
"Display name cannot be empty": "Le nom d'affichage ne peut pas être vide",
|
||||||
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace.",
|
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace."
|
||||||
"New password must have at least 6 characters": "Le nouveau mot de passe doit comporter au moins 6 caractères"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Échec de l'importation des utilisateurs"
|
"Failed to import users": "Échec de l'importation des utilisateurs"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Parameter hilang",
|
"Missing parameter": "Parameter hilang",
|
||||||
"Please login first": "Silahkan login terlebih dahulu",
|
"Please login first": "Silahkan login terlebih dahulu",
|
||||||
"The user: %s doesn't exist": "Pengguna: %s tidak ada",
|
"The user: %s doesn't exist": "Pengguna: %s tidak ada",
|
||||||
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:"
|
"don't support captchaProvider: ": "Jangan mendukung captchaProvider:",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "Server ldap ada"
|
"Ldap server exist": "Server ldap ada"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "Nama tampilan tidak boleh kosong",
|
"Display name cannot be empty": "Nama tampilan tidak boleh kosong",
|
||||||
"New password cannot contain blank space.": "Kata sandi baru tidak boleh mengandung spasi kosong.",
|
"New password cannot contain blank space.": "Kata sandi baru tidak boleh mengandung spasi kosong."
|
||||||
"New password must have at least 6 characters": "Kata sandi baru harus memiliki setidaknya 6 karakter"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Gagal mengimpor pengguna"
|
"Failed to import users": "Gagal mengimpor pengguna"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "不足しているパラメーター",
|
"Missing parameter": "不足しているパラメーター",
|
||||||
"Please login first": "最初にログインしてください",
|
"Please login first": "最初にログインしてください",
|
||||||
"The user: %s doesn't exist": "そのユーザー:%sは存在しません",
|
"The user: %s doesn't exist": "そのユーザー:%sは存在しません",
|
||||||
"don't support captchaProvider: ": "captchaProviderをサポートしないでください"
|
"don't support captchaProvider: ": "captchaProviderをサポートしないでください",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "LDAPサーバーは存在します"
|
"Ldap server exist": "LDAPサーバーは存在します"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "表示名は空にできません",
|
"Display name cannot be empty": "表示名は空にできません",
|
||||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。",
|
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。"
|
||||||
"New password must have at least 6 characters": "新しいパスワードは少なくとも6文字必要です"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "ユーザーのインポートに失敗しました"
|
"Failed to import users": "ユーザーのインポートに失敗しました"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "누락된 매개변수",
|
"Missing parameter": "누락된 매개변수",
|
||||||
"Please login first": "먼저 로그인 하십시오",
|
"Please login first": "먼저 로그인 하십시오",
|
||||||
"The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
|
"The user: %s doesn't exist": "사용자 %s는 존재하지 않습니다",
|
||||||
"don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요"
|
"don't support captchaProvider: ": "CaptchaProvider를 지원하지 마세요",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "LDAP 서버가 존재합니다"
|
"Ldap server exist": "LDAP 서버가 존재합니다"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다",
|
"Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다",
|
||||||
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다.",
|
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다."
|
||||||
"New password must have at least 6 characters": "새로운 비밀번호는 최소 6자 이상이어야 합니다"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "사용자 가져오기를 실패했습니다"
|
"Failed to import users": "사용자 가져오기를 실패했습니다"
|
||||||
|
149
i18n/locales/pt/data.json
Normal file
149
i18n/locales/pt/data.json
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
{
|
||||||
|
"account": {
|
||||||
|
"Failed to add user": "Failed to add user",
|
||||||
|
"Get init score failed, error: %w": "Get init score failed, error: %w",
|
||||||
|
"Please sign out first": "Please sign out first",
|
||||||
|
"The application does not allow to sign up new account": "The application does not allow to sign up new account"
|
||||||
|
},
|
||||||
|
"auth": {
|
||||||
|
"Challenge method should be S256": "Challenge method should be S256",
|
||||||
|
"Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
|
||||||
|
"Failed to login in: %s": "Failed to login in: %s",
|
||||||
|
"Invalid token": "Invalid token",
|
||||||
|
"State expected: %s, but got: %s": "State expected: %s, but got: %s",
|
||||||
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
|
||||||
|
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
|
||||||
|
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
|
||||||
|
"The application: %s does not exist": "The application: %s does not exist",
|
||||||
|
"The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
|
||||||
|
"The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
|
||||||
|
"Unauthorized operation": "Unauthorized operation",
|
||||||
|
"Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s"
|
||||||
|
},
|
||||||
|
"cas": {
|
||||||
|
"Service %s and %s do not match": "Service %s and %s do not match"
|
||||||
|
},
|
||||||
|
"chat": {
|
||||||
|
"The chat type must be \\\"AI\\\"": "The chat type must be \\\"AI\\\"",
|
||||||
|
"The chat: %s is not found": "The chat: %s is not found",
|
||||||
|
"The message is invalid": "The message is invalid",
|
||||||
|
"The message: %s is not found": "The message: %s is not found",
|
||||||
|
"The provider: %s is invalid": "The provider: %s is invalid",
|
||||||
|
"The provider: %s is not found": "The provider: %s is not found"
|
||||||
|
},
|
||||||
|
"check": {
|
||||||
|
"Affiliation cannot be blank": "Affiliation cannot be blank",
|
||||||
|
"DisplayName cannot be blank": "DisplayName cannot be blank",
|
||||||
|
"DisplayName is not valid real name": "DisplayName is not valid real name",
|
||||||
|
"Email already exists": "Email already exists",
|
||||||
|
"Email cannot be empty": "Email cannot be empty",
|
||||||
|
"Email is invalid": "Email is invalid",
|
||||||
|
"Empty username.": "Empty username.",
|
||||||
|
"FirstName cannot be blank": "FirstName cannot be blank",
|
||||||
|
"LDAP user name or password incorrect": "LDAP user name or password incorrect",
|
||||||
|
"LastName cannot be blank": "LastName cannot be blank",
|
||||||
|
"Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
|
||||||
|
"Organization does not exist": "Organization does not exist",
|
||||||
|
"Password must have at least 6 characters": "Password must have at least 6 characters",
|
||||||
|
"Phone already exists": "Phone already exists",
|
||||||
|
"Phone cannot be empty": "Phone cannot be empty",
|
||||||
|
"Phone number is invalid": "Phone number is invalid",
|
||||||
|
"Session outdated, please login again": "Session outdated, please login again",
|
||||||
|
"The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
|
||||||
|
"The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
|
||||||
|
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
|
||||||
|
"Username already exists": "Username already exists",
|
||||||
|
"Username cannot be an email address": "Username cannot be an email address",
|
||||||
|
"Username cannot contain white spaces": "Username cannot contain white spaces",
|
||||||
|
"Username cannot start with a digit": "Username cannot start with a digit",
|
||||||
|
"Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
|
||||||
|
"Username must have at least 2 characters": "Username must have at least 2 characters",
|
||||||
|
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
|
||||||
|
"Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
|
||||||
|
"password or code is incorrect": "password or code is incorrect",
|
||||||
|
"password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
|
||||||
|
"unsupported password type: %s": "unsupported password type: %s"
|
||||||
|
},
|
||||||
|
"general": {
|
||||||
|
"Missing parameter": "Missing parameter",
|
||||||
|
"Please login first": "Please login first",
|
||||||
|
"The user: %s doesn't exist": "The user: %s doesn't exist",
|
||||||
|
"don't support captchaProvider: ": "don't support captchaProvider: ",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
|
},
|
||||||
|
"ldap": {
|
||||||
|
"Ldap server exist": "Ldap server exist"
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"Please link first": "Please link first",
|
||||||
|
"This application has no providers": "This application has no providers",
|
||||||
|
"This application has no providers of type": "This application has no providers of type",
|
||||||
|
"This provider can't be unlinked": "This provider can't be unlinked",
|
||||||
|
"You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
|
||||||
|
"You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
|
||||||
|
},
|
||||||
|
"organization": {
|
||||||
|
"Only admin can modify the %s.": "Only admin can modify the %s.",
|
||||||
|
"The %s is immutable.": "The %s is immutable.",
|
||||||
|
"Unknown modify rule %s.": "Unknown modify rule %s."
|
||||||
|
},
|
||||||
|
"provider": {
|
||||||
|
"Invalid application id": "Invalid application id",
|
||||||
|
"the provider: %s does not exist": "the provider: %s does not exist"
|
||||||
|
},
|
||||||
|
"resource": {
|
||||||
|
"User is nil for tag: avatar": "User is nil for tag: avatar",
|
||||||
|
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
|
||||||
|
},
|
||||||
|
"saml": {
|
||||||
|
"Application %s not found": "Application %s not found"
|
||||||
|
},
|
||||||
|
"saml_sp": {
|
||||||
|
"provider %s's category is not SAML": "provider %s's category is not SAML"
|
||||||
|
},
|
||||||
|
"service": {
|
||||||
|
"Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
|
||||||
|
"Invalid Email receivers: %s": "Invalid Email receivers: %s",
|
||||||
|
"Invalid phone receivers: %s": "Invalid phone receivers: %s"
|
||||||
|
},
|
||||||
|
"storage": {
|
||||||
|
"The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
|
||||||
|
"The provider type: %s is not supported": "The provider type: %s is not supported"
|
||||||
|
},
|
||||||
|
"token": {
|
||||||
|
"Empty clientId or clientSecret": "Empty clientId or clientSecret",
|
||||||
|
"Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
|
||||||
|
"Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
|
||||||
|
"Invalid client_id": "Invalid client_id",
|
||||||
|
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
|
||||||
|
"Token not found, invalid accessToken": "Token not found, invalid accessToken"
|
||||||
|
},
|
||||||
|
"user": {
|
||||||
|
"Display name cannot be empty": "Display name cannot be empty",
|
||||||
|
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||||
|
},
|
||||||
|
"user_upload": {
|
||||||
|
"Failed to import users": "Failed to import users"
|
||||||
|
},
|
||||||
|
"util": {
|
||||||
|
"No application is found for userId: %s": "No application is found for userId: %s",
|
||||||
|
"No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
|
||||||
|
"The provider: %s is not found": "The provider: %s is not found"
|
||||||
|
},
|
||||||
|
"verification": {
|
||||||
|
"Code has not been sent yet!": "Code has not been sent yet!",
|
||||||
|
"Invalid captcha provider.": "Invalid captcha provider.",
|
||||||
|
"Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
|
||||||
|
"Turing test failed.": "Turing test failed.",
|
||||||
|
"Unable to get the email modify rule.": "Unable to get the email modify rule.",
|
||||||
|
"Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
|
||||||
|
"Unknown type": "Unknown type",
|
||||||
|
"Wrong verification code!": "Wrong verification code!",
|
||||||
|
"You should verify your code in %d min!": "You should verify your code in %d min!",
|
||||||
|
"the user does not exist, please sign up first": "the user does not exist, please sign up first"
|
||||||
|
},
|
||||||
|
"webauthn": {
|
||||||
|
"Found no credentials for this user": "Found no credentials for this user",
|
||||||
|
"Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
|
||||||
|
}
|
||||||
|
}
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Отсутствующий параметр",
|
"Missing parameter": "Отсутствующий параметр",
|
||||||
"Please login first": "Пожалуйста, сначала войдите в систему",
|
"Please login first": "Пожалуйста, сначала войдите в систему",
|
||||||
"The user: %s doesn't exist": "Пользователь %s не существует",
|
"The user: %s doesn't exist": "Пользователь %s не существует",
|
||||||
"don't support captchaProvider: ": "не поддерживайте captchaProvider:"
|
"don't support captchaProvider: ": "не поддерживайте captchaProvider:",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "LDAP-сервер существует"
|
"Ldap server exist": "LDAP-сервер существует"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "Отображаемое имя не может быть пустым",
|
"Display name cannot be empty": "Отображаемое имя не может быть пустым",
|
||||||
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы.",
|
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы."
|
||||||
"New password must have at least 6 characters": "Новый пароль должен содержать не менее 6 символов"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Не удалось импортировать пользователей"
|
"Failed to import users": "Не удалось импортировать пользователей"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "Thiếu tham số",
|
"Missing parameter": "Thiếu tham số",
|
||||||
"Please login first": "Vui lòng đăng nhập trước",
|
"Please login first": "Vui lòng đăng nhập trước",
|
||||||
"The user: %s doesn't exist": "Người dùng: %s không tồn tại",
|
"The user: %s doesn't exist": "Người dùng: %s không tồn tại",
|
||||||
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: "
|
"don't support captchaProvider: ": "không hỗ trợ captchaProvider: ",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "Máy chủ LDAP tồn tại"
|
"Ldap server exist": "Máy chủ LDAP tồn tại"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "Tên hiển thị không thể trống",
|
"Display name cannot be empty": "Tên hiển thị không thể trống",
|
||||||
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng.",
|
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng."
|
||||||
"New password must have at least 6 characters": "Mật khẩu mới phải có ít nhất 6 ký tự"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "Không thể nhập người dùng"
|
"Failed to import users": "Không thể nhập người dùng"
|
||||||
|
@@ -68,7 +68,8 @@
|
|||||||
"Missing parameter": "缺少参数",
|
"Missing parameter": "缺少参数",
|
||||||
"Please login first": "请先登录",
|
"Please login first": "请先登录",
|
||||||
"The user: %s doesn't exist": "用户: %s不存在",
|
"The user: %s doesn't exist": "用户: %s不存在",
|
||||||
"don't support captchaProvider: ": "不支持验证码提供商: "
|
"don't support captchaProvider: ": "不支持验证码提供商: ",
|
||||||
|
"this operation is not allowed in demo mode": "this operation is not allowed in demo mode"
|
||||||
},
|
},
|
||||||
"ldap": {
|
"ldap": {
|
||||||
"Ldap server exist": "LDAP服务器已存在"
|
"Ldap server exist": "LDAP服务器已存在"
|
||||||
@@ -119,8 +120,7 @@
|
|||||||
},
|
},
|
||||||
"user": {
|
"user": {
|
||||||
"Display name cannot be empty": "显示名称不可为空",
|
"Display name cannot be empty": "显示名称不可为空",
|
||||||
"New password cannot contain blank space.": "新密码不可以包含空格",
|
"New password cannot contain blank space.": "新密码不可以包含空格"
|
||||||
"New password must have at least 6 characters": "新密码至少需要6位字符"
|
|
||||||
},
|
},
|
||||||
"user_upload": {
|
"user_upload": {
|
||||||
"Failed to import users": "导入用户失败"
|
"Failed to import users": "导入用户失败"
|
||||||
|
@@ -8,6 +8,7 @@
|
|||||||
"favicon": "",
|
"favicon": "",
|
||||||
"passwordType": "plain",
|
"passwordType": "plain",
|
||||||
"passwordSalt": "",
|
"passwordSalt": "",
|
||||||
|
"passwordOptions": ["AtLeast6"],
|
||||||
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"],
|
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"],
|
||||||
"defaultAvatar": "",
|
"defaultAvatar": "",
|
||||||
"defaultApplication": "",
|
"defaultApplication": "",
|
||||||
|
11
main.go
11
main.go
@@ -30,13 +30,18 @@ import (
|
|||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func getCreateDatabaseFlag() bool {
|
||||||
createDatabase := flag.Bool("createDatabase", false, "true if you need Casdoor to create database")
|
res := flag.Bool("createDatabase", false, "true if you need Casdoor to create database")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
return *res
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
createDatabase := getCreateDatabaseFlag()
|
||||||
|
|
||||||
object.InitAdapter()
|
object.InitAdapter()
|
||||||
|
object.CreateTables(createDatabase)
|
||||||
object.DoMigration()
|
object.DoMigration()
|
||||||
object.CreateTables(*createDatabase)
|
|
||||||
|
|
||||||
object.InitDb()
|
object.InitDb()
|
||||||
object.InitFromFile()
|
object.InitFromFile()
|
||||||
|
@@ -41,18 +41,26 @@ func InitConfig() {
|
|||||||
beego.BConfig.WebConfig.Session.SessionOn = true
|
beego.BConfig.WebConfig.Session.SessionOn = true
|
||||||
|
|
||||||
InitAdapter()
|
InitAdapter()
|
||||||
DoMigration()
|
|
||||||
CreateTables(true)
|
CreateTables(true)
|
||||||
|
DoMigration()
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitAdapter() {
|
func InitAdapter() {
|
||||||
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
|
adapter = NewAdapter(conf.GetConfigString("driverName"), conf.GetConfigDataSourceName(), conf.GetConfigString("dbName"))
|
||||||
|
|
||||||
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
|
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, tableNamePrefix)
|
||||||
|
adapter.Engine.SetTableMapper(tbMapper)
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateTables(createDatabase bool) {
|
func CreateTables(createDatabase bool) {
|
||||||
if createDatabase {
|
if createDatabase {
|
||||||
adapter.CreateDatabase()
|
err := adapter.CreateDatabase()
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
adapter.createTable()
|
adapter.createTable()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -122,10 +130,6 @@ func (a *Adapter) createTable() {
|
|||||||
showSql := conf.GetConfigBool("showSql")
|
showSql := conf.GetConfigBool("showSql")
|
||||||
a.Engine.ShowSQL(showSql)
|
a.Engine.ShowSQL(showSql)
|
||||||
|
|
||||||
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
|
||||||
tbMapper := core.NewPrefixMapper(core.SnakeMapper{}, tableNamePrefix)
|
|
||||||
a.Engine.SetTableMapper(tbMapper)
|
|
||||||
|
|
||||||
err := a.Engine.Sync2(new(Organization))
|
err := a.Engine.Sync2(new(Organization))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -136,6 +140,16 @@ func (a *Adapter) createTable() {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(Group))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = a.Engine.Sync2(new(UserGroupRelation))
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
err = a.Engine.Sync2(new(Role))
|
err = a.Engine.Sync2(new(Role))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -280,3 +294,52 @@ func GetSession(owner string, offset, limit int, field, value, sortField, sortOr
|
|||||||
}
|
}
|
||||||
return session
|
return session
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetSessionForUser(owner string, offset, limit int, field, value, sortField, sortOrder string) *xorm.Session {
|
||||||
|
session := adapter.Engine.Prepare()
|
||||||
|
if offset != -1 && limit != -1 {
|
||||||
|
session.Limit(limit, offset)
|
||||||
|
}
|
||||||
|
if owner != "" {
|
||||||
|
if offset == -1 {
|
||||||
|
session = session.And("owner=?", owner)
|
||||||
|
} else {
|
||||||
|
session = session.And("a.owner=?", owner)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if field != "" && value != "" {
|
||||||
|
if filterField(field) {
|
||||||
|
if offset != -1 {
|
||||||
|
field = fmt.Sprintf("a.%s", field)
|
||||||
|
}
|
||||||
|
session = session.And(fmt.Sprintf("%s like ?", util.SnakeString(field)), fmt.Sprintf("%%%s%%", value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if sortField == "" || sortOrder == "" {
|
||||||
|
sortField = "created_time"
|
||||||
|
}
|
||||||
|
|
||||||
|
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
|
||||||
|
tableName := tableNamePrefix + "user"
|
||||||
|
if offset == -1 {
|
||||||
|
if sortOrder == "ascend" {
|
||||||
|
session = session.Asc(util.SnakeString(sortField))
|
||||||
|
} else {
|
||||||
|
session = session.Desc(util.SnakeString(sortField))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if sortOrder == "ascend" {
|
||||||
|
session = session.Alias("a").
|
||||||
|
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
|
||||||
|
Select("b.*").
|
||||||
|
Asc("a." + util.SnakeString(sortField))
|
||||||
|
} else {
|
||||||
|
session = session.Alias("a").
|
||||||
|
Join("INNER", []string{tableName, "b"}, "a.owner = b.owner and a.name = b.name").
|
||||||
|
Select("b.*").
|
||||||
|
Desc("a." + util.SnakeString(sortField))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return session
|
||||||
|
}
|
||||||
|
@@ -235,16 +235,16 @@ func GetApplicationByUser(user *User) (*Application, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetApplicationByUserId(userId string) (application *Application, user *User, err error) {
|
func GetApplicationByUserId(userId string) (application *Application, err error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(userId)
|
owner, name := util.GetOwnerAndNameFromId(userId)
|
||||||
if owner == "app" {
|
if owner == "app" {
|
||||||
application, err = getApplication("admin", name)
|
application, err = getApplication("admin", name)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user, err = GetUser(userId)
|
user, err := GetUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
application, err = GetApplicationByUser(user)
|
application, err = GetApplicationByUser(user)
|
||||||
return
|
return
|
||||||
|
@@ -1,167 +0,0 @@
|
|||||||
// 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 object
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/md5"
|
|
||||||
"fmt"
|
|
||||||
"image"
|
|
||||||
"image/color"
|
|
||||||
"image/png"
|
|
||||||
"io"
|
|
||||||
"net/http"
|
|
||||||
"strings"
|
|
||||||
|
|
||||||
"github.com/fogleman/gg"
|
|
||||||
)
|
|
||||||
|
|
||||||
func hasGravatar(client *http.Client, email string) (bool, error) {
|
|
||||||
// Clean and lowercase the email
|
|
||||||
email = strings.TrimSpace(strings.ToLower(email))
|
|
||||||
|
|
||||||
// Generate MD5 hash of the email
|
|
||||||
hash := md5.New()
|
|
||||||
io.WriteString(hash, email)
|
|
||||||
hashedEmail := fmt.Sprintf("%x", hash.Sum(nil))
|
|
||||||
|
|
||||||
// Create Gravatar URL with d=404 parameter
|
|
||||||
gravatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s?d=404", hashedEmail)
|
|
||||||
|
|
||||||
// Send a request to Gravatar
|
|
||||||
req, err := http.NewRequest("GET", gravatarURL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
// Check if the user has a custom Gravatar image
|
|
||||||
if resp.StatusCode == http.StatusOK {
|
|
||||||
return true, nil
|
|
||||||
} else if resp.StatusCode == http.StatusNotFound {
|
|
||||||
return false, nil
|
|
||||||
} else {
|
|
||||||
return false, fmt.Errorf("failed to fetch gravatar image: %s", resp.Status)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getGravatarFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
|
||||||
// Clean and lowercase the email
|
|
||||||
email = strings.TrimSpace(strings.ToLower(email))
|
|
||||||
|
|
||||||
// Generate MD5 hash of the email
|
|
||||||
hash := md5.New()
|
|
||||||
io.WriteString(hash, email)
|
|
||||||
hashedEmail := fmt.Sprintf("%x", hash.Sum(nil))
|
|
||||||
|
|
||||||
// Create Gravatar URL
|
|
||||||
gravatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s", hashedEmail)
|
|
||||||
|
|
||||||
// Download the image
|
|
||||||
req, err := http.NewRequest("GET", gravatarURL, nil)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
resp, err := client.Do(req)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
defer resp.Body.Close()
|
|
||||||
|
|
||||||
if resp.StatusCode != http.StatusOK {
|
|
||||||
return nil, "", fmt.Errorf("failed to download gravatar image: %s", resp.Status)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the content type and determine the file extension
|
|
||||||
contentType := resp.Header.Get("Content-Type")
|
|
||||||
fileExtension := ""
|
|
||||||
switch contentType {
|
|
||||||
case "image/jpeg":
|
|
||||||
fileExtension = ".jpg"
|
|
||||||
case "image/png":
|
|
||||||
fileExtension = ".png"
|
|
||||||
case "image/gif":
|
|
||||||
fileExtension = ".gif"
|
|
||||||
default:
|
|
||||||
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save the image to a bytes.Buffer
|
|
||||||
buffer := &bytes.Buffer{}
|
|
||||||
_, err = io.Copy(buffer, resp.Body)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer, fileExtension, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getColor(data []byte) color.RGBA {
|
|
||||||
r := int(data[0]) % 256
|
|
||||||
g := int(data[1]) % 256
|
|
||||||
b := int(data[2]) % 256
|
|
||||||
return color.RGBA{uint8(r), uint8(g), uint8(b), 255}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getIdenticonFileBuffer(username string) (*bytes.Buffer, string, error) {
|
|
||||||
username = strings.TrimSpace(strings.ToLower(username))
|
|
||||||
|
|
||||||
hash := md5.New()
|
|
||||||
io.WriteString(hash, username)
|
|
||||||
hashedUsername := hash.Sum(nil)
|
|
||||||
|
|
||||||
// Define the size of the image
|
|
||||||
const imageSize = 420
|
|
||||||
const cellSize = imageSize / 7
|
|
||||||
|
|
||||||
// Create a new image
|
|
||||||
img := image.NewRGBA(image.Rect(0, 0, imageSize, imageSize))
|
|
||||||
|
|
||||||
// Create a context
|
|
||||||
dc := gg.NewContextForRGBA(img)
|
|
||||||
|
|
||||||
// Set a background color
|
|
||||||
dc.SetColor(color.RGBA{240, 240, 240, 255})
|
|
||||||
dc.Clear()
|
|
||||||
|
|
||||||
// Get avatar color
|
|
||||||
avatarColor := getColor(hashedUsername)
|
|
||||||
|
|
||||||
// Draw cells
|
|
||||||
for i := 0; i < 7; i++ {
|
|
||||||
for j := 0; j < 7; j++ {
|
|
||||||
if (hashedUsername[i] >> uint(j) & 1) == 1 {
|
|
||||||
dc.SetColor(avatarColor)
|
|
||||||
dc.DrawRectangle(float64(j*cellSize), float64(i*cellSize), float64(cellSize), float64(cellSize))
|
|
||||||
dc.Fill()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save image to a bytes.Buffer
|
|
||||||
buffer := &bytes.Buffer{}
|
|
||||||
err := png.Encode(buffer, img)
|
|
||||||
if err != nil {
|
|
||||||
return nil, "", fmt.Errorf("failed to save image: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
return buffer, ".png", nil
|
|
||||||
}
|
|
@@ -30,9 +30,8 @@ type CasbinAdapter struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
|
||||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
Type string `xorm:"varchar(100)" json:"type"`
|
Model string `xorm:"varchar(100)" json:"model"`
|
||||||
Model string `xorm:"varchar(100)" json:"model"`
|
|
||||||
|
|
||||||
Host string `xorm:"varchar(100)" json:"host"`
|
Host string `xorm:"varchar(100)" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
@@ -46,14 +45,14 @@ type CasbinAdapter struct {
|
|||||||
Adapter *xormadapter.Adapter `xorm:"-" json:"-"`
|
Adapter *xormadapter.Adapter `xorm:"-" json:"-"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCasbinAdapterCount(owner, organization, field, value string) (int64, error) {
|
func GetCasbinAdapterCount(owner, field, value string) (int64, error) {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
return session.Count(&CasbinAdapter{Organization: organization})
|
return session.Count(&CasbinAdapter{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetCasbinAdapters(owner string, organization string) ([]*CasbinAdapter, error) {
|
func GetCasbinAdapters(owner string) ([]*CasbinAdapter, error) {
|
||||||
adapters := []*CasbinAdapter{}
|
adapters := []*CasbinAdapter{}
|
||||||
err := adapter.Engine.Where("owner = ? and organization = ?", owner, organization).Find(&adapters)
|
err := adapter.Engine.Desc("created_time").Find(&adapters, &CasbinAdapter{Owner: owner})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return adapters, err
|
return adapters, err
|
||||||
}
|
}
|
||||||
@@ -61,10 +60,10 @@ func GetCasbinAdapters(owner string, organization string) ([]*CasbinAdapter, err
|
|||||||
return adapters, nil
|
return adapters, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaginationCasbinAdapters(owner, organization string, page, limit int, field, value, sort, order string) ([]*CasbinAdapter, error) {
|
func GetPaginationCasbinAdapters(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*CasbinAdapter, error) {
|
||||||
session := GetSession(owner, page, limit, field, value, sort, order)
|
|
||||||
adapters := []*CasbinAdapter{}
|
adapters := []*CasbinAdapter{}
|
||||||
err := session.Find(&adapters, &CasbinAdapter{Organization: organization})
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
err := session.Find(&adapters)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return adapters, err
|
return adapters, err
|
||||||
}
|
}
|
||||||
@@ -214,6 +213,10 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) ([]*xormadapter.CasbinRule, erro
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if modelObj == nil {
|
||||||
|
return nil, fmt.Errorf("The model: %s does not exist", util.GetId(casbinAdapter.Owner, casbinAdapter.Model))
|
||||||
|
}
|
||||||
|
|
||||||
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
enforcer, err := initEnforcer(modelObj, casbinAdapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
@@ -203,6 +203,16 @@ func CheckPassword(user *User, password string, lang string, options ...bool) st
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func CheckPasswordComplexityByOrg(organization *Organization, password string) string {
|
||||||
|
errorMsg := checkPasswordComplexity(password, organization.PasswordOptions)
|
||||||
|
return errorMsg
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckPasswordComplexity(user *User, password string) string {
|
||||||
|
organization, _ := GetOrganizationByUser(user)
|
||||||
|
return CheckPasswordComplexityByOrg(organization, password)
|
||||||
|
}
|
||||||
|
|
||||||
func checkLdapUserPassword(user *User, password string, lang string) string {
|
func checkLdapUserPassword(user *User, password string, lang string) string {
|
||||||
ldaps, err := GetLdaps(user.Owner)
|
ldaps, err := GetLdaps(user.Owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -353,7 +363,7 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
|
|||||||
|
|
||||||
allowed := true
|
allowed := true
|
||||||
for _, permission := range permissions {
|
for _, permission := range permissions {
|
||||||
if !permission.IsEnabled || len(permission.Users) == 0 {
|
if !permission.IsEnabled {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
98
object/check_password_complexity.go
Normal file
98
object/check_password_complexity.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ValidatorFunc func(password string) string
|
||||||
|
|
||||||
|
var (
|
||||||
|
regexLowerCase = regexp.MustCompile(`[a-z]`)
|
||||||
|
regexUpperCase = regexp.MustCompile(`[A-Z]`)
|
||||||
|
regexDigit = regexp.MustCompile(`\d`)
|
||||||
|
regexSpecial = regexp.MustCompile(`[!@#$%^&*]`)
|
||||||
|
)
|
||||||
|
|
||||||
|
func isValidOption_AtLeast6(password string) string {
|
||||||
|
if len(password) < 6 {
|
||||||
|
return "The password must have at least 6 characters"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidOption_AtLeast8(password string) string {
|
||||||
|
if len(password) < 8 {
|
||||||
|
return "The password must have at least 8 characters"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidOption_Aa123(password string) string {
|
||||||
|
hasLowerCase := regexLowerCase.MatchString(password)
|
||||||
|
hasUpperCase := regexUpperCase.MatchString(password)
|
||||||
|
hasDigit := regexDigit.MatchString(password)
|
||||||
|
|
||||||
|
if !hasLowerCase || !hasUpperCase || !hasDigit {
|
||||||
|
return "The password must contain at least one uppercase letter, one lowercase letter and one digit"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidOption_SpecialChar(password string) string {
|
||||||
|
if !regexSpecial.MatchString(password) {
|
||||||
|
return "The password must contain at least one special character"
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func isValidOption_NoRepeat(password string) string {
|
||||||
|
for i := 0; i < len(password)-1; i++ {
|
||||||
|
if password[i] == password[i+1] {
|
||||||
|
return "The password must not contain any repeated characters"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
func checkPasswordComplexity(password string, options []string) string {
|
||||||
|
if len(password) == 0 {
|
||||||
|
return "Please input your password!"
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(options) == 0 {
|
||||||
|
options = []string{"AtLeast6"}
|
||||||
|
}
|
||||||
|
|
||||||
|
checkers := map[string]ValidatorFunc{
|
||||||
|
"AtLeast6": isValidOption_AtLeast6,
|
||||||
|
"AtLeast8": isValidOption_AtLeast8,
|
||||||
|
"Aa123": isValidOption_Aa123,
|
||||||
|
"SpecialChar": isValidOption_SpecialChar,
|
||||||
|
"NoRepeat": isValidOption_NoRepeat,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, option := range options {
|
||||||
|
checkerFunc, ok := checkers[option]
|
||||||
|
if ok {
|
||||||
|
errorMsg := checkerFunc(password)
|
||||||
|
if errorMsg != "" {
|
||||||
|
return errorMsg
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
228
object/group.go
Normal file
228
object/group.go
Normal file
@@ -0,0 +1,228 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/xorm-io/core"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Group struct {
|
||||||
|
Owner string `xorm:"varchar(100) notnull pk" json:"owner"`
|
||||||
|
Name string `xorm:"varchar(100) notnull pk unique" json:"name"`
|
||||||
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
|
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||||
|
|
||||||
|
Id string `xorm:"varchar(100) not null index" json:"id"`
|
||||||
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Manager string `xorm:"varchar(100)" json:"manager"`
|
||||||
|
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
|
||||||
|
Type string `xorm:"varchar(100)" json:"type"`
|
||||||
|
ParentId string `xorm:"varchar(100)" json:"parentId"`
|
||||||
|
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
|
||||||
|
Users *[]string `xorm:"-" json:"users"`
|
||||||
|
|
||||||
|
Title string `json:"title,omitempty"`
|
||||||
|
Key string `json:"key,omitempty"`
|
||||||
|
Children []*Group `json:"children,omitempty"`
|
||||||
|
|
||||||
|
IsEnabled bool `json:"isEnabled"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type GroupNode struct{}
|
||||||
|
|
||||||
|
func GetGroupCount(owner, field, value string) (int64, error) {
|
||||||
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
|
count, err := session.Count(&Group{})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroups(owner string) ([]*Group, error) {
|
||||||
|
groups := []*Group{}
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&groups, &Group{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationGroups(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Group, error) {
|
||||||
|
groups := []*Group{}
|
||||||
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
err := session.Find(&groups)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroup(owner string, name string) (*Group, error) {
|
||||||
|
if owner == "" || name == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
group := Group{Owner: owner, Name: name}
|
||||||
|
existed, err := adapter.Engine.Get(&group)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &group, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGroupById(id string) (*Group, error) {
|
||||||
|
if id == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
group := Group{Id: id}
|
||||||
|
existed, err := adapter.Engine.Get(&group)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &group, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroup(id string) (*Group, error) {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
return getGroup(owner, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func UpdateGroup(id string, group *Group) (bool, error) {
|
||||||
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
|
oldGroup, err := getGroup(owner, name)
|
||||||
|
if oldGroup == nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
group.UpdatedTime = util.GetCurrentTime()
|
||||||
|
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(group)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddGroup(group *Group) (bool, error) {
|
||||||
|
if group.Id == "" {
|
||||||
|
group.Id = util.GenerateId()
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := adapter.Engine.Insert(group)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddGroups(groups []*Group) (bool, error) {
|
||||||
|
if len(groups) == 0 {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
affected, err := adapter.Engine.Insert(groups)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func DeleteGroup(group *Group) (bool, error) {
|
||||||
|
_, err := adapter.Engine.Get(group)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if count, err := adapter.Engine.Where("parent_id = ?", group.Id).Count(&Group{}); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if count > 0 {
|
||||||
|
return false, errors.New("group has children group")
|
||||||
|
}
|
||||||
|
|
||||||
|
if count, err := GetGroupUserCount(group.GetId(), "", ""); err != nil {
|
||||||
|
return false, err
|
||||||
|
} else if count > 0 {
|
||||||
|
return false, errors.New("group has users")
|
||||||
|
}
|
||||||
|
|
||||||
|
session := adapter.Engine.NewSession()
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
if err := session.Begin(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if _, err := session.Delete(&UserGroupRelation{GroupId: group.Id}); err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := session.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := session.Commit(); err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected != 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (group *Group) GetId() string {
|
||||||
|
return fmt.Sprintf("%s/%s", group.Owner, group.Name)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||||
|
treeData := []*Group{}
|
||||||
|
|
||||||
|
for _, group := range groups {
|
||||||
|
if group.ParentId == parentId {
|
||||||
|
node := &Group{
|
||||||
|
Title: group.DisplayName,
|
||||||
|
Key: group.Name,
|
||||||
|
Type: group.Type,
|
||||||
|
Owner: group.Owner,
|
||||||
|
Id: group.Id,
|
||||||
|
}
|
||||||
|
children := ConvertToTreeData(groups, group.Id)
|
||||||
|
if len(children) > 0 {
|
||||||
|
node.Children = children
|
||||||
|
}
|
||||||
|
treeData = append(treeData, node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return treeData
|
||||||
|
}
|
@@ -61,6 +61,7 @@ func getBuiltInAccountItems() []*AccountItem {
|
|||||||
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
{Name: "Signup application", Visible: true, ViewRule: "Public", ModifyRule: "Admin"},
|
||||||
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
{Name: "Roles", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||||
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
{Name: "Permissions", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||||
|
{Name: "Groups", Visible: true, ViewRule: "Public", ModifyRule: "Immutable"},
|
||||||
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "3rd-party logins", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
|
{Name: "Properties", Visible: false, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||||
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
{Name: "Is admin", Visible: true, ViewRule: "Admin", ModifyRule: "Admin"},
|
||||||
@@ -91,6 +92,7 @@ func initBuiltInOrganization() bool {
|
|||||||
WebsiteUrl: "https://example.com",
|
WebsiteUrl: "https://example.com",
|
||||||
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
|
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
|
||||||
PasswordType: "plain",
|
PasswordType: "plain",
|
||||||
|
PasswordOptions: []string{"AtLeast6"},
|
||||||
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
|
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
|
||||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
||||||
Tags: []string{},
|
Tags: []string{},
|
||||||
|
@@ -14,7 +14,10 @@
|
|||||||
|
|
||||||
package object
|
package object
|
||||||
|
|
||||||
import "github.com/casdoor/casdoor/util"
|
import (
|
||||||
|
"github.com/casdoor/casdoor/conf"
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
)
|
||||||
|
|
||||||
type InitData struct {
|
type InitData struct {
|
||||||
Organizations []*Organization `json:"organizations"`
|
Organizations []*Organization `json:"organizations"`
|
||||||
@@ -35,7 +38,12 @@ type InitData struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func InitFromFile() {
|
func InitFromFile() {
|
||||||
initData, err := readInitDataFromFile("./init_data.json")
|
initDataFile := conf.GetConfigString("initDataFile")
|
||||||
|
if initDataFile == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
initData, err := readInitDataFromFile(initDataFile)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -22,20 +22,13 @@ import (
|
|||||||
type Migrator_1_314_0_PR_1841 struct{}
|
type Migrator_1_314_0_PR_1841 struct{}
|
||||||
|
|
||||||
func (*Migrator_1_314_0_PR_1841) IsMigrationNeeded() bool {
|
func (*Migrator_1_314_0_PR_1841) IsMigrationNeeded() bool {
|
||||||
users := []*User{}
|
count, err := adapter.Engine.Where("password_type=?", "").Count(&User{})
|
||||||
|
|
||||||
err := adapter.Engine.Table("user").Find(&users)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
// table doesn't exist
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, u := range users {
|
return count > 100
|
||||||
if u.PasswordType != "" {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (*Migrator_1_314_0_PR_1841) DoMigration() *migrate.Migration {
|
func (*Migrator_1_314_0_PR_1841) DoMigration() *migrate.Migration {
|
||||||
|
@@ -27,6 +27,7 @@ type Model struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
|
|
||||||
ModelText string `xorm:"mediumtext" json:"modelText"`
|
ModelText string `xorm:"mediumtext" json:"modelText"`
|
||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
|
@@ -22,6 +22,7 @@ import (
|
|||||||
"github.com/casdoor/casdoor/cred"
|
"github.com/casdoor/casdoor/cred"
|
||||||
"github.com/casdoor/casdoor/i18n"
|
"github.com/casdoor/casdoor/i18n"
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/xorm-io/builder"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -55,6 +56,7 @@ type Organization struct {
|
|||||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||||
|
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
||||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||||
@@ -75,11 +77,18 @@ func GetOrganizationCount(owner, field, value string) (int64, error) {
|
|||||||
return session.Count(&Organization{})
|
return session.Count(&Organization{})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetOrganizations(owner string) ([]*Organization, error) {
|
func GetOrganizations(owner string, name ...string) ([]*Organization, error) {
|
||||||
organizations := []*Organization{}
|
organizations := []*Organization{}
|
||||||
err := adapter.Engine.Desc("created_time").Find(&organizations, &Organization{Owner: owner})
|
if name != nil && len(name) > 0 {
|
||||||
if err != nil {
|
err := adapter.Engine.Desc("created_time").Where(builder.In("name", name)).Find(&organizations)
|
||||||
return nil, err
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
err := adapter.Engine.Desc("created_time").Find(&organizations, &Organization{Owner: owner})
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return organizations, nil
|
return organizations, nil
|
||||||
@@ -334,6 +343,13 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
group := new(Group)
|
||||||
|
group.Owner = newName
|
||||||
|
_, err = session.Where("owner=?", oldName).Update(group)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
role := new(Role)
|
role := new(Role)
|
||||||
_, err = adapter.Engine.Where("owner=?", oldName).Get(role)
|
_, err = adapter.Engine.Where("owner=?", oldName).Get(role)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -386,7 +402,6 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
|||||||
|
|
||||||
casbinAdapter := new(CasbinAdapter)
|
casbinAdapter := new(CasbinAdapter)
|
||||||
casbinAdapter.Owner = newName
|
casbinAdapter.Owner = newName
|
||||||
casbinAdapter.Organization = newName
|
|
||||||
_, err = session.Where("owner=?", oldName).Update(casbinAdapter)
|
_, err = session.Where("owner=?", oldName).Update(casbinAdapter)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@@ -56,9 +56,9 @@ type Payment struct {
|
|||||||
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
|
InvoiceUrl string `xorm:"varchar(255)" json:"invoiceUrl"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaymentCount(owner, field, value string) (int64, error) {
|
func GetPaymentCount(owner, organization, field, value string) (int64, error) {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
return session.Count(&Payment{})
|
return session.Count(&Payment{Organization: organization})
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPayments(owner string) ([]*Payment, error) {
|
func GetPayments(owner string) ([]*Payment, error) {
|
||||||
@@ -81,10 +81,10 @@ func GetUserPayments(owner string, organization string, user string) ([]*Payment
|
|||||||
return payments, nil
|
return payments, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaginationPayments(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*Payment, error) {
|
func GetPaginationPayments(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Payment, error) {
|
||||||
payments := []*Payment{}
|
payments := []*Payment{}
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&payments)
|
err := session.Find(&payments, &Payment{Organization: organization})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -149,7 +149,7 @@ func DeletePayment(payment *Payment) (bool, error) {
|
|||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func notifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string) (*Payment, error, string) {
|
func notifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string, orderId string) (*Payment, error, string) {
|
||||||
provider, err := getProvider(owner, providerName)
|
provider, err := getProvider(owner, providerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
@@ -180,7 +180,7 @@ func notifyPayment(request *http.Request, body []byte, owner string, providerNam
|
|||||||
return payment, err, pProvider.GetResponseError(err)
|
return payment, err, pProvider.GetResponseError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
productDisplayName, paymentName, price, productName, providerName, err := pProvider.Notify(request, body, cert.AuthorityPublicKey)
|
productDisplayName, paymentName, price, productName, providerName, err := pProvider.Notify(request, body, cert.AuthorityPublicKey, orderId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return payment, err, pProvider.GetResponseError(err)
|
return payment, err, pProvider.GetResponseError(err)
|
||||||
}
|
}
|
||||||
@@ -199,8 +199,8 @@ func notifyPayment(request *http.Request, body []byte, owner string, providerNam
|
|||||||
return payment, err, pProvider.GetResponseError(err)
|
return payment, err, pProvider.GetResponseError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
func NotifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string) (error, string) {
|
func NotifyPayment(request *http.Request, body []byte, owner string, providerName string, productName string, paymentName string, orderId string) (error, string) {
|
||||||
payment, err, errorResponse := notifyPayment(request, body, owner, providerName, productName, paymentName)
|
payment, err, errorResponse := notifyPayment(request, body, owner, providerName, productName, paymentName, orderId)
|
||||||
if payment != nil {
|
if payment != nil {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
payment.State = "Error"
|
payment.State = "Error"
|
||||||
|
@@ -27,6 +27,7 @@ type Permission struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
|
|
||||||
Users []string `xorm:"mediumtext" json:"users"`
|
Users []string `xorm:"mediumtext" json:"users"`
|
||||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||||
@@ -263,18 +264,48 @@ func DeletePermission(permission *Permission) (bool, error) {
|
|||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPermissionsByUser(userId string) ([]*Permission, error) {
|
func GetPermissionsAndRolesByUser(userId string) ([]*Permission, []*Role, error) {
|
||||||
permissions := []*Permission{}
|
permissions := []*Permission{}
|
||||||
err := adapter.Engine.Where("users like ?", "%"+userId+"\"%").Find(&permissions)
|
err := adapter.Engine.Where("users like ?", "%"+userId+"\"%").Find(&permissions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return permissions, err
|
return nil, nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range permissions {
|
existedPerms := map[string]struct{}{}
|
||||||
permissions[i].Users = nil
|
|
||||||
|
for _, perm := range permissions {
|
||||||
|
perm.Users = nil
|
||||||
|
|
||||||
|
if _, ok := existedPerms[perm.Name]; !ok {
|
||||||
|
existedPerms[perm.Name] = struct{}{}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return permissions, nil
|
permFromRoles := []*Permission{}
|
||||||
|
|
||||||
|
roles, err := GetRolesByUser(userId)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range roles {
|
||||||
|
perms := []*Permission{}
|
||||||
|
err := adapter.Engine.Where("roles like ?", "%"+role.Name+"\"%").Find(&perms)
|
||||||
|
if err != nil {
|
||||||
|
return nil, nil, err
|
||||||
|
}
|
||||||
|
permFromRoles = append(permFromRoles, perms...)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, perm := range permFromRoles {
|
||||||
|
perm.Users = nil
|
||||||
|
if _, ok := existedPerms[perm.Name]; !ok {
|
||||||
|
existedPerms[perm.Name] = struct{}{}
|
||||||
|
permissions = append(permissions, perm)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return permissions, roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPermissionsByRole(roleId string) ([]*Permission, error) {
|
func GetPermissionsByRole(roleId string) ([]*Permission, error) {
|
||||||
|
@@ -241,37 +241,33 @@ func removePolicies(permission *Permission) {
|
|||||||
|
|
||||||
type CasbinRequest = []interface{}
|
type CasbinRequest = []interface{}
|
||||||
|
|
||||||
func Enforce(permissionId string, request *CasbinRequest) bool {
|
func Enforce(permissionId string, request *CasbinRequest) (bool, error) {
|
||||||
permission, err := GetPermission(permissionId)
|
permission, err := GetPermission(permissionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
enforcer := getEnforcer(permission)
|
enforcer := getEnforcer(permission)
|
||||||
|
return enforcer.Enforce(*request...)
|
||||||
allow, err := enforcer.Enforce(*request...)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return allow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func BatchEnforce(permissionId string, requests *[]CasbinRequest) []bool {
|
func BatchEnforce(permissionId string, requests *[]CasbinRequest) ([]bool, error) {
|
||||||
permission, err := GetPermission(permissionId)
|
permission, err := GetPermission(permissionId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
res := []bool{}
|
||||||
|
for i := 0; i < len(*requests); i++ {
|
||||||
|
res = append(res, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
return res, err
|
||||||
}
|
}
|
||||||
|
|
||||||
enforcer := getEnforcer(permission)
|
enforcer := getEnforcer(permission)
|
||||||
allow, err := enforcer.BatchEnforce(*requests)
|
return enforcer.BatchEnforce(*requests)
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
return allow
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) []string {
|
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) []string {
|
||||||
permissions, err := GetPermissionsByUser(userId)
|
permissions, _, err := GetPermissionsAndRolesByUser(userId)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -31,7 +31,6 @@ type Pricing struct {
|
|||||||
|
|
||||||
Plans []string `xorm:"mediumtext" json:"plans"`
|
Plans []string `xorm:"mediumtext" json:"plans"`
|
||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
HasTrial bool `json:"hasTrial"`
|
|
||||||
TrialDuration int `json:"trialDuration"`
|
TrialDuration int `json:"trialDuration"`
|
||||||
Application string `xorm:"varchar(100)" json:"application"`
|
Application string `xorm:"varchar(100)" json:"application"`
|
||||||
|
|
||||||
|
@@ -156,24 +156,24 @@ func (product *Product) getProvider(providerId string) (*Provider, error) {
|
|||||||
return provider, nil
|
return provider, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func BuyProduct(id string, providerName string, user *User, host string) (string, error) {
|
func BuyProduct(id string, providerName string, user *User, host string) (string, string, error) {
|
||||||
product, err := GetProduct(id)
|
product, err := GetProduct(id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if product == nil {
|
if product == nil {
|
||||||
return "", fmt.Errorf("the product: %s does not exist", id)
|
return "", "", fmt.Errorf("the product: %s does not exist", id)
|
||||||
}
|
}
|
||||||
|
|
||||||
provider, err := product.getProvider(providerName)
|
provider, err := product.getProvider(providerName)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
pProvider, _, err := provider.getPaymentProvider()
|
pProvider, _, err := provider.getPaymentProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
owner := product.Owner
|
owner := product.Owner
|
||||||
@@ -186,9 +186,9 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
|
|||||||
returnUrl := fmt.Sprintf("%s/payments/%s/result", originFrontend, paymentName)
|
returnUrl := fmt.Sprintf("%s/payments/%s/result", originFrontend, paymentName)
|
||||||
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s/%s/%s", originBackend, owner, providerName, productName, paymentName)
|
notifyUrl := fmt.Sprintf("%s/api/notify-payment/%s/%s/%s/%s", originBackend, owner, providerName, productName, paymentName)
|
||||||
|
|
||||||
payUrl, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, returnUrl, notifyUrl)
|
payUrl, orderId, err := pProvider.Pay(providerName, productName, payerName, paymentName, productDisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
payment := Payment{
|
payment := Payment{
|
||||||
@@ -210,16 +210,21 @@ func BuyProduct(id string, providerName string, user *User, host string) (string
|
|||||||
ReturnUrl: product.ReturnUrl,
|
ReturnUrl: product.ReturnUrl,
|
||||||
State: "Created",
|
State: "Created",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if provider.Type == "Dummy" {
|
||||||
|
payment.State = "Paid"
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := AddPayment(&payment)
|
affected, err := AddPayment(&payment)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if !affected {
|
if !affected {
|
||||||
return "", fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
return "", "", fmt.Errorf("failed to add payment: %s", util.StructToJson(payment))
|
||||||
}
|
}
|
||||||
|
|
||||||
return payUrl, err
|
return payUrl, orderId, err
|
||||||
}
|
}
|
||||||
|
|
||||||
func ExtendProductWithProviders(product *Product) error {
|
func ExtendProductWithProviders(product *Product) error {
|
||||||
|
@@ -38,7 +38,7 @@ func TestProduct(t *testing.T) {
|
|||||||
paymentName := util.GenerateTimeId()
|
paymentName := util.GenerateTimeId()
|
||||||
returnUrl := ""
|
returnUrl := ""
|
||||||
notifyUrl := ""
|
notifyUrl := ""
|
||||||
payUrl, err := pProvider.Pay(provider.Name, product.Name, "alice", paymentName, product.DisplayName, product.Price, returnUrl, notifyUrl)
|
payUrl, _, err := pProvider.Pay(provider.Name, product.Name, "alice", paymentName, product.DisplayName, product.Price, product.Currency, returnUrl, notifyUrl)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -225,8 +225,10 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
|
|||||||
session = session.Omit("client_secret2")
|
session = session.Omit("client_secret2")
|
||||||
}
|
}
|
||||||
|
|
||||||
provider.Endpoint = util.GetEndPoint(provider.Endpoint)
|
if provider.Type != "Keycloak" {
|
||||||
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
|
provider.Endpoint = util.GetEndPoint(provider.Endpoint)
|
||||||
|
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := session.Update(provider)
|
affected, err := session.Update(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -237,8 +239,10 @@ func UpdateProvider(id string, provider *Provider) (bool, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func AddProvider(provider *Provider) (bool, error) {
|
func AddProvider(provider *Provider) (bool, error) {
|
||||||
provider.Endpoint = util.GetEndPoint(provider.Endpoint)
|
if provider.Type != "Keycloak" {
|
||||||
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
|
provider.Endpoint = util.GetEndPoint(provider.Endpoint)
|
||||||
|
provider.IntranetEndpoint = util.GetEndPoint(provider.IntranetEndpoint)
|
||||||
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.Insert(provider)
|
affected, err := adapter.Engine.Insert(provider)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@@ -29,6 +29,7 @@ type Role struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
|
|
||||||
Users []string `xorm:"mediumtext" json:"users"`
|
Users []string `xorm:"mediumtext" json:"users"`
|
||||||
Roles []string `xorm:"mediumtext" json:"roles"`
|
Roles []string `xorm:"mediumtext" json:"roles"`
|
||||||
@@ -258,11 +259,22 @@ func GetRolesByUser(userId string) ([]*Role, error) {
|
|||||||
return roles, err
|
return roles, err
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range roles {
|
allRolesIds := make([]string, 0, len(roles))
|
||||||
roles[i].Users = nil
|
|
||||||
|
for _, role := range roles {
|
||||||
|
allRolesIds = append(allRolesIds, role.GetId())
|
||||||
}
|
}
|
||||||
|
|
||||||
return roles, nil
|
allRoles, err := GetAncestorRoles(allRolesIds...)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := range allRoles {
|
||||||
|
allRoles[i].Users = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return allRoles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func roleChangeTrigger(oldName string, newName string) error {
|
func roleChangeTrigger(oldName string, newName string) error {
|
||||||
@@ -334,14 +346,22 @@ func GetRolesByNamePrefix(owner string, prefix string) ([]*Role, error) {
|
|||||||
return roles, nil
|
return roles, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAncestorRoles(roleId string) ([]*Role, error) {
|
// GetAncestorRoles returns a list of roles that contain the given roleIds
|
||||||
|
func GetAncestorRoles(roleIds ...string) ([]*Role, error) {
|
||||||
var (
|
var (
|
||||||
result []*Role
|
result = []*Role{}
|
||||||
roleMap = make(map[string]*Role)
|
roleMap = make(map[string]*Role)
|
||||||
visited = make(map[string]bool)
|
visited = make(map[string]bool)
|
||||||
)
|
)
|
||||||
|
if len(roleIds) == 0 {
|
||||||
|
return result, nil
|
||||||
|
}
|
||||||
|
|
||||||
owner, _ := util.GetOwnerAndNameFromIdNoCheck(roleId)
|
for _, roleId := range roleIds {
|
||||||
|
visited[roleId] = true
|
||||||
|
}
|
||||||
|
|
||||||
|
owner, _ := util.GetOwnerAndNameFromIdNoCheck(roleIds[0])
|
||||||
|
|
||||||
allRoles, err := GetRoles(owner)
|
allRoles, err := GetRoles(owner)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -359,7 +379,7 @@ func GetAncestorRoles(roleId string) ([]*Role, error) {
|
|||||||
result = append(result, r)
|
result = append(result, r)
|
||||||
} else if !ok {
|
} else if !ok {
|
||||||
rId := r.GetId()
|
rId := r.GetId()
|
||||||
visited[rId] = containsRole(r, roleId, roleMap, visited)
|
visited[rId] = containsRole(r, roleMap, visited, roleIds...)
|
||||||
if visited[rId] {
|
if visited[rId] {
|
||||||
result = append(result, r)
|
result = append(result, r)
|
||||||
}
|
}
|
||||||
@@ -369,19 +389,19 @@ func GetAncestorRoles(roleId string) ([]*Role, error) {
|
|||||||
return result, nil
|
return result, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// containsRole is a helper function to check if a slice of roles contains a specific roleId
|
// containsRole is a helper function to check if a roles is related to any role in the given list roles
|
||||||
func containsRole(role *Role, roleId string, roleMap map[string]*Role, visited map[string]bool) bool {
|
func containsRole(role *Role, roleMap map[string]*Role, visited map[string]bool, roleIds ...string) bool {
|
||||||
if isContain, ok := visited[role.GetId()]; ok {
|
if isContain, ok := visited[role.GetId()]; ok {
|
||||||
return isContain
|
return isContain
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, subRole := range role.Roles {
|
for _, subRole := range role.Roles {
|
||||||
if subRole == roleId {
|
if util.HasString(roleIds, subRole) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
r, ok := roleMap[subRole]
|
r, ok := roleMap[subRole]
|
||||||
if ok && containsRole(r, roleId, roleMap, visited) {
|
if ok && containsRole(r, roleMap, visited, roleIds...) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -23,6 +23,7 @@ import (
|
|||||||
"encoding/json"
|
"encoding/json"
|
||||||
"encoding/pem"
|
"encoding/pem"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"time"
|
"time"
|
||||||
@@ -195,6 +196,10 @@ func GetSamlMeta(application *Application, host string) (*IdpEntityDescriptor, e
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if cert == nil {
|
||||||
|
return nil, errors.New("please set a cert for the application first")
|
||||||
|
}
|
||||||
|
|
||||||
block, _ := pem.Decode([]byte(cert.Certificate))
|
block, _ := pem.Decode([]byte(cert.Certificate))
|
||||||
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
|
certificate := base64.StdEncoding.EncodeToString(block.Bytes)
|
||||||
|
|
||||||
|
@@ -29,15 +29,14 @@ type Subscription struct {
|
|||||||
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
Name string `xorm:"varchar(100) notnull pk" json:"name"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||||
Duration int `json:"duration"`
|
|
||||||
|
|
||||||
Description string `xorm:"varchar(100)" json:"description"`
|
StartDate time.Time `json:"startDate"`
|
||||||
Plan string `xorm:"varchar(100)" json:"plan"`
|
EndDate time.Time `json:"endDate"`
|
||||||
|
Duration int `json:"duration"`
|
||||||
StartDate time.Time `json:"startDate"`
|
Description string `xorm:"varchar(100)" json:"description"`
|
||||||
EndDate time.Time `json:"endDate"`
|
|
||||||
|
|
||||||
User string `xorm:"mediumtext" json:"user"`
|
User string `xorm:"mediumtext" json:"user"`
|
||||||
|
Plan string `xorm:"varchar(100)" json:"plan"`
|
||||||
|
|
||||||
IsEnabled bool `json:"isEnabled"`
|
IsEnabled bool `json:"isEnabled"`
|
||||||
Submitter string `xorm:"varchar(100)" json:"submitter"`
|
Submitter string `xorm:"varchar(100)" json:"submitter"`
|
||||||
|
@@ -38,6 +38,7 @@ func getEnabledSyncerForOrganization(organization string) (*Syncer, error) {
|
|||||||
|
|
||||||
for _, syncer := range syncers {
|
for _, syncer := range syncers {
|
||||||
if syncer.Organization == organization && syncer.IsEnabled {
|
if syncer.Organization == organization && syncer.IsEnabled {
|
||||||
|
syncer.initAdapter()
|
||||||
return syncer, nil
|
return syncer, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -628,7 +628,12 @@ func GetPasswordToken(application *Application, username string, password string
|
|||||||
ErrorDescription: "the user does not exist",
|
ErrorDescription: "the user does not exist",
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
msg := CheckPassword(user, password, "en")
|
var msg string
|
||||||
|
if user.Ldap != "" {
|
||||||
|
msg = checkLdapUserPassword(user, password, "en")
|
||||||
|
} else {
|
||||||
|
msg = CheckPassword(user, password, "en")
|
||||||
|
}
|
||||||
if msg != "" {
|
if msg != "" {
|
||||||
return nil, &TokenError{
|
return nil, &TokenError{
|
||||||
Error: InvalidGrant,
|
Error: InvalidGrant,
|
||||||
|
164
object/user.go
164
object/user.go
@@ -15,12 +15,10 @@
|
|||||||
package object
|
package object
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/conf"
|
"github.com/casdoor/casdoor/conf"
|
||||||
"github.com/casdoor/casdoor/proxy"
|
|
||||||
"github.com/casdoor/casdoor/util"
|
"github.com/casdoor/casdoor/util"
|
||||||
"github.com/go-webauthn/webauthn/webauthn"
|
"github.com/go-webauthn/webauthn/webauthn"
|
||||||
"github.com/xorm-io/core"
|
"github.com/xorm-io/core"
|
||||||
@@ -34,7 +32,7 @@ const (
|
|||||||
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"`
|
||||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
CreatedTime string `xorm:"varchar(100) index" json:"createdTime"`
|
||||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||||
|
|
||||||
Id string `xorm:"varchar(100) index" json:"id"`
|
Id string `xorm:"varchar(100) index" json:"id"`
|
||||||
@@ -46,6 +44,7 @@ type User struct {
|
|||||||
FirstName string `xorm:"varchar(100)" json:"firstName"`
|
FirstName string `xorm:"varchar(100)" json:"firstName"`
|
||||||
LastName string `xorm:"varchar(100)" json:"lastName"`
|
LastName string `xorm:"varchar(100)" json:"lastName"`
|
||||||
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
||||||
|
AvatarType string `xorm:"varchar(100)" json:"avatarType"`
|
||||||
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
|
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
|
||||||
Email string `xorm:"varchar(100) index" json:"email"`
|
Email string `xorm:"varchar(100) index" json:"email"`
|
||||||
EmailVerified bool `json:"emailVerified"`
|
EmailVerified bool `json:"emailVerified"`
|
||||||
@@ -77,6 +76,9 @@ type User struct {
|
|||||||
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
|
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
|
||||||
Hash string `xorm:"varchar(100)" json:"hash"`
|
Hash string `xorm:"varchar(100)" json:"hash"`
|
||||||
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
||||||
|
Groups []string `xorm:"varchar(1000)" json:"groups"`
|
||||||
|
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
|
||||||
|
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
|
||||||
|
|
||||||
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
||||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||||
@@ -173,15 +175,16 @@ type User struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type Userinfo struct {
|
type Userinfo struct {
|
||||||
Sub string `json:"sub"`
|
Sub string `json:"sub"`
|
||||||
Iss string `json:"iss"`
|
Iss string `json:"iss"`
|
||||||
Aud string `json:"aud"`
|
Aud string `json:"aud"`
|
||||||
Name string `json:"preferred_username,omitempty"`
|
Name string `json:"preferred_username,omitempty"`
|
||||||
DisplayName string `json:"name,omitempty"`
|
DisplayName string `json:"name,omitempty"`
|
||||||
Email string `json:"email,omitempty"`
|
Email string `json:"email,omitempty"`
|
||||||
Avatar string `json:"picture,omitempty"`
|
Avatar string `json:"picture,omitempty"`
|
||||||
Address string `json:"address,omitempty"`
|
Address string `json:"address,omitempty"`
|
||||||
Phone string `json:"phone,omitempty"`
|
Phone string `json:"phone,omitempty"`
|
||||||
|
Groups []string `json:"groups,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ManagedAccount struct {
|
type ManagedAccount struct {
|
||||||
@@ -208,7 +211,7 @@ func GetGlobalUsers() ([]*User, error) {
|
|||||||
|
|
||||||
func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
session := GetSession("", offset, limit, field, value, sortField, sortOrder)
|
session := GetSessionForUser("", offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&users)
|
err := session.Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@@ -217,8 +220,13 @@ func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOr
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetUserCount(owner, field, value string) (int64, error) {
|
func GetUserCount(owner, field, value string, groupId string) (int64, error) {
|
||||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||||
|
|
||||||
|
if groupId != "" {
|
||||||
|
return GetGroupUserCount(groupId, field, value)
|
||||||
|
}
|
||||||
|
|
||||||
return session.Count(&User{})
|
return session.Count(&User{})
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,14 +264,18 @@ func GetSortedUsers(owner string, sorter string, limit int) ([]*User, error) {
|
|||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string, groupId string) ([]*User, error) {
|
||||||
users := []*User{}
|
users := []*User{}
|
||||||
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
|
|
||||||
|
if groupId != "" {
|
||||||
|
return GetPaginationGroupUsers(groupId, offset, limit, field, value, sortField, sortOrder)
|
||||||
|
}
|
||||||
|
|
||||||
|
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
||||||
err := session.Find(&users)
|
err := session.Find(&users)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return users, nil
|
return users, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -374,6 +386,23 @@ func GetUserByUserId(owner string, userId string) (*User, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func GetUserByAccessKey(accessKey string) (*User, error) {
|
||||||
|
if accessKey == "" {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
user := User{AccessKey: accessKey}
|
||||||
|
existed, err := adapter.Engine.Get(&user)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if existed {
|
||||||
|
return &user, nil
|
||||||
|
} else {
|
||||||
|
return nil, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func GetUser(id string) (*User, error) {
|
func GetUser(id string) (*User, error) {
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
return getUser(owner, name)
|
return getUser(owner, name)
|
||||||
@@ -478,7 +507,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
"owner", "display_name", "avatar",
|
"owner", "display_name", "avatar",
|
||||||
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
"location", "address", "country_code", "region", "language", "affiliation", "title", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
|
||||||
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
"is_admin", "is_global_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts",
|
||||||
"signin_wrong_times", "last_signin_wrong_time",
|
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret",
|
||||||
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
|
||||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||||
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
||||||
@@ -492,7 +521,7 @@ 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")
|
||||||
}
|
}
|
||||||
|
|
||||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)
|
affected, err := updateUser(oldUser, user, columns)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -500,6 +529,35 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
|||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func updateUser(oldUser, user *User, columns []string) (int64, error) {
|
||||||
|
session := adapter.Engine.NewSession()
|
||||||
|
defer session.Close()
|
||||||
|
|
||||||
|
session.Begin()
|
||||||
|
|
||||||
|
if util.ContainsString(columns, "groups") {
|
||||||
|
affected, err := updateUserGroupRelation(session, user)
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
affected, err := session.ID(core.PK{oldUser.Owner, oldUser.Name}).Cols(columns...).Update(user)
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.Commit()
|
||||||
|
if err != nil {
|
||||||
|
session.Rollback()
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return affected, nil
|
||||||
|
}
|
||||||
|
|
||||||
func UpdateUserForAllFields(id string, user *User) (bool, error) {
|
func UpdateUserForAllFields(id string, user *User) (bool, error) {
|
||||||
var err error
|
var err error
|
||||||
owner, name := util.GetOwnerAndNameFromId(id)
|
owner, name := util.GetOwnerAndNameFromId(id)
|
||||||
@@ -554,6 +612,10 @@ func AddUser(user *User) (bool, error) {
|
|||||||
return false, nil
|
return false, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if user.PasswordType == "" && organization.PasswordType != "" {
|
||||||
|
user.PasswordType = organization.PasswordType
|
||||||
|
}
|
||||||
|
|
||||||
user.UpdateUserPassword(organization)
|
user.UpdateUserPassword(organization)
|
||||||
|
|
||||||
err = user.UpdateUserHash()
|
err = user.UpdateUserHash()
|
||||||
@@ -575,7 +637,7 @@ func AddUser(user *User) (bool, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
count, err := GetUserCount(user.Owner, "", "")
|
count, err := GetUserCount(user.Owner, "", "", "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
@@ -663,6 +725,11 @@ func DeleteUser(user *User) (bool, error) {
|
|||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
affected, err = deleteRelationByUser(user.Id)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
return affected != 0, nil
|
return affected != 0, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -678,6 +745,7 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
|
|||||||
resp.Name = user.Name
|
resp.Name = user.Name
|
||||||
resp.DisplayName = user.DisplayName
|
resp.DisplayName = user.DisplayName
|
||||||
resp.Avatar = user.Avatar
|
resp.Avatar = user.Avatar
|
||||||
|
resp.Groups = []string{user.Owner}
|
||||||
}
|
}
|
||||||
if strings.Contains(scope, "email") {
|
if strings.Contains(scope, "email") {
|
||||||
resp.Email = user.Email
|
resp.Email = user.Email
|
||||||
@@ -708,12 +776,11 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Roles, err = GetRolesByUser(user.GetId())
|
user.Permissions, user.Roles, err = GetPermissionsAndRolesByUser(user.GetId())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
user.Permissions, err = GetPermissionsByUser(user.GetId())
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -775,46 +842,6 @@ func userChangeTrigger(oldName string, newName string) error {
|
|||||||
return session.Commit()
|
return session.Commit()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (user *User) refreshAvatar() (bool, error) {
|
|
||||||
var err error
|
|
||||||
var fileBuffer *bytes.Buffer
|
|
||||||
var ext string
|
|
||||||
|
|
||||||
// Gravatar + Identicon
|
|
||||||
if strings.Contains(user.Avatar, "Gravatar") && user.Email != "" {
|
|
||||||
client := proxy.ProxyHttpClient
|
|
||||||
has, err := hasGravatar(client, user.Email)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
|
|
||||||
if has {
|
|
||||||
fileBuffer, ext, err = getGravatarFileBuffer(client, user.Email)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileBuffer == nil && strings.Contains(user.Avatar, "Identicon") {
|
|
||||||
fileBuffer, ext, err = getIdenticonFileBuffer(user.Name)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if fileBuffer != nil {
|
|
||||||
avatarUrl, err := getPermanentAvatarUrlFromBuffer(user.Owner, user.Name, fileBuffer, ext, true)
|
|
||||||
if err != nil {
|
|
||||||
return false, err
|
|
||||||
}
|
|
||||||
user.Avatar = avatarUrl
|
|
||||||
return true, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return false, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (user *User) IsMfaEnabled() bool {
|
func (user *User) IsMfaEnabled() bool {
|
||||||
return len(user.MultiFactorAuths) > 0
|
return len(user.MultiFactorAuths) > 0
|
||||||
}
|
}
|
||||||
@@ -846,3 +873,14 @@ func (user *User) GetPreferMfa(masked bool) *MfaProps {
|
|||||||
return user.MultiFactorAuths[0]
|
return user.MultiFactorAuths[0]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func AddUserkeys(user *User, isAdmin bool) (bool, error) {
|
||||||
|
if user == nil {
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
user.AccessKey = util.GenerateId()
|
||||||
|
user.AccessSecret = util.GenerateId()
|
||||||
|
|
||||||
|
return UpdateUser(user.GetId(), user, []string{}, isAdmin)
|
||||||
|
}
|
||||||
|
149
object/user_avatar.go
Normal file
149
object/user_avatar.go
Normal file
@@ -0,0 +1,149 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/proxy"
|
||||||
|
)
|
||||||
|
|
||||||
|
func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, error) {
|
||||||
|
// Download the image
|
||||||
|
req, err := http.NewRequest("GET", url, nil)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, err.Error())
|
||||||
|
if strings.Contains(err.Error(), "EOF") {
|
||||||
|
return nil, "", nil
|
||||||
|
} else {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
if resp.StatusCode != http.StatusOK {
|
||||||
|
fmt.Printf("downloadImage() error for url [%s]: %s\n", url, resp.Status)
|
||||||
|
if resp.StatusCode == 404 {
|
||||||
|
return nil, "", nil
|
||||||
|
} else {
|
||||||
|
return nil, "", fmt.Errorf("failed to download gravatar image: %s", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the content type and determine the file extension
|
||||||
|
contentType := resp.Header.Get("Content-Type")
|
||||||
|
fileExtension := ""
|
||||||
|
|
||||||
|
if strings.Contains(contentType, "text/html") {
|
||||||
|
fileExtension = ".html"
|
||||||
|
} else {
|
||||||
|
switch contentType {
|
||||||
|
case "image/jpeg":
|
||||||
|
fileExtension = ".jpg"
|
||||||
|
case "image/png":
|
||||||
|
fileExtension = ".png"
|
||||||
|
case "image/gif":
|
||||||
|
fileExtension = ".gif"
|
||||||
|
case "image/vnd.microsoft.icon":
|
||||||
|
fileExtension = ".ico"
|
||||||
|
case "image/x-icon":
|
||||||
|
fileExtension = ".ico"
|
||||||
|
default:
|
||||||
|
return nil, "", fmt.Errorf("unsupported content type: %s", contentType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save the image to a bytes.Buffer
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
_, err = io.Copy(buffer, resp.Body)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, fileExtension, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (user *User) refreshAvatar() (bool, error) {
|
||||||
|
var err error
|
||||||
|
var fileBuffer *bytes.Buffer
|
||||||
|
var ext string
|
||||||
|
|
||||||
|
// Gravatar
|
||||||
|
if (user.AvatarType == "Auto" || user.AvatarType == "Gravatar") && user.Email != "" {
|
||||||
|
client := proxy.ProxyHttpClient
|
||||||
|
|
||||||
|
has, err := hasGravatar(client, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if has {
|
||||||
|
fileBuffer, ext, err = getGravatarFileBuffer(client, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileBuffer != nil {
|
||||||
|
user.AvatarType = "Gravatar"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Favicon
|
||||||
|
if fileBuffer == nil && (user.AvatarType == "Auto" || user.AvatarType == "Favicon") {
|
||||||
|
client := proxy.ProxyHttpClient
|
||||||
|
|
||||||
|
fileBuffer, ext, err = getFaviconFileBuffer(client, user.Email)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileBuffer != nil {
|
||||||
|
user.AvatarType = "Favicon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Identicon
|
||||||
|
if fileBuffer == nil && (user.AvatarType == "Auto" || user.AvatarType == "Identicon") {
|
||||||
|
fileBuffer, ext, err = getIdenticonFileBuffer(user.Name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileBuffer != nil {
|
||||||
|
user.AvatarType = "Identicon"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if fileBuffer != nil {
|
||||||
|
avatarUrl, err := getPermanentAvatarUrlFromBuffer(user.Owner, user.Name, fileBuffer, ext, true)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
user.Avatar = avatarUrl
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
220
object/user_avatar_favicon.go
Normal file
220
object/user_avatar_favicon.go
Normal file
@@ -0,0 +1,220 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"golang.org/x/net/html"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Link struct {
|
||||||
|
Rel string
|
||||||
|
Sizes string
|
||||||
|
Href string
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetFaviconUrl(htmlStr string) (string, error) {
|
||||||
|
doc, err := html.Parse(strings.NewReader(htmlStr))
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
var links []Link
|
||||||
|
findLinks(doc, &links)
|
||||||
|
|
||||||
|
if len(links) == 0 {
|
||||||
|
return "", fmt.Errorf("no Favicon links found")
|
||||||
|
}
|
||||||
|
|
||||||
|
chosenLink := chooseFaviconLink(links)
|
||||||
|
if chosenLink == nil {
|
||||||
|
return "", fmt.Errorf("unable to determine favicon URL")
|
||||||
|
}
|
||||||
|
|
||||||
|
return chosenLink.Href, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func findLinks(n *html.Node, links *[]Link) {
|
||||||
|
if n.Type == html.ElementNode && n.Data == "link" {
|
||||||
|
link := parseLink(n)
|
||||||
|
if link != nil {
|
||||||
|
*links = append(*links, *link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for c := n.FirstChild; c != nil; c = c.NextSibling {
|
||||||
|
findLinks(c, links)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseLink(n *html.Node) *Link {
|
||||||
|
var link Link
|
||||||
|
|
||||||
|
for _, attr := range n.Attr {
|
||||||
|
switch attr.Key {
|
||||||
|
case "rel":
|
||||||
|
link.Rel = attr.Val
|
||||||
|
case "sizes":
|
||||||
|
link.Sizes = attr.Val
|
||||||
|
case "href":
|
||||||
|
link.Href = attr.Val
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if link.Href != "" {
|
||||||
|
return &link
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func chooseFaviconLink(links []Link) *Link {
|
||||||
|
var appleTouchLinks []Link
|
||||||
|
var shortcutLinks []Link
|
||||||
|
var iconLinks []Link
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
switch link.Rel {
|
||||||
|
case "apple-touch-icon":
|
||||||
|
appleTouchLinks = append(appleTouchLinks, link)
|
||||||
|
case "shortcut icon":
|
||||||
|
shortcutLinks = append(shortcutLinks, link)
|
||||||
|
case "icon":
|
||||||
|
iconLinks = append(iconLinks, link)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(appleTouchLinks) > 0 {
|
||||||
|
return chooseFaviconLinkBySizes(appleTouchLinks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(shortcutLinks) > 0 {
|
||||||
|
return chooseFaviconLinkBySizes(shortcutLinks)
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(iconLinks) > 0 {
|
||||||
|
return chooseFaviconLinkBySizes(iconLinks)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func chooseFaviconLinkBySizes(links []Link) *Link {
|
||||||
|
if len(links) == 1 {
|
||||||
|
return &links[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
var chosenLink *Link
|
||||||
|
|
||||||
|
for _, link := range links {
|
||||||
|
if chosenLink == nil || compareSizes(link.Sizes, chosenLink.Sizes) > 0 {
|
||||||
|
chosenLink = &link
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return chosenLink
|
||||||
|
}
|
||||||
|
|
||||||
|
func compareSizes(sizes1, sizes2 string) int {
|
||||||
|
if sizes1 == sizes2 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
size1 := parseSize(sizes1)
|
||||||
|
size2 := parseSize(sizes2)
|
||||||
|
|
||||||
|
if size1 == nil {
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
if size2 == nil {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if size1[0] == size2[0] {
|
||||||
|
return size1[1] - size2[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
return size1[0] - size2[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseSize(sizes string) []int {
|
||||||
|
size := strings.Split(sizes, "x")
|
||||||
|
if len(size) != 2 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
var result []int
|
||||||
|
|
||||||
|
for _, s := range size {
|
||||||
|
val := strings.TrimSpace(s)
|
||||||
|
if len(val) > 0 {
|
||||||
|
num := 0
|
||||||
|
for i := 0; i < len(val); i++ {
|
||||||
|
if val[i] >= '0' && val[i] <= '9' {
|
||||||
|
num = num*10 + int(val[i]-'0')
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
result = append(result, num)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(result) == 2 {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
||||||
|
tokens := strings.Split(email, "@")
|
||||||
|
domain := tokens[1]
|
||||||
|
if domain == "gmail.com" || domain == "163.com" || domain == "qq.com" {
|
||||||
|
return nil, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
htmlUrl := fmt.Sprintf("https://%s", domain)
|
||||||
|
buffer, _, err := downloadImage(client, htmlUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
faviconUrl := ""
|
||||||
|
if buffer != nil {
|
||||||
|
faviconUrl, err = GetFaviconUrl(buffer.String())
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !strings.HasPrefix(faviconUrl, "http") {
|
||||||
|
faviconUrl, err = url.JoinPath(htmlUrl, faviconUrl)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if faviconUrl == "" {
|
||||||
|
faviconUrl = fmt.Sprintf("https://%s/favicon.ico", domain)
|
||||||
|
}
|
||||||
|
return downloadImage(client, faviconUrl)
|
||||||
|
}
|
76
object/user_avatar_gravatar.go
Normal file
76
object/user_avatar_gravatar.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"net/http"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func hasGravatar(client *http.Client, email string) (bool, error) {
|
||||||
|
// Clean and lowercase the email
|
||||||
|
email = strings.TrimSpace(strings.ToLower(email))
|
||||||
|
|
||||||
|
// Generate MD5 hash of the email
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, email)
|
||||||
|
hashedEmail := fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
|
||||||
|
// Create Gravatar URL with d=404 parameter
|
||||||
|
gravatarURL := fmt.Sprintf("https://www.gravatar.com/avatar/%s?d=404", hashedEmail)
|
||||||
|
|
||||||
|
// Send a request to Gravatar
|
||||||
|
req, err := http.NewRequest("GET", gravatarURL, nil)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err := client.Do(req)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
defer resp.Body.Close()
|
||||||
|
|
||||||
|
// Check if the user has a custom Gravatar image
|
||||||
|
if resp.StatusCode == http.StatusOK {
|
||||||
|
return true, nil
|
||||||
|
} else if resp.StatusCode == http.StatusNotFound {
|
||||||
|
return false, nil
|
||||||
|
} else {
|
||||||
|
return false, fmt.Errorf("failed to fetch gravatar image: %s", resp.Status)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getGravatarFileBuffer(client *http.Client, email string) (*bytes.Buffer, string, error) {
|
||||||
|
// Clean and lowercase the email
|
||||||
|
email = strings.TrimSpace(strings.ToLower(email))
|
||||||
|
|
||||||
|
// Generate MD5 hash of the email
|
||||||
|
hash := md5.New()
|
||||||
|
_, err := io.WriteString(hash, email)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", err
|
||||||
|
}
|
||||||
|
hashedEmail := fmt.Sprintf("%x", hash.Sum(nil))
|
||||||
|
|
||||||
|
// Create Gravatar URL
|
||||||
|
gravatarUrl := fmt.Sprintf("https://www.gravatar.com/avatar/%s", hashedEmail)
|
||||||
|
|
||||||
|
return downloadImage(client, gravatarUrl)
|
||||||
|
}
|
80
object/user_avatar_identicon.go
Normal file
80
object/user_avatar_identicon.go
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
// 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 object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/md5"
|
||||||
|
"fmt"
|
||||||
|
"image"
|
||||||
|
"image/color"
|
||||||
|
"image/png"
|
||||||
|
"io"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/fogleman/gg"
|
||||||
|
)
|
||||||
|
|
||||||
|
func getColor(data []byte) color.RGBA {
|
||||||
|
r := int(data[0]) % 256
|
||||||
|
g := int(data[1]) % 256
|
||||||
|
b := int(data[2]) % 256
|
||||||
|
return color.RGBA{uint8(r), uint8(g), uint8(b), 255}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getIdenticonFileBuffer(username string) (*bytes.Buffer, string, error) {
|
||||||
|
username = strings.TrimSpace(strings.ToLower(username))
|
||||||
|
|
||||||
|
hash := md5.New()
|
||||||
|
io.WriteString(hash, username)
|
||||||
|
hashedUsername := hash.Sum(nil)
|
||||||
|
|
||||||
|
// Define the size of the image
|
||||||
|
const imageSize = 420
|
||||||
|
const cellSize = imageSize / 7
|
||||||
|
|
||||||
|
// Create a new image
|
||||||
|
img := image.NewRGBA(image.Rect(0, 0, imageSize, imageSize))
|
||||||
|
|
||||||
|
// Create a context
|
||||||
|
dc := gg.NewContextForRGBA(img)
|
||||||
|
|
||||||
|
// Set a background color
|
||||||
|
dc.SetColor(color.RGBA{240, 240, 240, 255})
|
||||||
|
dc.Clear()
|
||||||
|
|
||||||
|
// Get avatar color
|
||||||
|
avatarColor := getColor(hashedUsername)
|
||||||
|
|
||||||
|
// Draw cells
|
||||||
|
for i := 0; i < 7; i++ {
|
||||||
|
for j := 0; j < 7; j++ {
|
||||||
|
if (hashedUsername[i] >> uint(j) & 1) == 1 {
|
||||||
|
dc.SetColor(avatarColor)
|
||||||
|
dc.DrawRectangle(float64(j*cellSize), float64(i*cellSize), float64(cellSize), float64(cellSize))
|
||||||
|
dc.Fill()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save image to a bytes.Buffer
|
||||||
|
buffer := &bytes.Buffer{}
|
||||||
|
err := png.Encode(buffer, img)
|
||||||
|
if err != nil {
|
||||||
|
return nil, "", fmt.Errorf("failed to save image: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return buffer, ".png", nil
|
||||||
|
}
|
@@ -16,7 +16,6 @@ package object
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
"github.com/casdoor/casdoor/proxy"
|
"github.com/casdoor/casdoor/proxy"
|
||||||
@@ -58,7 +57,11 @@ func TestUpdateAvatars(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for _, user := range users {
|
for _, user := range users {
|
||||||
if strings.HasPrefix(user.Avatar, "http") {
|
//if strings.HasPrefix(user.Avatar, "http") {
|
||||||
|
// continue
|
||||||
|
//}
|
||||||
|
|
||||||
|
if user.AvatarType != "Auto" {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,7 +72,7 @@ func TestUpdateAvatars(t *testing.T) {
|
|||||||
|
|
||||||
if updated {
|
if updated {
|
||||||
user.PermanentAvatar = "*"
|
user.PermanentAvatar = "*"
|
||||||
_, err = UpdateUser(user.GetId(), user, []string{"avatar"}, true)
|
_, err = UpdateUser(user.GetId(), user, []string{"avatar", "avatar_type"}, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
157
object/user_group.go
Normal file
157
object/user_group.go
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
package object
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/casdoor/casdoor/util"
|
||||||
|
"github.com/xorm-io/core"
|
||||||
|
"github.com/xorm-io/xorm"
|
||||||
|
)
|
||||||
|
|
||||||
|
type UserGroupRelation struct {
|
||||||
|
UserId string `xorm:"varchar(100) notnull pk" json:"userId"`
|
||||||
|
GroupId string `xorm:"varchar(100) notnull pk" json:"groupId"`
|
||||||
|
|
||||||
|
CreatedTime string `xorm:"created" json:"createdTime"`
|
||||||
|
UpdatedTime string `xorm:"updated" json:"updatedTime"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUserGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
||||||
|
physicalGroupCount, err := session.Where("type = ?", "Physical").In("id", user.Groups).Count(Group{})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if physicalGroupCount > 1 {
|
||||||
|
return 0, errors.New("user can only be in one physical group")
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := []*Group{}
|
||||||
|
err = session.In("id", user.Groups).Find(&groups)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
if len(groups) != len(user.Groups) {
|
||||||
|
return 0, errors.New("group not found")
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = session.Delete(&UserGroupRelation{UserId: user.Id})
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
relations := []*UserGroupRelation{}
|
||||||
|
for _, group := range groups {
|
||||||
|
relations = append(relations, &UserGroupRelation{UserId: user.Id, GroupId: group.Id})
|
||||||
|
}
|
||||||
|
if len(relations) == 0 {
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
_, err = session.Insert(relations)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
|
||||||
|
user, err := getUser(owner, name)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
groups := []string{}
|
||||||
|
for _, group := range user.Groups {
|
||||||
|
if group != groupId {
|
||||||
|
groups = append(groups, group)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
user.Groups = groups
|
||||||
|
|
||||||
|
_, err = UpdateUser(util.GetId(owner, name), user, []string{"groups"}, false)
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteUserGroupRelation(session *xorm.Session, userId, groupId string) (int64, error) {
|
||||||
|
affected, err := session.ID(core.PK{userId, groupId}).Delete(&UserGroupRelation{})
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteRelationByUser(id string) (int64, error) {
|
||||||
|
affected, err := adapter.Engine.Delete(&UserGroupRelation{UserId: id})
|
||||||
|
return affected, err
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupUserCount(id string, field, value string) (int64, error) {
|
||||||
|
group, err := GetGroup(id)
|
||||||
|
if group == nil || err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if field == "" && value == "" {
|
||||||
|
return adapter.Engine.Count(UserGroupRelation{GroupId: group.Id})
|
||||||
|
} else {
|
||||||
|
return adapter.Engine.Table("user").
|
||||||
|
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
|
||||||
|
Where("r.group_id = ?", group.Id).
|
||||||
|
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||||
|
Count()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetPaginationGroupUsers(id string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||||
|
group, err := GetGroup(id)
|
||||||
|
if group == nil || err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []*User{}
|
||||||
|
session := adapter.Engine.Table("user").
|
||||||
|
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
|
||||||
|
Where("r.group_id = ?", group.Id)
|
||||||
|
|
||||||
|
if offset != -1 && limit != -1 {
|
||||||
|
session.Limit(limit, offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if field != "" && value != "" {
|
||||||
|
session = session.And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%")
|
||||||
|
}
|
||||||
|
|
||||||
|
if sortField == "" || sortOrder == "" {
|
||||||
|
sortField = "created_time"
|
||||||
|
}
|
||||||
|
if sortOrder == "ascend" {
|
||||||
|
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||||
|
} else {
|
||||||
|
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||||
|
}
|
||||||
|
|
||||||
|
err = session.Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetGroupUsers(id string) ([]*User, error) {
|
||||||
|
group, err := GetGroup(id)
|
||||||
|
if group == nil || err != nil {
|
||||||
|
return []*User{}, err
|
||||||
|
}
|
||||||
|
|
||||||
|
users := []*User{}
|
||||||
|
err = adapter.Engine.Table("user_group_relation").Join("INNER", []string{"user", "u"}, "user_group_relation.user_id = u.id").
|
||||||
|
Where("user_group_relation.group_id = ?", group.Id).
|
||||||
|
Find(&users)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return users, nil
|
||||||
|
}
|
@@ -295,6 +295,13 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
|||||||
itemsChanged = append(itemsChanged, item)
|
itemsChanged = append(itemsChanged, item)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
oldUserGroupsJson, _ := json.Marshal(oldUser.Groups)
|
||||||
|
newUserGroupsJson, _ := json.Marshal(newUser.Groups)
|
||||||
|
if string(oldUserGroupsJson) != string(newUserGroupsJson) {
|
||||||
|
item := GetAccountItemByName("Groups", organization)
|
||||||
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
}
|
||||||
|
|
||||||
if oldUser.IsAdmin != newUser.IsAdmin {
|
if oldUser.IsAdmin != newUser.IsAdmin {
|
||||||
item := GetAccountItemByName("Is admin", organization)
|
item := GetAccountItemByName("Is admin", organization)
|
||||||
itemsChanged = append(itemsChanged, item)
|
itemsChanged = append(itemsChanged, item)
|
||||||
|
@@ -224,7 +224,7 @@ func GetVerifyType(username string) (verificationCodeType string) {
|
|||||||
if strings.Contains(username, "@") {
|
if strings.Contains(username, "@") {
|
||||||
return VerifyTypeEmail
|
return VerifyTypeEmail
|
||||||
} else {
|
} else {
|
||||||
return VerifyTypeEmail
|
return VerifyTypePhone
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -45,7 +45,7 @@ func NewAlipayPaymentProvider(appId string, appCertificate string, appPrivateKey
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
// pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
|
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
@@ -62,12 +62,12 @@ func (pp *AlipayPaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
|
|
||||||
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
payUrl, err := pp.Client.TradePagePay(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
return payUrl, nil
|
return payUrl, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error) {
|
func (pp *AlipayPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||||
bm, err := alipay.ParseNotifyToBodyMap(request)
|
bm, err := alipay.ParseNotifyToBodyMap(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, "", "", err
|
return "", "", 0, "", "", err
|
||||||
|
44
pp/dummy.go
Normal file
44
pp/dummy.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
// 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 pp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/http"
|
||||||
|
)
|
||||||
|
|
||||||
|
type DummyPaymentProvider struct{}
|
||||||
|
|
||||||
|
func NewDummyPaymentProvider() (*DummyPaymentProvider, error) {
|
||||||
|
pp := &DummyPaymentProvider{}
|
||||||
|
return pp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *DummyPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
|
payUrl := fmt.Sprintf("/payments/%s/result", paymentName)
|
||||||
|
return payUrl, "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *DummyPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||||
|
return "", "", 0, "", "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *DummyPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||||
|
return "", nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (pp *DummyPaymentProvider) GetResponseError(err error) string {
|
||||||
|
return ""
|
||||||
|
}
|
20
pp/gc.go
20
pp/gc.go
@@ -153,7 +153,7 @@ func (pp *GcPaymentProvider) doPost(postBytes []byte) ([]byte, error) {
|
|||||||
return respBytes, nil
|
return respBytes, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
payReqInfo := GcPayReqInfo{
|
payReqInfo := GcPayReqInfo{
|
||||||
OrderDate: util.GenerateSimpleTimeId(),
|
OrderDate: util.GenerateSimpleTimeId(),
|
||||||
OrderNo: paymentName,
|
OrderNo: paymentName,
|
||||||
@@ -168,7 +168,7 @@ func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerN
|
|||||||
|
|
||||||
b, err := json.Marshal(payReqInfo)
|
b, err := json.Marshal(payReqInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
body := GcRequestBody{
|
body := GcRequestBody{
|
||||||
@@ -184,39 +184,39 @@ func (pp *GcPaymentProvider) Pay(providerName string, productName string, payerN
|
|||||||
|
|
||||||
bodyBytes, err := json.Marshal(body)
|
bodyBytes, err := json.Marshal(body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
respBytes, err := pp.doPost(bodyBytes)
|
respBytes, err := pp.doPost(bodyBytes)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
var respBody GcResponseBody
|
var respBody GcResponseBody
|
||||||
err = json.Unmarshal(respBytes, &respBody)
|
err = json.Unmarshal(respBytes, &respBody)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if respBody.ReturnCode != "SUCCESS" {
|
if respBody.ReturnCode != "SUCCESS" {
|
||||||
return "", fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
return "", "", fmt.Errorf("%s: %s", respBody.ReturnCode, respBody.ReturnMsg)
|
||||||
}
|
}
|
||||||
|
|
||||||
payRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
payRespInfoBytes, err := base64.StdEncoding.DecodeString(respBody.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
var payRespInfo GcPayRespInfo
|
var payRespInfo GcPayRespInfo
|
||||||
err = json.Unmarshal(payRespInfoBytes, &payRespInfo)
|
err = json.Unmarshal(payRespInfoBytes, &payRespInfo)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
return payRespInfo.PayUrl, nil
|
return payRespInfo.PayUrl, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error) {
|
func (pp *GcPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||||
reqBody := GcRequestBody{}
|
reqBody := GcRequestBody{}
|
||||||
m, err := url.ParseQuery(string(body))
|
m, err := url.ParseQuery(string(body))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
92
pp/paypal.go
92
pp/paypal.go
@@ -16,10 +16,13 @@ package pp
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"errors"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
"github.com/plutov/paypal/v4"
|
"github.com/go-pay/gopay"
|
||||||
|
"github.com/go-pay/gopay/paypal"
|
||||||
|
"github.com/go-pay/gopay/pkg/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type PaypalPaymentProvider struct {
|
type PaypalPaymentProvider struct {
|
||||||
@@ -29,7 +32,7 @@ type PaypalPaymentProvider struct {
|
|||||||
func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentProvider, error) {
|
func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentProvider, error) {
|
||||||
pp := &PaypalPaymentProvider{}
|
pp := &PaypalPaymentProvider{}
|
||||||
|
|
||||||
client, err := paypal.NewClient(clientID, secret, paypal.APIBaseSandBox)
|
client, err := paypal.NewClient(clientID, secret, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@@ -38,51 +41,62 @@ func NewPaypalPaymentProvider(clientID string, secret string) (*PaypalPaymentPro
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
func (pp *PaypalPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
// pp.Client.SetLog(os.Stdout) // Set log to terminal stdout
|
// pp.Client.DebugSwitch = gopay.DebugOn // Set log to terminal stdout
|
||||||
|
|
||||||
receiverEmail := "sb-tmsqa26118644@business.example.com"
|
priceStr := strconv.FormatFloat(price, 'f', 2, 64)
|
||||||
|
var pus []*paypal.PurchaseUnit
|
||||||
amount := paypal.AmountPayout{
|
item := &paypal.PurchaseUnit{
|
||||||
Value: fmt.Sprintf("%.2f", price),
|
ReferenceId: util.GetRandomString(16),
|
||||||
Currency: "USD",
|
Amount: &paypal.Amount{
|
||||||
}
|
CurrencyCode: currency,
|
||||||
|
Value: priceStr,
|
||||||
description := fmt.Sprintf("%s-%s", providerName, productName)
|
|
||||||
|
|
||||||
payout := paypal.Payout{
|
|
||||||
SenderBatchHeader: &paypal.SenderBatchHeader{
|
|
||||||
EmailSubject: description,
|
|
||||||
},
|
|
||||||
Items: []paypal.PayoutItem{
|
|
||||||
{
|
|
||||||
RecipientType: "EMAIL",
|
|
||||||
Receiver: receiverEmail,
|
|
||||||
Amount: &amount,
|
|
||||||
Note: description,
|
|
||||||
SenderItemID: description,
|
|
||||||
},
|
|
||||||
},
|
},
|
||||||
|
Description: joinAttachString([]string{productDisplayName, productName, providerName}),
|
||||||
}
|
}
|
||||||
|
pus = append(pus, item)
|
||||||
|
|
||||||
_, err := pp.Client.GetAccessToken(context.Background())
|
bm := make(gopay.BodyMap)
|
||||||
|
bm.Set("intent", "CAPTURE")
|
||||||
|
bm.Set("purchase_units", pus)
|
||||||
|
bm.SetBodyMap("application_context", func(b gopay.BodyMap) {
|
||||||
|
b.Set("brand_name", "Casdoor")
|
||||||
|
b.Set("locale", "en-PT")
|
||||||
|
b.Set("return_url", returnUrl)
|
||||||
|
})
|
||||||
|
|
||||||
|
ppRsp, err := pp.Client.CreateOrder(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
|
}
|
||||||
|
if ppRsp.Code != paypal.Success {
|
||||||
|
return "", "", errors.New(ppRsp.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
payoutResponse, err := pp.Client.CreatePayout(context.Background(), payout)
|
return ppRsp.Response.Links[1].Href, ppRsp.Response.Id, nil
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
|
|
||||||
payUrl := payoutResponse.Links[0].Href
|
|
||||||
return payUrl, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error) {
|
func (pp *PaypalPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||||
// The PayPal SDK does not directly support IPN verification.
|
ppRsp, err := pp.Client.OrderCapture(context.Background(), orderId, nil)
|
||||||
// So, you need to implement this part according to PayPal's IPN guide.
|
if err != nil {
|
||||||
return "", "", 0, "", "", nil
|
return "", "", 0, "", "", err
|
||||||
|
}
|
||||||
|
if ppRsp.Code != paypal.Success {
|
||||||
|
return "", "", 0, "", "", errors.New(ppRsp.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
paymentName := ppRsp.Response.Id
|
||||||
|
price, err := strconv.ParseFloat(ppRsp.Response.PurchaseUnits[0].Amount.Value, 64)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", 0, "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
productDisplayName, productName, providerName, err := parseAttachString(ppRsp.Response.PurchaseUnits[0].Description)
|
||||||
|
if err != nil {
|
||||||
|
return "", "", 0, "", "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
return productDisplayName, paymentName, price, productName, providerName, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *PaypalPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
func (pp *PaypalPaymentProvider) GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error) {
|
||||||
|
@@ -17,33 +17,39 @@ package pp
|
|||||||
import "net/http"
|
import "net/http"
|
||||||
|
|
||||||
type PaymentProvider interface {
|
type PaymentProvider interface {
|
||||||
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error)
|
Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error)
|
||||||
Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error)
|
Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error)
|
||||||
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
GetInvoice(paymentName string, personName string, personIdCard string, personEmail string, personPhone string, invoiceType string, invoiceTitle string, invoiceTaxId string) (string, error)
|
||||||
GetResponseError(err error) string
|
GetResponseError(err error) string
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetPaymentProvider(typ string, clientId string, clientSecret string, host string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string, clientId2 string) (PaymentProvider, error) {
|
func GetPaymentProvider(typ string, clientId string, clientSecret string, host string, appCertificate string, appPrivateKey string, authorityPublicKey string, authorityRootPublicKey string, clientId2 string) (PaymentProvider, error) {
|
||||||
if typ == "Alipay" {
|
if typ == "Dummy" {
|
||||||
newAlipayPaymentProvider, err := NewAlipayPaymentProvider(clientId, appCertificate, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
|
pp, err := NewDummyPaymentProvider()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newAlipayPaymentProvider, nil
|
return pp, nil
|
||||||
|
} else if typ == "Alipay" {
|
||||||
|
pp, err := NewAlipayPaymentProvider(clientId, appCertificate, appPrivateKey, authorityPublicKey, authorityRootPublicKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return pp, nil
|
||||||
} else if typ == "GC" {
|
} else if typ == "GC" {
|
||||||
return NewGcPaymentProvider(clientId, clientSecret, host), nil
|
return NewGcPaymentProvider(clientId, clientSecret, host), nil
|
||||||
} else if typ == "WeChat Pay" {
|
} else if typ == "WeChat Pay" {
|
||||||
newWechatPaymentProvider, err := NewWechatPaymentProvider(clientId, clientSecret, clientId2, appCertificate, appPrivateKey)
|
pp, err := NewWechatPaymentProvider(clientId, clientSecret, clientId2, appCertificate, appPrivateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newWechatPaymentProvider, nil
|
return pp, nil
|
||||||
} else if typ == "PayPal" {
|
} else if typ == "PayPal" {
|
||||||
newPaypalPaymentProvider, err := NewPaypalPaymentProvider(clientId, clientSecret)
|
pp, err := NewPaypalPaymentProvider(clientId, clientSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newPaypalPaymentProvider, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@@ -56,7 +56,7 @@ func NewWechatPaymentProvider(mchId string, apiV3Key string, appId string, mchCe
|
|||||||
return pp, nil
|
return pp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, returnUrl string, notifyUrl string) (string, error) {
|
func (pp *WechatPaymentProvider) Pay(providerName string, productName string, payerName string, paymentName string, productDisplayName string, price float64, currency string, returnUrl string, notifyUrl string) (string, string, error) {
|
||||||
// pp.Client.DebugSwitch = gopay.DebugOn
|
// pp.Client.DebugSwitch = gopay.DebugOn
|
||||||
|
|
||||||
bm := gopay.BodyMap{}
|
bm := gopay.BodyMap{}
|
||||||
@@ -73,17 +73,17 @@ func (pp *WechatPaymentProvider) Pay(providerName string, productName string, pa
|
|||||||
|
|
||||||
wxRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
wxRsp, err := pp.Client.V3TransactionNative(context.Background(), bm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", "", err
|
||||||
}
|
}
|
||||||
|
|
||||||
if wxRsp.Code != wechat.Success {
|
if wxRsp.Code != wechat.Success {
|
||||||
return "", errors.New(wxRsp.Error)
|
return "", "", errors.New(wxRsp.Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
return wxRsp.Response.CodeUrl, nil
|
return wxRsp.Response.CodeUrl, "", nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string) (string, string, float64, string, string, error) {
|
func (pp *WechatPaymentProvider) Notify(request *http.Request, body []byte, authorityPublicKey string, orderId string) (string, string, float64, string, string, error) {
|
||||||
notifyReq, err := wechat.V3ParseNotify(request)
|
notifyReq, err := wechat.V3ParseNotify(request)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
|
@@ -15,6 +15,7 @@
|
|||||||
package proxy
|
package proxy
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
@@ -71,7 +72,7 @@ func getProxyHttpClient() *http.Client {
|
|||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
tr := &http.Transport{Dial: dialer.Dial}
|
tr := &http.Transport{Dial: dialer.Dial, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||||
return &http.Client{
|
return &http.Client{
|
||||||
Transport: tr,
|
Transport: tr,
|
||||||
}
|
}
|
||||||
|
@@ -26,8 +26,10 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type Object struct {
|
type Object struct {
|
||||||
Owner string `json:"owner"`
|
Owner string `json:"owner"`
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
|
AccessKey string `json:"accessKey"`
|
||||||
|
AccessSecret string `json:"accessSecret"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUsername(ctx *context.Context) (username string) {
|
func getUsername(ctx *context.Context) (username string) {
|
||||||
@@ -43,6 +45,9 @@ func getUsername(ctx *context.Context) (username string) {
|
|||||||
username = getUsernameByClientIdSecret(ctx)
|
username = getUsernameByClientIdSecret(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if username == "" {
|
||||||
|
username = getUsernameByKeys(ctx)
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -98,6 +103,30 @@ func getObject(ctx *context.Context) (string, string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getKeys(ctx *context.Context) (string, string) {
|
||||||
|
method := ctx.Request.Method
|
||||||
|
|
||||||
|
if method == http.MethodGet {
|
||||||
|
accessKey := ctx.Input.Query("accessKey")
|
||||||
|
accessSecret := ctx.Input.Query("accessSecret")
|
||||||
|
return accessKey, accessSecret
|
||||||
|
} else {
|
||||||
|
body := ctx.Input.RequestBody
|
||||||
|
|
||||||
|
if len(body) == 0 {
|
||||||
|
return ctx.Request.Form.Get("accessKey"), ctx.Request.Form.Get("accessSecret")
|
||||||
|
}
|
||||||
|
|
||||||
|
var obj Object
|
||||||
|
err := json.Unmarshal(body, &obj)
|
||||||
|
if err != nil {
|
||||||
|
return "", ""
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj.AccessKey, obj.AccessSecret
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func willLog(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
func willLog(subOwner string, subName string, method string, urlPath string, objOwner string, objName string) bool {
|
||||||
if subOwner == "anonymous" && subName == "anonymous" && method == "GET" && (urlPath == "/api/get-account" || urlPath == "/api/get-app-login") && objOwner == "" && objName == "" {
|
if subOwner == "anonymous" && subName == "anonymous" && method == "GET" && (urlPath == "/api/get-account" || urlPath == "/api/get-app-login") && objOwner == "" && objName == "" {
|
||||||
return false
|
return false
|
||||||
|
@@ -49,7 +49,7 @@ func AutoSigninFilter(ctx *context.Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
userId := util.GetId(token.Organization, token.User)
|
userId := util.GetId(token.Organization, token.User)
|
||||||
application, _, err := object.GetApplicationByUserId(fmt.Sprintf("app/%s", token.Application))
|
application, err := object.GetApplicationByUserId(fmt.Sprintf("app/%s", token.Application))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
panic(err)
|
||||||
}
|
}
|
||||||
|
@@ -84,6 +84,19 @@ func getUsernameByClientIdSecret(ctx *context.Context) string {
|
|||||||
return fmt.Sprintf("app/%s", application.Name)
|
return fmt.Sprintf("app/%s", application.Name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func getUsernameByKeys(ctx *context.Context) string {
|
||||||
|
accessKey, accessSecret := getKeys(ctx)
|
||||||
|
user, err := object.GetUserByAccessKey(accessKey)
|
||||||
|
if err != nil {
|
||||||
|
panic(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if user != nil && accessSecret == user.AccessSecret {
|
||||||
|
return user.GetId()
|
||||||
|
}
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
func getSessionUser(ctx *context.Context) string {
|
func getSessionUser(ctx *context.Context) string {
|
||||||
user := ctx.Input.CruSession.Get("username")
|
user := ctx.Input.CruSession.Get("username")
|
||||||
if user == nil {
|
if user == nil {
|
||||||
|
@@ -73,9 +73,17 @@ func initAPI() {
|
|||||||
beego.Router("/api/get-user-count", &controllers.ApiController{}, "GET:GetUserCount")
|
beego.Router("/api/get-user-count", &controllers.ApiController{}, "GET:GetUserCount")
|
||||||
beego.Router("/api/get-user", &controllers.ApiController{}, "GET:GetUser")
|
beego.Router("/api/get-user", &controllers.ApiController{}, "GET:GetUser")
|
||||||
beego.Router("/api/update-user", &controllers.ApiController{}, "POST:UpdateUser")
|
beego.Router("/api/update-user", &controllers.ApiController{}, "POST:UpdateUser")
|
||||||
|
beego.Router("/api/add-user-keys", &controllers.ApiController{}, "POST:AddUserkeys")
|
||||||
beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser")
|
beego.Router("/api/add-user", &controllers.ApiController{}, "POST:AddUser")
|
||||||
beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser")
|
beego.Router("/api/delete-user", &controllers.ApiController{}, "POST:DeleteUser")
|
||||||
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
|
beego.Router("/api/upload-users", &controllers.ApiController{}, "POST:UploadUsers")
|
||||||
|
beego.Router("/api/remove-user-from-group", &controllers.ApiController{}, "POST:RemoveUserFromGroup")
|
||||||
|
|
||||||
|
beego.Router("/api/get-groups", &controllers.ApiController{}, "GET:GetGroups")
|
||||||
|
beego.Router("/api/get-group", &controllers.ApiController{}, "GET:GetGroup")
|
||||||
|
beego.Router("/api/update-group", &controllers.ApiController{}, "POST:UpdateGroup")
|
||||||
|
beego.Router("/api/add-group", &controllers.ApiController{}, "POST:AddGroup")
|
||||||
|
beego.Router("/api/delete-group", &controllers.ApiController{}, "POST:DeleteGroup")
|
||||||
|
|
||||||
beego.Router("/api/get-roles", &controllers.ApiController{}, "GET:GetRoles")
|
beego.Router("/api/get-roles", &controllers.ApiController{}, "GET:GetRoles")
|
||||||
beego.Router("/api/get-role", &controllers.ApiController{}, "GET:GetRole")
|
beego.Router("/api/get-role", &controllers.ApiController{}, "GET:GetRole")
|
||||||
|
@@ -29,7 +29,7 @@ func NewMinIOS3StorageProvider(clientId string, clientSecret string, region stri
|
|||||||
Endpoint: endpoint,
|
Endpoint: endpoint,
|
||||||
S3Endpoint: endpoint,
|
S3Endpoint: endpoint,
|
||||||
ACL: awss3.BucketCannedACLPublicRead,
|
ACL: awss3.BucketCannedACLPublicRead,
|
||||||
S3ForcePathStyle: false,
|
S3ForcePathStyle: true,
|
||||||
})
|
})
|
||||||
|
|
||||||
return sp
|
return sp
|
||||||
|
@@ -777,6 +777,46 @@
|
|||||||
"operationId": "ApiController.HandleOfficialAccountEvent"
|
"operationId": "ApiController.HandleOfficialAccountEvent"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/batch-enforce": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Enforce API"
|
||||||
|
],
|
||||||
|
"description": "perform enforce",
|
||||||
|
"operationId": "ApiController.BatchEnforce",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "body",
|
||||||
|
"name": "body",
|
||||||
|
"description": "casbin request array",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/object.CasbinRequest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "permissionId",
|
||||||
|
"description": "permission id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "modelId",
|
||||||
|
"description": "model id",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The Response object",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controllers.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/buy-product": {
|
"/api/buy-product": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -1384,6 +1424,52 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/enforce": {
|
||||||
|
"post": {
|
||||||
|
"tags": [
|
||||||
|
"Enforce API"
|
||||||
|
],
|
||||||
|
"description": "perform enforce",
|
||||||
|
"operationId": "ApiController.Enforce",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "body",
|
||||||
|
"name": "body",
|
||||||
|
"description": "casbin request",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/object.CasbinRequest"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "permissionId",
|
||||||
|
"description": "permission id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "modelId",
|
||||||
|
"description": "model id",
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "resourceId",
|
||||||
|
"description": "resource id",
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The Response object",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controllers.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/get-account": {
|
"/api/get-account": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -1954,6 +2040,35 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/get-organization-names": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"Organization API"
|
||||||
|
],
|
||||||
|
"description": "get all organization names",
|
||||||
|
"operationId": "ApiController.GetOrganizationNames",
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"in": "query",
|
||||||
|
"name": "owner",
|
||||||
|
"description": "owner",
|
||||||
|
"required": true,
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The Response object",
|
||||||
|
"schema": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/object.Organization"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/get-organizations": {
|
"/api/get-organizations": {
|
||||||
"get": {
|
"get": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -2826,7 +2941,6 @@
|
|||||||
"in": "query",
|
"in": "query",
|
||||||
"name": "id",
|
"name": "id",
|
||||||
"description": "The id ( owner/name ) of the user",
|
"description": "The id ( owner/name ) of the user",
|
||||||
"required": true,
|
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -3062,6 +3176,23 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"/api/health": {
|
||||||
|
"get": {
|
||||||
|
"tags": [
|
||||||
|
"System API"
|
||||||
|
],
|
||||||
|
"description": "check if the system is live",
|
||||||
|
"operationId": "ApiController.Health",
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The Response object",
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/definitions/controllers.Response"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"/api/invoice-payment": {
|
"/api/invoice-payment": {
|
||||||
"post": {
|
"post": {
|
||||||
"tags": [
|
"tags": [
|
||||||
@@ -4501,11 +4632,11 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"definitions": {
|
"definitions": {
|
||||||
"1183.0x1400042eb70.false": {
|
"1225.0xc0002e2ae0.false": {
|
||||||
"title": "false",
|
"title": "false",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
"1217.0x1400042eba0.false": {
|
"1260.0xc0002e2b10.false": {
|
||||||
"title": "false",
|
"title": "false",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
@@ -4554,10 +4685,10 @@
|
|||||||
"type": "object",
|
"type": "object",
|
||||||
"properties": {
|
"properties": {
|
||||||
"data": {
|
"data": {
|
||||||
"$ref": "#/definitions/1183.0x1400042eb70.false"
|
"$ref": "#/definitions/1225.0xc0002e2ae0.false"
|
||||||
},
|
},
|
||||||
"data2": {
|
"data2": {
|
||||||
"$ref": "#/definitions/1217.0x1400042eba0.false"
|
"$ref": "#/definitions/1260.0xc0002e2b10.false"
|
||||||
},
|
},
|
||||||
"msg": {
|
"msg": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
@@ -4595,6 +4726,10 @@
|
|||||||
"title": "JSONWebKey",
|
"title": "JSONWebKey",
|
||||||
"type": "object"
|
"type": "object"
|
||||||
},
|
},
|
||||||
|
"object.\u0026{179844 0xc000a02f90 false}": {
|
||||||
|
"title": "\u0026{179844 0xc000a02f90 false}",
|
||||||
|
"type": "object"
|
||||||
|
},
|
||||||
"object.AccountItem": {
|
"object.AccountItem": {
|
||||||
"title": "AccountItem",
|
"title": "AccountItem",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -4693,6 +4828,9 @@
|
|||||||
"formCss": {
|
"formCss": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"formCssMobile": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"formOffset": {
|
"formOffset": {
|
||||||
"type": "integer",
|
"type": "integer",
|
||||||
"format": "int64"
|
"format": "int64"
|
||||||
@@ -4715,6 +4853,9 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"orgChoiceMode": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"organization": {
|
"organization": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -4772,6 +4913,13 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"object.CasbinRequest": {
|
||||||
|
"title": "CasbinRequest",
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/object.\u0026{179844 0xc000a02f90 false}"
|
||||||
|
}
|
||||||
|
},
|
||||||
"object.Cert": {
|
"object.Cert": {
|
||||||
"title": "Cert",
|
"title": "Cert",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -5008,6 +5156,18 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"object.MfaItem": {
|
||||||
|
"title": "MfaItem",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"rule": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"object.MfaProps": {
|
"object.MfaProps": {
|
||||||
"title": "MfaProps",
|
"title": "MfaProps",
|
||||||
"type": "object",
|
"type": "object",
|
||||||
@@ -5190,6 +5350,12 @@
|
|||||||
"masterPassword": {
|
"masterPassword": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"mfaItems": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/definitions/object.MfaItem"
|
||||||
|
}
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -5395,9 +5561,18 @@
|
|||||||
"displayName": {
|
"displayName": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"isEnabled": {
|
||||||
|
"type": "boolean"
|
||||||
|
},
|
||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"options": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
"owner": {
|
"owner": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -5411,9 +5586,6 @@
|
|||||||
},
|
},
|
||||||
"role": {
|
"role": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
|
||||||
"options": {
|
|
||||||
"type": "array"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -5737,6 +5909,9 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"object": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"organization": {
|
"organization": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6341,6 +6516,9 @@
|
|||||||
"passwordSalt": {
|
"passwordSalt": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"passwordType": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"patreon": {
|
"patreon": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6505,6 +6683,9 @@
|
|||||||
"name": {
|
"name": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"organization": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"phone": {
|
"phone": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
@@ -6634,4 +6815,4 @@
|
|||||||
"type": "object"
|
"type": "object"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@@ -502,6 +502,32 @@ paths:
|
|||||||
tags:
|
tags:
|
||||||
- HandleOfficialAccountEvent API
|
- HandleOfficialAccountEvent API
|
||||||
operationId: ApiController.HandleOfficialAccountEvent
|
operationId: ApiController.HandleOfficialAccountEvent
|
||||||
|
/api/batch-enforce:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Enforce API
|
||||||
|
description: perform enforce
|
||||||
|
operationId: ApiController.BatchEnforce
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: casbin request array
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/object.CasbinRequest'
|
||||||
|
- in: query
|
||||||
|
name: permissionId
|
||||||
|
description: permission id
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: modelId
|
||||||
|
description: model id
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The Response object
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controllers.Response'
|
||||||
/api/buy-product:
|
/api/buy-product:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
@@ -893,6 +919,36 @@ paths:
|
|||||||
description: The Response object
|
description: The Response object
|
||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/controllers.Response'
|
$ref: '#/definitions/controllers.Response'
|
||||||
|
/api/enforce:
|
||||||
|
post:
|
||||||
|
tags:
|
||||||
|
- Enforce API
|
||||||
|
description: perform enforce
|
||||||
|
operationId: ApiController.Enforce
|
||||||
|
parameters:
|
||||||
|
- in: body
|
||||||
|
name: body
|
||||||
|
description: casbin request
|
||||||
|
required: true
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/object.CasbinRequest'
|
||||||
|
- in: query
|
||||||
|
name: permissionId
|
||||||
|
description: permission id
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: modelId
|
||||||
|
description: model id
|
||||||
|
type: string
|
||||||
|
- in: query
|
||||||
|
name: resourceId
|
||||||
|
description: resource id
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The Response object
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controllers.Response'
|
||||||
/api/get-account:
|
/api/get-account:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -1267,6 +1323,25 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/object.Application'
|
$ref: '#/definitions/object.Application'
|
||||||
|
/api/get-organization-names:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- Organization API
|
||||||
|
description: get all organization names
|
||||||
|
operationId: ApiController.GetOrganizationNames
|
||||||
|
parameters:
|
||||||
|
- in: query
|
||||||
|
name: owner
|
||||||
|
description: owner
|
||||||
|
required: true
|
||||||
|
type: string
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The Response object
|
||||||
|
schema:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/object.Organization'
|
||||||
/api/get-organizations:
|
/api/get-organizations:
|
||||||
get:
|
get:
|
||||||
tags:
|
tags:
|
||||||
@@ -1841,7 +1916,6 @@ paths:
|
|||||||
- in: query
|
- in: query
|
||||||
name: id
|
name: id
|
||||||
description: The id ( owner/name ) of the user
|
description: The id ( owner/name ) of the user
|
||||||
required: true
|
|
||||||
type: string
|
type: string
|
||||||
- in: query
|
- in: query
|
||||||
name: owner
|
name: owner
|
||||||
@@ -1994,6 +2068,17 @@ paths:
|
|||||||
type: array
|
type: array
|
||||||
items:
|
items:
|
||||||
$ref: '#/definitions/object.Webhook'
|
$ref: '#/definitions/object.Webhook'
|
||||||
|
/api/health:
|
||||||
|
get:
|
||||||
|
tags:
|
||||||
|
- System API
|
||||||
|
description: check if the system is live
|
||||||
|
operationId: ApiController.Health
|
||||||
|
responses:
|
||||||
|
"200":
|
||||||
|
description: The Response object
|
||||||
|
schema:
|
||||||
|
$ref: '#/definitions/controllers.Response'
|
||||||
/api/invoice-payment:
|
/api/invoice-payment:
|
||||||
post:
|
post:
|
||||||
tags:
|
tags:
|
||||||
@@ -2940,10 +3025,10 @@ paths:
|
|||||||
schema:
|
schema:
|
||||||
$ref: '#/definitions/Response'
|
$ref: '#/definitions/Response'
|
||||||
definitions:
|
definitions:
|
||||||
1183.0x1400042eb70.false:
|
1225.0xc0002e2ae0.false:
|
||||||
title: "false"
|
title: "false"
|
||||||
type: object
|
type: object
|
||||||
1217.0x1400042eba0.false:
|
1260.0xc0002e2b10.false:
|
||||||
title: "false"
|
title: "false"
|
||||||
type: object
|
type: object
|
||||||
LaravelResponse:
|
LaravelResponse:
|
||||||
@@ -2979,9 +3064,9 @@ definitions:
|
|||||||
type: object
|
type: object
|
||||||
properties:
|
properties:
|
||||||
data:
|
data:
|
||||||
$ref: '#/definitions/1183.0x1400042eb70.false'
|
$ref: '#/definitions/1225.0xc0002e2ae0.false'
|
||||||
data2:
|
data2:
|
||||||
$ref: '#/definitions/1217.0x1400042eba0.false'
|
$ref: '#/definitions/1260.0xc0002e2b10.false'
|
||||||
msg:
|
msg:
|
||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
@@ -3005,6 +3090,9 @@ definitions:
|
|||||||
jose.JSONWebKey:
|
jose.JSONWebKey:
|
||||||
title: JSONWebKey
|
title: JSONWebKey
|
||||||
type: object
|
type: object
|
||||||
|
object.&{179844 0xc000a02f90 false}:
|
||||||
|
title: '&{179844 0xc000a02f90 false}'
|
||||||
|
type: object
|
||||||
object.AccountItem:
|
object.AccountItem:
|
||||||
title: AccountItem
|
title: AccountItem
|
||||||
type: object
|
type: object
|
||||||
@@ -3072,6 +3160,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
formCss:
|
formCss:
|
||||||
type: string
|
type: string
|
||||||
|
formCssMobile:
|
||||||
|
type: string
|
||||||
formOffset:
|
formOffset:
|
||||||
type: integer
|
type: integer
|
||||||
format: int64
|
format: int64
|
||||||
@@ -3087,6 +3177,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
orgChoiceMode:
|
||||||
|
type: string
|
||||||
organization:
|
organization:
|
||||||
type: string
|
type: string
|
||||||
organizationObj:
|
organizationObj:
|
||||||
@@ -3124,6 +3216,11 @@ definitions:
|
|||||||
$ref: '#/definitions/object.ThemeData'
|
$ref: '#/definitions/object.ThemeData'
|
||||||
tokenFormat:
|
tokenFormat:
|
||||||
type: string
|
type: string
|
||||||
|
object.CasbinRequest:
|
||||||
|
title: CasbinRequest
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/object.&{179844 0xc000a02f90 false}'
|
||||||
object.Cert:
|
object.Cert:
|
||||||
title: Cert
|
title: Cert
|
||||||
type: object
|
type: object
|
||||||
@@ -3284,6 +3381,14 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
text:
|
text:
|
||||||
type: string
|
type: string
|
||||||
|
object.MfaItem:
|
||||||
|
title: MfaItem
|
||||||
|
type: object
|
||||||
|
properties:
|
||||||
|
name:
|
||||||
|
type: string
|
||||||
|
rule:
|
||||||
|
type: string
|
||||||
object.MfaProps:
|
object.MfaProps:
|
||||||
title: MfaProps
|
title: MfaProps
|
||||||
type: object
|
type: object
|
||||||
@@ -3407,6 +3512,10 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
masterPassword:
|
masterPassword:
|
||||||
type: string
|
type: string
|
||||||
|
mfaItems:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
$ref: '#/definitions/object.MfaItem'
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
owner:
|
owner:
|
||||||
@@ -3544,8 +3653,14 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
displayName:
|
displayName:
|
||||||
type: string
|
type: string
|
||||||
|
isEnabled:
|
||||||
|
type: boolean
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
options:
|
||||||
|
type: array
|
||||||
|
items:
|
||||||
|
type: string
|
||||||
owner:
|
owner:
|
||||||
type: string
|
type: string
|
||||||
pricePerMonth:
|
pricePerMonth:
|
||||||
@@ -3556,8 +3671,6 @@ definitions:
|
|||||||
format: double
|
format: double
|
||||||
role:
|
role:
|
||||||
type: string
|
type: string
|
||||||
options:
|
|
||||||
type: array
|
|
||||||
object.Pricing:
|
object.Pricing:
|
||||||
title: Pricing
|
title: Pricing
|
||||||
type: object
|
type: object
|
||||||
@@ -3775,6 +3888,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
object:
|
||||||
|
type: string
|
||||||
organization:
|
organization:
|
||||||
type: string
|
type: string
|
||||||
owner:
|
owner:
|
||||||
@@ -4181,6 +4296,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
passwordSalt:
|
passwordSalt:
|
||||||
type: string
|
type: string
|
||||||
|
passwordType:
|
||||||
|
type: string
|
||||||
patreon:
|
patreon:
|
||||||
type: string
|
type: string
|
||||||
paypal:
|
paypal:
|
||||||
@@ -4291,6 +4408,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
name:
|
name:
|
||||||
type: string
|
type: string
|
||||||
|
organization:
|
||||||
|
type: string
|
||||||
phone:
|
phone:
|
||||||
type: string
|
type: string
|
||||||
picture:
|
picture:
|
||||||
|
@@ -278,3 +278,13 @@ func GetEndPoint(endpoint string) string {
|
|||||||
}
|
}
|
||||||
return endpoint
|
return endpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HasString reports if slice has input string.
|
||||||
|
func HasString(strs []string, str string) bool {
|
||||||
|
for _, i := range strs {
|
||||||
|
if i == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
@@ -32,7 +32,7 @@ class AdapterEditPage extends React.Component {
|
|||||||
super(props);
|
super(props);
|
||||||
this.state = {
|
this.state = {
|
||||||
classes: props,
|
classes: props,
|
||||||
owner: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
adapterName: props.match.params.adapterName,
|
adapterName: props.match.params.adapterName,
|
||||||
adapter: null,
|
adapter: null,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
@@ -47,20 +47,25 @@ class AdapterEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getAdapter() {
|
getAdapter() {
|
||||||
AdapterBackend.getAdapter("admin", this.state.adapterName)
|
AdapterBackend.getAdapter(this.state.organizationName, this.state.adapterName)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
|
if (res.data === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
adapter: res.data,
|
adapter: res.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getModels(this.state.owner);
|
this.getModels(this.state.organizationName);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getOrganizations() {
|
getOrganizations() {
|
||||||
OrganizationBackend.getOrganizations(this.state.organizationName)
|
OrganizationBackend.getOrganizations("admin")
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
organizations: (res.msg === undefined) ? res : [],
|
organizations: (res.msg === undefined) ? res : [],
|
||||||
@@ -109,9 +114,8 @@ class AdapterEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.adapter.organization} onChange={(value => {
|
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.adapter.owner} onChange={(value => {
|
||||||
this.getModels(value);
|
this.getModels(value);
|
||||||
this.updateAdapterField("organization", value);
|
|
||||||
this.updateAdapterField("owner", value);
|
this.updateAdapterField("owner", value);
|
||||||
})}>
|
})}>
|
||||||
{
|
{
|
||||||
@@ -248,7 +252,7 @@ class AdapterEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
|
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22}>
|
<Col span={22}>
|
||||||
<PolicyTable owner={this.state.owner} name={this.state.adapterName} mode={this.state.mode} />
|
<PolicyTable owner={this.state.organizationName} name={this.state.adapterName} mode={this.state.mode} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
@@ -267,7 +271,7 @@ class AdapterEditPage extends React.Component {
|
|||||||
|
|
||||||
submitAdapterEdit(willExist) {
|
submitAdapterEdit(willExist) {
|
||||||
const adapter = Setting.deepCopy(this.state.adapter);
|
const adapter = Setting.deepCopy(this.state.adapter);
|
||||||
AdapterBackend.updateAdapter(this.state.owner, this.state.adapterName, adapter)
|
AdapterBackend.updateAdapter(this.state.organizationName, this.state.adapterName, adapter)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
@@ -278,7 +282,7 @@ class AdapterEditPage extends React.Component {
|
|||||||
if (willExist) {
|
if (willExist) {
|
||||||
this.props.history.push("/adapters");
|
this.props.history.push("/adapters");
|
||||||
} else {
|
} else {
|
||||||
this.props.history.push(`/adapters/${this.state.owner}/${this.state.adapter.name}`);
|
this.props.history.push(`/adapters/${this.state.organizationName}/${this.state.adapter.name}`);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
@@ -26,10 +26,9 @@ class AdapterListPage extends BaseListPage {
|
|||||||
newAdapter() {
|
newAdapter() {
|
||||||
const randomName = Setting.getRandomName();
|
const randomName = Setting.getRandomName();
|
||||||
return {
|
return {
|
||||||
owner: "admin",
|
owner: this.props.account.owner,
|
||||||
name: `adapter_${randomName}`,
|
name: `adapter_${randomName}`,
|
||||||
createdTime: moment().format(),
|
createdTime: moment().format(),
|
||||||
organization: this.props.account.owner,
|
|
||||||
type: "Database",
|
type: "Database",
|
||||||
host: "localhost",
|
host: "localhost",
|
||||||
port: 3306,
|
port: 3306,
|
||||||
@@ -47,7 +46,7 @@ class AdapterListPage extends BaseListPage {
|
|||||||
AdapterBackend.addAdapter(newAdapter)
|
AdapterBackend.addAdapter(newAdapter)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.props.history.push({pathname: `/adapters/${newAdapter.organization}/${newAdapter.name}`, mode: "add"});
|
this.props.history.push({pathname: `/adapters/${newAdapter.owner}/${newAdapter.name}`, mode: "add"});
|
||||||
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
} else {
|
} else {
|
||||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
@@ -96,11 +95,11 @@ class AdapterListPage extends BaseListPage {
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: i18next.t("general:Organization"),
|
title: i18next.t("general:Organization"),
|
||||||
dataIndex: "organization",
|
dataIndex: "owner",
|
||||||
key: "organization",
|
key: "owner",
|
||||||
width: "120px",
|
width: "120px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("organization"),
|
...this.getColumnSearchProps("owner"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<Link to={`/organizations/${text}`}>
|
<Link to={`/organizations/${text}`}>
|
||||||
@@ -247,11 +246,13 @@ class AdapterListPage extends BaseListPage {
|
|||||||
value = params.type;
|
value = params.type;
|
||||||
}
|
}
|
||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
AdapterBackend.getAdapters("admin", Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
AdapterBackend.getAdapters(Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -263,9 +264,10 @@ class AdapterListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -15,6 +15,9 @@
|
|||||||
import React, {Component} from "react";
|
import React, {Component} from "react";
|
||||||
import "./App.less";
|
import "./App.less";
|
||||||
import {Helmet} from "react-helmet";
|
import {Helmet} from "react-helmet";
|
||||||
|
import GroupTreePage from "./GroupTreePage";
|
||||||
|
import GroupEditPage from "./GroupEdit";
|
||||||
|
import GroupListPage from "./GroupList";
|
||||||
import * as Setting from "./Setting";
|
import * as Setting from "./Setting";
|
||||||
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
import {StyleProvider, legacyLogicalPropertiesTransformer} from "@ant-design/cssinjs";
|
||||||
import {BarsOutlined, CommentOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
import {BarsOutlined, CommentOutlined, DownOutlined, InfoCircleFilled, LogoutOutlined, SettingOutlined} from "@ant-design/icons";
|
||||||
@@ -128,10 +131,12 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
if (uri === "/") {
|
if (uri === "/") {
|
||||||
this.setState({selectedMenuKey: "/"});
|
this.setState({selectedMenuKey: "/"});
|
||||||
} else if (uri.includes("/organizations")) {
|
} else if (uri.includes("/organizations") || uri.includes("/trees")) {
|
||||||
this.setState({selectedMenuKey: "/organizations"});
|
this.setState({selectedMenuKey: "/organizations"});
|
||||||
} else if (uri.includes("/users")) {
|
} else if (uri.includes("/users")) {
|
||||||
this.setState({selectedMenuKey: "/users"});
|
this.setState({selectedMenuKey: "/users"});
|
||||||
|
} else if (uri.includes("/groups")) {
|
||||||
|
this.setState({selectedMenuKey: "/groups"});
|
||||||
} else if (uri.includes("/roles")) {
|
} else if (uri.includes("/roles")) {
|
||||||
this.setState({selectedMenuKey: "/roles"});
|
this.setState({selectedMenuKey: "/roles"});
|
||||||
} else if (uri.includes("/permissions")) {
|
} else if (uri.includes("/permissions")) {
|
||||||
@@ -223,7 +228,7 @@ class App extends Component {
|
|||||||
|
|
||||||
setLanguage(account) {
|
setLanguage(account) {
|
||||||
const language = account?.language;
|
const language = account?.language;
|
||||||
if (language !== "" && language !== i18next.language) {
|
if (language !== null && language !== "" && language !== i18next.language) {
|
||||||
Setting.setLanguage(language);
|
Setting.setLanguage(language);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -405,12 +410,13 @@ class App extends Component {
|
|||||||
|
|
||||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
||||||
|
|
||||||
if (Setting.isAdminUser(this.state.account)) {
|
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||||
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
||||||
"/organizations"));
|
"/organizations"));
|
||||||
}
|
|
||||||
|
|
||||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
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>,
|
res.push(Setting.getItem(<Link to="/users">{i18next.t("general:Users")}</Link>,
|
||||||
"/users"
|
"/users"
|
||||||
));
|
));
|
||||||
@@ -552,6 +558,10 @@ class App extends Component {
|
|||||||
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/organizations" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} onChangeTheme={this.setTheme} {...props} />)} />
|
<Route exact path="/organizations/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<OrganizationEditPage account={this.state.account} onChangeTheme={this.setTheme} {...props} />)} />
|
||||||
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/organizations/:organizationName/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/trees/:organizationName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/trees/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupTreePage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/groups" render={(props) => this.renderLoginIfNotLoggedIn(<GroupListPage account={this.state.account} {...props} />)} />
|
||||||
|
<Route exact path="/groups/:organizationName/:groupName" render={(props) => this.renderLoginIfNotLoggedIn(<GroupEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/users" render={(props) => this.renderLoginIfNotLoggedIn(<UserListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
|
<Route exact path="/users/:organizationName/:userName" render={(props) => <UserEditPage account={this.state.account} {...props} />} />
|
||||||
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/roles" render={(props) => this.renderLoginIfNotLoggedIn(<RoleListPage account={this.state.account} {...props} />)} />
|
||||||
@@ -585,11 +595,11 @@ class App extends Component {
|
|||||||
<Route exact path="/messages" render={(props) => this.renderLoginIfNotLoggedIn(<MessageListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/messages" render={(props) => this.renderLoginIfNotLoggedIn(<MessageListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/messages/:messageName" render={(props) => this.renderLoginIfNotLoggedIn(<MessageEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/messages/:messageName" render={(props) => this.renderLoginIfNotLoggedIn(<MessageEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/plans" render={(props) => this.renderLoginIfNotLoggedIn(<PlanListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/plan/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/plans/:organizationName/:planName" render={(props) => this.renderLoginIfNotLoggedIn(<PlanEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/pricings" render={(props) => this.renderLoginIfNotLoggedIn(<PricingListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/pricing/:organizationName/:pricingName" render={(props) => this.renderLoginIfNotLoggedIn(<PricingEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/pricings/:organizationName/:pricingName" render={(props) => this.renderLoginIfNotLoggedIn(<PricingEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/subscriptions" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/subscriptions" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/subscription/:organizationName/:subscriptionName" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/subscriptions/:organizationName/:subscriptionName" render={(props) => this.renderLoginIfNotLoggedIn(<SubscriptionEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products" render={(props) => this.renderLoginIfNotLoggedIn(<ProductListPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products/:productName" render={(props) => this.renderLoginIfNotLoggedIn(<ProductEditPage account={this.state.account} {...props} />)} />
|
||||||
<Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
|
<Route exact path="/products/:productName/buy" render={(props) => this.renderLoginIfNotLoggedIn(<ProductBuyPage account={this.state.account} {...props} />)} />
|
||||||
@@ -618,6 +628,11 @@ class App extends Component {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
isWithoutCard() {
|
||||||
|
return Setting.isMobile() || window.location.pathname === "/chat" ||
|
||||||
|
window.location.pathname.startsWith("/trees");
|
||||||
|
}
|
||||||
|
|
||||||
renderContent() {
|
renderContent() {
|
||||||
const onClick = ({key}) => {
|
const onClick = ({key}) => {
|
||||||
if (key === "/swagger") {
|
if (key === "/swagger") {
|
||||||
@@ -628,7 +643,6 @@ class App extends Component {
|
|||||||
};
|
};
|
||||||
return (
|
return (
|
||||||
<Layout id="parent-area">
|
<Layout id="parent-area">
|
||||||
{/* https://github.com/ant-design/ant-design/issues/40394 ant design bug. If it will be fixed, we can delete the code for control the color of Header*/}
|
|
||||||
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm.includes("dark") ? "black" : "white"}}>
|
<Header style={{padding: "0", marginBottom: "3px", backgroundColor: this.state.themeAlgorithm.includes("dark") ? "black" : "white"}}>
|
||||||
{Setting.isMobile() ? null : (
|
{Setting.isMobile() ? null : (
|
||||||
<Link to={"/"}>
|
<Link to={"/"}>
|
||||||
@@ -664,7 +678,7 @@ class App extends Component {
|
|||||||
}
|
}
|
||||||
</Header>
|
</Header>
|
||||||
<Content style={{display: "flex", flexDirection: "column"}} >
|
<Content style={{display: "flex", flexDirection: "column"}} >
|
||||||
{(Setting.isMobile() || window.location.pathname === "/chat") ?
|
{this.isWithoutCard() ?
|
||||||
this.renderRouter() :
|
this.renderRouter() :
|
||||||
<Card className="content-warp-card">
|
<Card className="content-warp-card">
|
||||||
{this.renderRouter()}
|
{this.renderRouter()}
|
||||||
|
@@ -74,7 +74,6 @@ img {
|
|||||||
|
|
||||||
.content-warp-card {
|
.content-warp-card {
|
||||||
box-shadow: 0 1px 5px 0 rgb(51 51 51 / 14%);
|
box-shadow: 0 1px 5px 0 rgb(51 51 51 / 14%);
|
||||||
margin: 5px;
|
|
||||||
flex: 1;
|
flex: 1;
|
||||||
align-items: stretch;
|
align-items: stretch;
|
||||||
}
|
}
|
||||||
|
@@ -119,6 +119,11 @@ class ApplicationEditPage extends React.Component {
|
|||||||
getApplication() {
|
getApplication() {
|
||||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||||
.then((application) => {
|
.then((application) => {
|
||||||
|
if (application === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
|
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
|
||||||
application.grantTypes = ["authorization_code"];
|
application.grantTypes = ["authorization_code"];
|
||||||
}
|
}
|
||||||
@@ -155,11 +160,16 @@ class ApplicationEditPage extends React.Component {
|
|||||||
}
|
}
|
||||||
|
|
||||||
getProviders() {
|
getProviders() {
|
||||||
ProviderBackend.getProviders(this.state.owner).then((res => {
|
ProviderBackend.getProviders(this.state.owner)
|
||||||
this.setState({
|
.then((res) => {
|
||||||
providers: res,
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
providers: res.data,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
getSamlMetadata() {
|
getSamlMetadata() {
|
||||||
@@ -564,7 +574,7 @@ class ApplicationEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("application:Grant types"), i18next.t("application:Grant types - Tooltip"))} :
|
{Setting.getLabel(i18next.t("application:Grant types"), i18next.t("application:Grant types - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} mode="tags" style={{width: "100%"}}
|
<Select virtual={false} mode="multiple" style={{width: "100%"}}
|
||||||
value={this.state.application.grantTypes}
|
value={this.state.application.grantTypes}
|
||||||
onChange={(value => {
|
onChange={(value => {
|
||||||
this.updateApplicationField("grantTypes", value);
|
this.updateApplicationField("grantTypes", value);
|
||||||
|
@@ -276,9 +276,11 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
(Setting.isAdminUser(this.props.account) ? ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) :
|
(Setting.isAdminUser(this.props.account) ? ApplicationBackend.getApplications("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder) :
|
||||||
ApplicationBackend.getApplicationsByOrganization("admin", this.props.account.organization.name, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
ApplicationBackend.getApplicationsByOrganization("admin", this.props.account.organization.name, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -290,9 +292,10 @@ class ApplicationListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -45,6 +45,11 @@ class CertEditPage extends React.Component {
|
|||||||
getCert() {
|
getCert() {
|
||||||
CertBackend.getCert(this.state.owner, this.state.certName)
|
CertBackend.getCert(this.state.owner, this.state.certName)
|
||||||
.then((cert) => {
|
.then((cert) => {
|
||||||
|
if (cert === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
cert: cert,
|
cert: cert,
|
||||||
});
|
});
|
||||||
|
@@ -108,7 +108,7 @@ class CertListPage extends BaseListPage {
|
|||||||
key: "owner",
|
key: "owner",
|
||||||
width: "150px",
|
width: "150px",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("organization"),
|
...this.getColumnSearchProps("owner"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (text !== "admin") ? text : i18next.t("provider:admin (Shared)");
|
return (text !== "admin") ? text : i18next.t("provider:admin (Shared)");
|
||||||
},
|
},
|
||||||
@@ -239,9 +239,11 @@ class CertListPage extends BaseListPage {
|
|||||||
(Setting.isAdminUser(this.props.account) ? CertBackend.getGlobleCerts(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
(Setting.isAdminUser(this.props.account) ? CertBackend.getGlobleCerts(params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
: CertBackend.getCerts(this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
: CertBackend.getCerts(this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -253,9 +255,10 @@ class CertListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -41,6 +41,11 @@ class ChatEditPage extends React.Component {
|
|||||||
getChat() {
|
getChat() {
|
||||||
ChatBackend.getChat("admin", this.state.chatName)
|
ChatBackend.getChat("admin", this.state.chatName)
|
||||||
.then((chat) => {
|
.then((chat) => {
|
||||||
|
if (chat === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
chat: chat,
|
chat: chat,
|
||||||
});
|
});
|
||||||
@@ -175,7 +180,7 @@ class ChatEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("general:Users"), i18next.t("chat:Users - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Users"), i18next.t("chat:Users - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select mode="tags" style={{width: "100%"}} value={this.state.chat.users}
|
<Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.chat.users}
|
||||||
onChange={(value => {this.updateChatField("users", value);})}
|
onChange={(value => {this.updateChatField("users", value);})}
|
||||||
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
|
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
|
||||||
/>
|
/>
|
||||||
|
@@ -82,6 +82,7 @@ class ChatListPage extends BaseListPage {
|
|||||||
dataIndex: "organization",
|
dataIndex: "organization",
|
||||||
key: "organization",
|
key: "organization",
|
||||||
width: "150px",
|
width: "150px",
|
||||||
|
fixed: "left",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("organization"),
|
...this.getColumnSearchProps("organization"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -165,7 +166,6 @@ class ChatListPage extends BaseListPage {
|
|||||||
dataIndex: "user1",
|
dataIndex: "user1",
|
||||||
key: "user1",
|
key: "user1",
|
||||||
width: "120px",
|
width: "120px",
|
||||||
fixed: "left",
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("user1"),
|
...this.getColumnSearchProps("user1"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -181,7 +181,6 @@ class ChatListPage extends BaseListPage {
|
|||||||
dataIndex: "user2",
|
dataIndex: "user2",
|
||||||
key: "user2",
|
key: "user2",
|
||||||
width: "120px",
|
width: "120px",
|
||||||
fixed: "left",
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("user2"),
|
...this.getColumnSearchProps("user2"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -268,9 +267,11 @@ class ChatListPage extends BaseListPage {
|
|||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
ChatBackend.getChats("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
ChatBackend.getChats("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -282,9 +283,10 @@ class ChatListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -278,9 +278,10 @@ class ChatPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -102,7 +102,7 @@ class EntryPage extends React.Component {
|
|||||||
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
<Route exact path="/result/:applicationName" render={(props) => this.renderHomeIfLoggedIn(<ResultPage {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
||||||
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
<Route exact path="/cas/:owner/:casApplicationName/logout" render={(props) => this.renderHomeIfLoggedIn(<CasLogout {...this.props} application={this.state.application} onUpdateApplication={onUpdateApplication} {...props} />)} />
|
||||||
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
|
<Route exact path="/cas/:owner/:casApplicationName/login" render={(props) => {return (<LoginPage {...this.props} application={this.state.application} type={"cas"} mode={"signin"} onUpdateApplication={onUpdateApplication} {...props} />);}} />
|
||||||
<Route exact path="/select-plan/:pricingName" render={(props) => this.renderHomeIfLoggedIn(<PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />)} />
|
<Route exact path="/select-plan/:owner/:pricingName" render={(props) => this.renderHomeIfLoggedIn(<PricingPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />)} />
|
||||||
</Switch>
|
</Switch>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
268
web/src/GroupEdit.js
Normal file
268
web/src/GroupEdit.js
Normal file
@@ -0,0 +1,268 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Button, Card, Col, Input, Row, Select, Switch} from "antd";
|
||||||
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
|
import * as OrganizationBackend from "./backend/OrganizationBackend";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
import i18next from "i18next";
|
||||||
|
|
||||||
|
class GroupEditPage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
classes: props,
|
||||||
|
groupName: props.match.params.groupName,
|
||||||
|
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
|
group: null,
|
||||||
|
users: [],
|
||||||
|
groups: [],
|
||||||
|
organizations: [],
|
||||||
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
this.getGroup();
|
||||||
|
this.getGroups(this.state.organizationName);
|
||||||
|
this.getOrganizations();
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroup() {
|
||||||
|
GroupBackend.getGroup(this.state.organizationName, this.state.groupName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
if (res.data === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
group: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroups(organizationName) {
|
||||||
|
GroupBackend.getGroups(organizationName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
groups: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getOrganizations() {
|
||||||
|
OrganizationBackend.getOrganizationNames("admin")
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
organizations: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
parseGroupField(key, value) {
|
||||||
|
if ([""].includes(key)) {
|
||||||
|
value = Setting.myParseInt(value);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
updateGroupField(key, value) {
|
||||||
|
value = this.parseGroupField(key, value);
|
||||||
|
|
||||||
|
const group = this.state.group;
|
||||||
|
group[key] = value;
|
||||||
|
this.setState({
|
||||||
|
group: group,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
getParentIdOptions() {
|
||||||
|
const groups = this.state.groups.filter((group) => group.id !== this.state.group.id);
|
||||||
|
const organization = this.state.organizations.find((organization) => organization.name === this.state.group.owner);
|
||||||
|
if (organization !== undefined) {
|
||||||
|
groups.push({id: organization.name, displayName: organization.displayName});
|
||||||
|
}
|
||||||
|
return groups.map((group) => ({label: group.displayName, value: group.id}));
|
||||||
|
}
|
||||||
|
|
||||||
|
renderGroup() {
|
||||||
|
return (
|
||||||
|
<Card size="small" title={
|
||||||
|
<div>
|
||||||
|
{this.state.mode === "add" ? i18next.t("group:New Group") : i18next.t("group:Edit Group")}
|
||||||
|
<Button onClick={() => this.submitGroupEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||||
|
<Button style={{marginLeft: "20px"}} type="primary" onClick={() => this.submitGroupEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||||
|
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} onClick={() => this.deleteGroup()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
style={(Setting.isMobile()) ? {margin: "5px"} : {}}
|
||||||
|
type="inner"
|
||||||
|
>
|
||||||
|
<Row style={{marginTop: "10px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account)} value={this.state.group.owner}
|
||||||
|
onChange={(value => {
|
||||||
|
this.updateGroupField("owner", value);
|
||||||
|
this.getGroups(value);
|
||||||
|
})}
|
||||||
|
options={this.state.organizations.map((organization) => Setting.getOption(organization.displayName, organization.name))
|
||||||
|
} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Name"), i18next.t("general:Name - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.group.name} onChange={e => {
|
||||||
|
this.updateGroupField("name", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Display name"), i18next.t("general:Display name - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.group.displayName} onChange={e => {
|
||||||
|
this.updateGroupField("displayName", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Type"), i18next.t("general:Type - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select style={{width: "100%"}}
|
||||||
|
options={
|
||||||
|
[
|
||||||
|
{label: i18next.t("group:Virtual"), value: "Virtual"},
|
||||||
|
{label: i18next.t("group:Physical"), value: "Physical"},
|
||||||
|
].map((item) => ({label: item.label, value: item.value}))
|
||||||
|
}
|
||||||
|
value={this.state.group.type} onChange={(value => {
|
||||||
|
this.updateGroupField("type", value);
|
||||||
|
}
|
||||||
|
)} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("group:Parent group"), i18next.t("group:Parent group - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select style={{width: "100%"}}
|
||||||
|
options={this.getParentIdOptions()}
|
||||||
|
value={this.state.group.parentId} onChange={(value => {
|
||||||
|
this.updateGroupField("parentId", value);
|
||||||
|
}
|
||||||
|
)} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={1} >
|
||||||
|
<Switch checked={this.state.group.isEnabled} onChange={checked => {
|
||||||
|
this.updateGroupField("isEnabled", checked);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
submitGroupEdit(willExist) {
|
||||||
|
const group = Setting.deepCopy(this.state.group);
|
||||||
|
group["isTopGroup"] = this.state.organizations.some((organization) => organization.name === group.parentId);
|
||||||
|
|
||||||
|
GroupBackend.updateGroup(this.state.organizationName, this.state.groupName, group)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||||
|
this.setState({
|
||||||
|
groupName: this.state.group.name,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (willExist) {
|
||||||
|
const groupTreeUrl = sessionStorage.getItem("groupTreeUrl");
|
||||||
|
if (groupTreeUrl !== null) {
|
||||||
|
sessionStorage.removeItem("groupTreeUrl");
|
||||||
|
this.props.history.push(groupTreeUrl);
|
||||||
|
} else {
|
||||||
|
this.props.history.push("/groups");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.props.history.push(`/groups/${this.state.group.owner}/${this.state.group.name}`);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||||
|
this.updateGroupField("name", this.state.groupName);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup() {
|
||||||
|
GroupBackend.deleteGroup(this.state.group)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
const groupTreeUrl = sessionStorage.getItem("groupTreeUrl");
|
||||||
|
if (groupTreeUrl !== null) {
|
||||||
|
sessionStorage.removeItem("groupTreeUrl");
|
||||||
|
this.props.history.push(groupTreeUrl);
|
||||||
|
} else {
|
||||||
|
this.props.history.push("/groups");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
{
|
||||||
|
this.state.group !== null ? this.renderGroup() : null
|
||||||
|
}
|
||||||
|
<div style={{marginTop: "20px", marginLeft: "40px"}}>
|
||||||
|
<Button size="large" onClick={() => this.submitGroupEdit(false)}>{i18next.t("general:Save")}</Button>
|
||||||
|
<Button style={{marginLeft: "20px"}} type="primary" size="large" onClick={() => this.submitGroupEdit(true)}>{i18next.t("general:Save & Exit")}</Button>
|
||||||
|
{this.state.mode === "add" ? <Button style={{marginLeft: "20px"}} size="large" onClick={() => this.deleteGroup()}>{i18next.t("general:Cancel")}</Button> : null}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupEditPage;
|
286
web/src/GroupList.js
Normal file
286
web/src/GroupList.js
Normal file
@@ -0,0 +1,286 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import React from "react";
|
||||||
|
import {Link} from "react-router-dom";
|
||||||
|
import {Button, Table} from "antd";
|
||||||
|
import moment from "moment";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import BaseListPage from "./BaseListPage";
|
||||||
|
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||||
|
|
||||||
|
class GroupListPage extends BaseListPage {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
...this.state,
|
||||||
|
owner: Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner,
|
||||||
|
groups: [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
super.UNSAFE_componentWillMount();
|
||||||
|
this.getGroups(this.state.owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
getGroups(organizationName) {
|
||||||
|
GroupBackend.getGroups(organizationName)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
groups: res.data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
newGroup() {
|
||||||
|
const randomName = Setting.getRandomName();
|
||||||
|
return {
|
||||||
|
owner: this.props.account.owner,
|
||||||
|
name: `group_${randomName}`,
|
||||||
|
createdTime: moment().format(),
|
||||||
|
updatedTime: moment().format(),
|
||||||
|
displayName: `New Group - ${randomName}`,
|
||||||
|
type: "Virtual",
|
||||||
|
parentId: this.props.account.owner,
|
||||||
|
isTopGroup: true,
|
||||||
|
isEnabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addGroup() {
|
||||||
|
const newGroup = this.newGroup();
|
||||||
|
GroupBackend.addGroup(newGroup)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.props.history.push({pathname: `/groups/${newGroup.owner}/${newGroup.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
deleteGroup(i) {
|
||||||
|
GroupBackend.deleteGroup(this.state.data[i])
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
|
this.setState({
|
||||||
|
data: Setting.deleteRow(this.state.data, i),
|
||||||
|
pagination: {total: this.state.pagination.total - 1},
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
renderTable(data) {
|
||||||
|
const columns = [
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Name"),
|
||||||
|
dataIndex: "name",
|
||||||
|
key: "name",
|
||||||
|
width: "120px",
|
||||||
|
fixed: "left",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("name"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/groups/${record.owner}/${text}`}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Organization"),
|
||||||
|
dataIndex: "owner",
|
||||||
|
key: "owner",
|
||||||
|
width: "120px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("owner"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return (
|
||||||
|
<Link to={`/organizations/${text}`}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Created time"),
|
||||||
|
dataIndex: "createdTime",
|
||||||
|
key: "createdTime",
|
||||||
|
width: "150px",
|
||||||
|
sorter: true,
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return Setting.getFormattedDate(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Updated time"),
|
||||||
|
dataIndex: "updatedTime",
|
||||||
|
key: "updatedTime",
|
||||||
|
width: "150px",
|
||||||
|
sorter: true,
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return Setting.getFormattedDate(text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Display name"),
|
||||||
|
dataIndex: "displayName",
|
||||||
|
key: "displayName",
|
||||||
|
width: "100px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("displayName"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Type"),
|
||||||
|
dataIndex: "type",
|
||||||
|
key: "type",
|
||||||
|
width: "110px",
|
||||||
|
sorter: true,
|
||||||
|
filterMultiple: false,
|
||||||
|
filters: [
|
||||||
|
{text: i18next.t("group:Virtual"), value: "Virtual"},
|
||||||
|
{text: i18next.t("group:Physical"), value: "Physical"},
|
||||||
|
],
|
||||||
|
render: (text, record, index) => {
|
||||||
|
return i18next.t("group:" + text);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("group:Parent group"),
|
||||||
|
dataIndex: "parentId",
|
||||||
|
key: "parentId",
|
||||||
|
width: "110px",
|
||||||
|
sorter: true,
|
||||||
|
...this.getColumnSearchProps("parentId"),
|
||||||
|
render: (text, record, index) => {
|
||||||
|
if (record.isTopGroup) {
|
||||||
|
return <Link to={`/organizations/${record.parentId}`}>
|
||||||
|
{record.parentId}
|
||||||
|
</Link>;
|
||||||
|
}
|
||||||
|
const parentGroup = this.state.groups.find((group) => group.id === text);
|
||||||
|
if (parentGroup === undefined) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return <Link to={`/groups/${parentGroup.owner}/${parentGroup.name}`}>
|
||||||
|
{parentGroup?.displayName}
|
||||||
|
</Link>;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: i18next.t("general:Action"),
|
||||||
|
dataIndex: "",
|
||||||
|
key: "op",
|
||||||
|
width: "170px",
|
||||||
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
|
render: (text, record, index) => {
|
||||||
|
const haveChildren = this.state.groups.find((group) => group.parentId === record.id) !== undefined;
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/groups/${record.owner}/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
|
<PopconfirmModal
|
||||||
|
disabled={haveChildren}
|
||||||
|
title={i18next.t("general:Sure to delete") + `: ${record.name} ?`}
|
||||||
|
onConfirm={() => this.deleteGroup(index)}
|
||||||
|
>
|
||||||
|
</PopconfirmModal>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
const paginationProps = {
|
||||||
|
total: this.state.pagination.total,
|
||||||
|
showQuickJumper: true,
|
||||||
|
showSizeChanger: true,
|
||||||
|
showTotal: () => i18next.t("general:{total} in total").replace("{total}", this.state.pagination.total),
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<Table scroll={{x: "max-content"}} columns={columns} dataSource={data} rowKey={(record) => `${record.owner}/${record.name}`} size="middle" bordered pagination={paginationProps}
|
||||||
|
title={() => (
|
||||||
|
<div>
|
||||||
|
{i18next.t("general:Groups")}
|
||||||
|
<Button type="primary" size="small" onClick={this.addGroup.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
loading={this.state.loading}
|
||||||
|
onChange={this.handleTableChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fetch = (params = {}) => {
|
||||||
|
let field = params.searchedColumn, value = params.searchText;
|
||||||
|
const sortField = params.sortField, sortOrder = params.sortOrder;
|
||||||
|
if (params.category !== undefined && params.category !== null) {
|
||||||
|
field = "category";
|
||||||
|
value = params.category;
|
||||||
|
} else if (params.type !== undefined && params.type !== null) {
|
||||||
|
field = "type";
|
||||||
|
value = params.type;
|
||||||
|
}
|
||||||
|
this.setState({loading: true});
|
||||||
|
GroupBackend.getGroups(this.state.owner, false, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
data: res.data,
|
||||||
|
pagination: {
|
||||||
|
...params.pagination,
|
||||||
|
total: res.data2,
|
||||||
|
},
|
||||||
|
searchText: params.searchText,
|
||||||
|
searchedColumn: params.searchedColumn,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
if (Setting.isResponseDenied(res)) {
|
||||||
|
this.setState({
|
||||||
|
isAuthorized: false,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupListPage;
|
336
web/src/GroupTreePage.js
Normal file
336
web/src/GroupTreePage.js
Normal file
@@ -0,0 +1,336 @@
|
|||||||
|
// 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.
|
||||||
|
|
||||||
|
import {DeleteOutlined, EditOutlined, HolderOutlined, PlusOutlined, UsergroupAddOutlined} from "@ant-design/icons";
|
||||||
|
import {Button, Col, Empty, Row, Space, Tree} from "antd";
|
||||||
|
import i18next from "i18next";
|
||||||
|
import moment from "moment/moment";
|
||||||
|
import React from "react";
|
||||||
|
import * as GroupBackend from "./backend/GroupBackend";
|
||||||
|
import * as Setting from "./Setting";
|
||||||
|
import OrganizationSelect from "./common/select/OrganizationSelect";
|
||||||
|
import UserListPage from "./UserListPage";
|
||||||
|
|
||||||
|
class GroupTreePage extends React.Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
classes: props,
|
||||||
|
owner: Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner,
|
||||||
|
organizationName: props.organizationName !== undefined ? props.organizationName : props.match.params.organizationName,
|
||||||
|
groupName: this.props.match?.params.groupName,
|
||||||
|
groupId: undefined,
|
||||||
|
treeData: [],
|
||||||
|
selectedKeys: [this.props.match?.params.groupName],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
UNSAFE_componentWillMount() {
|
||||||
|
this.getTreeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||||
|
if (this.state.organizationName !== prevState.organizationName) {
|
||||||
|
this.getTreeData();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prevState.treeData !== this.state.treeData) {
|
||||||
|
this.setTreeExpandedKeys();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getTreeData() {
|
||||||
|
GroupBackend.getGroups(this.state.organizationName, true).then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
this.setState({
|
||||||
|
treeData: res.data,
|
||||||
|
groupId: this.findNodeId({children: res.data}, this.state.groupName),
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
findNodeId(node, targetName) {
|
||||||
|
if (node.key === targetName) {
|
||||||
|
return node.id;
|
||||||
|
}
|
||||||
|
if (node.children) {
|
||||||
|
for (let i = 0; i < node.children.length; i++) {
|
||||||
|
const result = this.findNodeId(node.children[i], targetName);
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
setTreeTitle(treeData) {
|
||||||
|
const haveChildren = Array.isArray(treeData.children) && treeData.children.length > 0;
|
||||||
|
const isSelected = this.state.groupName === treeData.key;
|
||||||
|
return {
|
||||||
|
id: treeData.id,
|
||||||
|
key: treeData.key,
|
||||||
|
title: <Space>
|
||||||
|
{treeData.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||||
|
<span>{treeData.title}</span>
|
||||||
|
{isSelected && (
|
||||||
|
<React.Fragment>
|
||||||
|
<PlusOutlined
|
||||||
|
style={{
|
||||||
|
visibility: "visible",
|
||||||
|
color: "inherit",
|
||||||
|
transition: "color 0.3s",
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.color = "inherit";
|
||||||
|
}}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.4)";
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
sessionStorage.setItem("groupTreeUrl", window.location.pathname);
|
||||||
|
this.addGroup();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<EditOutlined
|
||||||
|
style={{
|
||||||
|
visibility: "visible",
|
||||||
|
color: "inherit",
|
||||||
|
transition: "color 0.3s",
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.color = "inherit";
|
||||||
|
}}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.4)";
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
sessionStorage.setItem("groupTreeUrl", window.location.pathname);
|
||||||
|
this.props.history.push(`/groups/${this.state.organizationName}/${treeData.key}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{!haveChildren &&
|
||||||
|
<DeleteOutlined
|
||||||
|
style={{
|
||||||
|
visibility: "visible",
|
||||||
|
color: "inherit",
|
||||||
|
transition: "color 0.3s",
|
||||||
|
}}
|
||||||
|
onMouseEnter={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||||
|
}}
|
||||||
|
onMouseLeave={(e) => {
|
||||||
|
e.currentTarget.style.color = "inherit";
|
||||||
|
}}
|
||||||
|
onMouseDown={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.4)";
|
||||||
|
}}
|
||||||
|
onMouseUp={(e) => {
|
||||||
|
e.currentTarget.style.color = "rgba(89,54,213,0.6)";
|
||||||
|
}}
|
||||||
|
onClick={(e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
GroupBackend.deleteGroup({owner: treeData.owner, name: treeData.key})
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully deleted"));
|
||||||
|
this.getTreeData();
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
</React.Fragment>
|
||||||
|
)}
|
||||||
|
</Space>,
|
||||||
|
children: haveChildren ? treeData.children.map(i => this.setTreeTitle(i)) : [],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
setTreeExpandedKeys = () => {
|
||||||
|
const expandedKeys = [];
|
||||||
|
const setExpandedKeys = (nodes) => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
expandedKeys.push(node.key);
|
||||||
|
if (node.children) {
|
||||||
|
setExpandedKeys(node.children);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
setExpandedKeys(this.state.treeData);
|
||||||
|
this.setState({
|
||||||
|
expandedKeys: expandedKeys,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
renderTree() {
|
||||||
|
const onSelect = (selectedKeys, info) => {
|
||||||
|
this.setState({
|
||||||
|
selectedKeys: selectedKeys,
|
||||||
|
groupName: info.node.key,
|
||||||
|
groupId: info.node.id,
|
||||||
|
});
|
||||||
|
this.props.history.push(`/trees/${this.state.organizationName}/${info.node.key}`);
|
||||||
|
};
|
||||||
|
const onExpand = (expandedKeysValue) => {
|
||||||
|
this.setState({
|
||||||
|
expandedKeys: expandedKeysValue,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.state.treeData.length === 0) {
|
||||||
|
return <Empty />;
|
||||||
|
}
|
||||||
|
|
||||||
|
const treeData = this.state.treeData.map(i => this.setTreeTitle(i));
|
||||||
|
return (
|
||||||
|
<Tree
|
||||||
|
blockNode={true}
|
||||||
|
defaultSelectedKeys={[this.state.groupName]}
|
||||||
|
defaultExpandAll={true}
|
||||||
|
selectedKeys={this.state.selectedKeys}
|
||||||
|
expandedKeys={this.state.expandedKeys}
|
||||||
|
onSelect={onSelect}
|
||||||
|
onExpand={onExpand}
|
||||||
|
showIcon={true}
|
||||||
|
treeData={treeData}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
renderOrganizationSelect() {
|
||||||
|
if (Setting.isAdminUser(this.props.account)) {
|
||||||
|
return (
|
||||||
|
<OrganizationSelect
|
||||||
|
initValue={this.state.organizationName}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
onChange={(value) => {
|
||||||
|
this.setState({
|
||||||
|
organizationName: value,
|
||||||
|
});
|
||||||
|
this.props.history.push(`/trees/${value}`);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newGroup(isRoot) {
|
||||||
|
const randomName = Setting.getRandomName();
|
||||||
|
return {
|
||||||
|
owner: this.state.organizationName,
|
||||||
|
name: `group_${randomName}`,
|
||||||
|
createdTime: moment().format(),
|
||||||
|
updatedTime: moment().format(),
|
||||||
|
displayName: `New Group - ${randomName}`,
|
||||||
|
type: "Virtual",
|
||||||
|
parentId: isRoot ? this.state.organizationName : this.state.groupId,
|
||||||
|
isTopGroup: isRoot,
|
||||||
|
isEnabled: true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
addGroup(isRoot = false) {
|
||||||
|
const newGroup = this.newGroup(isRoot);
|
||||||
|
GroupBackend.addGroup(newGroup)
|
||||||
|
.then((res) => {
|
||||||
|
if (res.status === "ok") {
|
||||||
|
sessionStorage.setItem("groupTreeUrl", window.location.pathname);
|
||||||
|
this.props.history.push({pathname: `/groups/${newGroup.owner}/${newGroup.name}`, mode: "add"});
|
||||||
|
Setting.showMessage("success", i18next.t("general:Successfully added"));
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(error => {
|
||||||
|
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<div style={{
|
||||||
|
flex: 1,
|
||||||
|
backgroundColor: "white",
|
||||||
|
padding: "5px 5px 2px 5px",
|
||||||
|
}}>
|
||||||
|
<Row>
|
||||||
|
<Col span={5}>
|
||||||
|
<Row>
|
||||||
|
<Col span={24} style={{textAlign: "center"}}>
|
||||||
|
{this.renderOrganizationSelect()}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row>
|
||||||
|
<Col span={24} style={{marginTop: "10px"}}>
|
||||||
|
<Button size={"small"}
|
||||||
|
onClick={() => {
|
||||||
|
this.setState({
|
||||||
|
selectedKeys: [],
|
||||||
|
groupName: null,
|
||||||
|
groupId: undefined,
|
||||||
|
});
|
||||||
|
this.props.history.push(`/trees/${this.state.organizationName}`);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18next.t("group:Show all")}
|
||||||
|
</Button>
|
||||||
|
<Button size={"small"} type={"primary"} style={{marginLeft: "10px"}} onClick={() => this.addGroup(true)}>
|
||||||
|
{i18next.t("general:Add")}
|
||||||
|
</Button>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<Row style={{marginTop: 10}}>
|
||||||
|
<Col span={24} style={{textAlign: "left"}}>
|
||||||
|
{this.renderTree()}
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</Col>
|
||||||
|
<Col span={19}>
|
||||||
|
<UserListPage
|
||||||
|
organizationName={this.state.organizationName}
|
||||||
|
groupName={this.state.groupName}
|
||||||
|
groupId={this.state.groupId}
|
||||||
|
{...this.props}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default GroupTreePage;
|
@@ -46,6 +46,11 @@ class MessageEditPage extends React.Component {
|
|||||||
getMessage() {
|
getMessage() {
|
||||||
MessageBackend.getMessage("admin", this.state.messageName)
|
MessageBackend.getMessage("admin", this.state.messageName)
|
||||||
.then((message) => {
|
.then((message) => {
|
||||||
|
if (message === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
message: message,
|
message: message,
|
||||||
});
|
});
|
||||||
|
@@ -78,6 +78,7 @@ class MessageListPage extends BaseListPage {
|
|||||||
dataIndex: "organization",
|
dataIndex: "organization",
|
||||||
key: "organization",
|
key: "organization",
|
||||||
width: "150px",
|
width: "150px",
|
||||||
|
fixed: "left",
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("organization"),
|
...this.getColumnSearchProps("organization"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -119,7 +120,6 @@ class MessageListPage extends BaseListPage {
|
|||||||
dataIndex: "chat",
|
dataIndex: "chat",
|
||||||
key: "chat",
|
key: "chat",
|
||||||
width: "120px",
|
width: "120px",
|
||||||
fixed: "left",
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("chat"),
|
...this.getColumnSearchProps("chat"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -135,7 +135,6 @@ class MessageListPage extends BaseListPage {
|
|||||||
dataIndex: "author",
|
dataIndex: "author",
|
||||||
key: "author",
|
key: "author",
|
||||||
width: "120px",
|
width: "120px",
|
||||||
fixed: "left",
|
|
||||||
sorter: true,
|
sorter: true,
|
||||||
...this.getColumnSearchProps("author"),
|
...this.getColumnSearchProps("author"),
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
@@ -211,9 +210,11 @@ class MessageListPage extends BaseListPage {
|
|||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
MessageBackend.getMessages("admin", Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
MessageBackend.getMessages("admin", Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -225,9 +226,10 @@ class MessageListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -36,7 +36,6 @@ class ModelEditPage extends React.Component {
|
|||||||
model: null,
|
model: null,
|
||||||
organizations: [],
|
organizations: [],
|
||||||
users: [],
|
users: [],
|
||||||
models: [],
|
|
||||||
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
mode: props.location.mode !== undefined ? props.location.mode : "edit",
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -49,11 +48,14 @@ class ModelEditPage extends React.Component {
|
|||||||
getModel() {
|
getModel() {
|
||||||
ModelBackend.getModel(this.state.organizationName, this.state.modelName)
|
ModelBackend.getModel(this.state.organizationName, this.state.modelName)
|
||||||
.then((model) => {
|
.then((model) => {
|
||||||
|
if (model === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
model: model,
|
model: model,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.getModels(model.organization);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -66,15 +68,6 @@ class ModelEditPage extends React.Component {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
getModels(organizationName) {
|
|
||||||
ModelBackend.getModels(organizationName)
|
|
||||||
.then((res) => {
|
|
||||||
this.setState({
|
|
||||||
models: res,
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
parseModelField(key, value) {
|
parseModelField(key, value) {
|
||||||
if ([""].includes(key)) {
|
if ([""].includes(key)) {
|
||||||
value = Setting.myParseInt(value);
|
value = Setting.myParseInt(value);
|
||||||
@@ -134,6 +127,16 @@ class ModelEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}} >
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Description"), i18next.t("general:Description - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Input value={this.state.model.description} onChange={e => {
|
||||||
|
this.updateModelField("description", e.target.value);
|
||||||
|
}} />
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("model:Model text"), i18next.t("model:Model text - Tooltip"))} :
|
{Setting.getLabel(i18next.t("model:Model text"), i18next.t("model:Model text - Tooltip"))} :
|
||||||
|
@@ -204,9 +204,11 @@ class ModelListPage extends BaseListPage {
|
|||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
ModelBackend.getModels(Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
ModelBackend.getModels(Setting.isAdminUser(this.props.account) ? "" : this.props.account.owner, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -218,9 +220,10 @@ class ModelListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@@ -49,10 +49,20 @@ class OrganizationEditPage extends React.Component {
|
|||||||
|
|
||||||
getOrganization() {
|
getOrganization() {
|
||||||
OrganizationBackend.getOrganization("admin", this.state.organizationName)
|
OrganizationBackend.getOrganization("admin", this.state.organizationName)
|
||||||
.then((organization) => {
|
.then((res) => {
|
||||||
this.setState({
|
if (res.status === "ok") {
|
||||||
organization: organization,
|
const organization = res.data;
|
||||||
});
|
if (organization === null) {
|
||||||
|
this.props.history.push("/404");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
organization: organization,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -183,6 +193,29 @@ class OrganizationEditPage extends React.Component {
|
|||||||
}} />
|
}} />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
<Row style={{marginTop: "20px"}}>
|
||||||
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
|
{Setting.getLabel(i18next.t("general:Password complexity options"), i18next.t("general:Password complexity options - Tooltip"))} :
|
||||||
|
</Col>
|
||||||
|
<Col span={22} >
|
||||||
|
<Select
|
||||||
|
virtual={false}
|
||||||
|
style={{width: "100%"}}
|
||||||
|
mode="multiple"
|
||||||
|
value={this.state.organization.passwordOptions}
|
||||||
|
onChange={(value => {
|
||||||
|
this.updateOrganizationField("passwordOptions", value);
|
||||||
|
})}
|
||||||
|
options={[
|
||||||
|
{value: "AtLeast6", name: i18next.t("user:The password must have at least 6 characters")},
|
||||||
|
{value: "AtLeast8", name: i18next.t("user:The password must have at least 8 characters")},
|
||||||
|
{value: "Aa123", name: i18next.t("user:The password must contain at least one uppercase letter, one lowercase letter and one digit")},
|
||||||
|
{value: "SpecialChar", name: i18next.t("user:The password must contain at least one special character")},
|
||||||
|
{value: "NoRepeat", name: i18next.t("user:The password must not contain any repeated characters")},
|
||||||
|
].map((item) => Setting.getOption(item.name, item.value))}
|
||||||
|
/>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
<Row style={{marginTop: "20px"}} >
|
<Row style={{marginTop: "20px"}} >
|
||||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
|
||||||
{Setting.getLabel(i18next.t("general:Supported country codes"), i18next.t("general:Supported country codes - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Supported country codes"), i18next.t("general:Supported country codes - Tooltip"))} :
|
||||||
@@ -205,7 +238,7 @@ class OrganizationEditPage extends React.Component {
|
|||||||
{Setting.getLabel(i18next.t("general:Languages"), i18next.t("general:Languages - Tooltip"))} :
|
{Setting.getLabel(i18next.t("general:Languages"), i18next.t("general:Languages - Tooltip"))} :
|
||||||
</Col>
|
</Col>
|
||||||
<Col span={22} >
|
<Col span={22} >
|
||||||
<Select virtual={false} mode="tags" style={{width: "100%"}}
|
<Select virtual={false} mode="multiple" style={{width: "100%"}}
|
||||||
options={Setting.Countries.map((item) => {
|
options={Setting.Countries.map((item) => {
|
||||||
return Setting.getOption(item.label, item.key);
|
return Setting.getOption(item.label, item.key);
|
||||||
})}
|
})}
|
||||||
|
@@ -34,6 +34,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
favicon: `${Setting.StaticBaseUrl}/img/favicon.png`,
|
favicon: `${Setting.StaticBaseUrl}/img/favicon.png`,
|
||||||
passwordType: "plain",
|
passwordType: "plain",
|
||||||
PasswordSalt: "",
|
PasswordSalt: "",
|
||||||
|
passwordOptions: [],
|
||||||
countryCodes: ["CN"],
|
countryCodes: ["CN"],
|
||||||
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
|
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
|
||||||
defaultApplication: "",
|
defaultApplication: "",
|
||||||
@@ -60,8 +61,10 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
|
{name: "Bio", visible: true, viewRule: "Public", modifyRule: "Self"},
|
||||||
{name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
{name: "Tag", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||||
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
{name: "Signup application", visible: true, viewRule: "Public", modifyRule: "Admin"},
|
||||||
|
{name: "API key", label: i18next.t("general:API key")},
|
||||||
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
{name: "Roles", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
{name: "Permissions", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
|
{name: "Groups", visible: true, viewRule: "Public", modifyRule: "Immutable"},
|
||||||
{name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"},
|
{name: "3rd-party logins", visible: true, viewRule: "Self", modifyRule: "Self"},
|
||||||
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
{Name: "Multi-factor authentication", Visible: true, ViewRule: "Self", ModifyRule: "Self"},
|
||||||
{name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"},
|
{name: "Properties", visible: false, viewRule: "Admin", modifyRule: "Admin"},
|
||||||
@@ -221,11 +224,12 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
title: i18next.t("general:Action"),
|
title: i18next.t("general:Action"),
|
||||||
dataIndex: "",
|
dataIndex: "",
|
||||||
key: "op",
|
key: "op",
|
||||||
width: "240px",
|
width: "320px",
|
||||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||||
render: (text, record, index) => {
|
render: (text, record, index) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/trees/${record.name}`)}>{i18next.t("general:Groups")}</Button>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}/users`)}>{i18next.t("general:Users")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} type="primary" onClick={() => this.props.history.push(`/organizations/${record.name}/users`)}>{i18next.t("general:Users")}</Button>
|
||||||
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/organizations/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
<Button style={{marginTop: "10px", marginBottom: "10px", marginRight: "10px"}} onClick={() => this.props.history.push(`/organizations/${record.name}`)}>{i18next.t("general:Edit")}</Button>
|
||||||
<PopconfirmModal
|
<PopconfirmModal
|
||||||
@@ -253,7 +257,7 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
title={() => (
|
title={() => (
|
||||||
<div>
|
<div>
|
||||||
{i18next.t("general:Organizations")}
|
{i18next.t("general:Organizations")}
|
||||||
<Button type="primary" size="small" onClick={this.addOrganization.bind(this)}>{i18next.t("general:Add")}</Button>
|
<Button type="primary" size="small" disabled={!Setting.isAdminUser(this.props.account)} onClick={this.addOrganization.bind(this)}>{i18next.t("general:Add")}</Button>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
loading={this.state.loading}
|
loading={this.state.loading}
|
||||||
@@ -273,9 +277,11 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
this.setState({loading: true});
|
this.setState({loading: true});
|
||||||
OrganizationBackend.getOrganizations("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
OrganizationBackend.getOrganizations("admin", params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder)
|
||||||
.then((res) => {
|
.then((res) => {
|
||||||
|
this.setState({
|
||||||
|
loading: false,
|
||||||
|
});
|
||||||
if (res.status === "ok") {
|
if (res.status === "ok") {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
data: res.data,
|
data: res.data,
|
||||||
pagination: {
|
pagination: {
|
||||||
...params.pagination,
|
...params.pagination,
|
||||||
@@ -287,9 +293,10 @@ class OrganizationListPage extends BaseListPage {
|
|||||||
} else {
|
} else {
|
||||||
if (Setting.isResponseDenied(res)) {
|
if (Setting.isResponseDenied(res)) {
|
||||||
this.setState({
|
this.setState({
|
||||||
loading: false,
|
|
||||||
isAuthorized: false,
|
isAuthorized: false,
|
||||||
});
|
});
|
||||||
|
} else {
|
||||||
|
Setting.showMessage("error", res.msg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user