mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-13 01:17:45 +08:00
Compare commits
37 Commits
Author | SHA1 | Date | |
---|---|---|---|
![]() |
8080b10b3b | ||
![]() |
cd7589775c | ||
![]() |
0a8c2a35fe | ||
![]() |
d1e734e4ce | ||
![]() |
68f032b54d | ||
![]() |
1780620ef4 | ||
![]() |
5c968ed1ce | ||
![]() |
4016fc0f65 | ||
![]() |
463b3ad976 | ||
![]() |
b817a55f9f | ||
![]() |
2c2ddfbb92 | ||
![]() |
cadb533595 | ||
![]() |
a3b0f1fc74 | ||
![]() |
c391af4552 | ||
![]() |
6ebca6dbe7 | ||
![]() |
d505a4bf2d | ||
![]() |
812bc5f6b2 | ||
![]() |
f6f4d44444 | ||
![]() |
926e73ed1b | ||
![]() |
65716af89e | ||
![]() |
d9c4f401e3 | ||
![]() |
58aa7dba6a | ||
![]() |
29fc820578 | ||
![]() |
d0ac265c91 | ||
![]() |
3562c36817 | ||
![]() |
7884e10ca3 | ||
![]() |
12dee8afd3 | ||
![]() |
ac4b870309 | ||
![]() |
b9140e2d5a | ||
![]() |
501f0dc74f | ||
![]() |
a932b76fba | ||
![]() |
0f57ac297b | ||
![]() |
edc6aa0d50 | ||
![]() |
ebc0e0f2c9 | ||
![]() |
63dd2e781e | ||
![]() |
b01ba792bb | ||
![]() |
98fb9f25b0 |
@@ -110,7 +110,7 @@ func GetLanguage(language string) string {
|
||||
return "en"
|
||||
}
|
||||
|
||||
if len(language) != 2 {
|
||||
if len(language) != 2 || language == "nu" {
|
||||
return "en"
|
||||
} else {
|
||||
return language
|
||||
|
@@ -140,6 +140,13 @@ func (c *ApiController) Signup() {
|
||||
username = id
|
||||
}
|
||||
|
||||
password := authForm.Password
|
||||
msg = object.CheckPasswordComplexityByOrg(organization, password)
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
initScore, err := organization.GetInitScore()
|
||||
if err != nil {
|
||||
c.ResponseError(fmt.Errorf(c.T("account:Get init score failed, error: %w"), err).Error())
|
||||
@@ -363,6 +370,7 @@ func (c *ApiController) GetAccount() {
|
||||
|
||||
user.Permissions = object.GetMaskedPermissions(user.Permissions)
|
||||
user.Roles = object.GetMaskedRoles(user.Roles)
|
||||
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
|
||||
|
||||
organization, err := object.GetMaskedOrganization(object.GetOrganizationByUser(user))
|
||||
if err != nil {
|
||||
|
@@ -50,7 +50,8 @@ func (c *ApiController) GetApplications() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
||||
@@ -59,13 +60,15 @@ func (c *ApiController) GetApplications() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetApplicationCount(owner, field, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
app, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
applications := object.GetMaskedApplications(app, userId)
|
||||
@@ -85,7 +88,8 @@ func (c *ApiController) GetApplication() {
|
||||
id := c.Input().Get("id")
|
||||
app, err := object.GetApplication(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplication(app, userId)
|
||||
@@ -104,7 +108,8 @@ func (c *ApiController) GetUserApplication() {
|
||||
id := c.Input().Get("id")
|
||||
user, err := object.GetUser(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
@@ -114,7 +119,8 @@ func (c *ApiController) GetUserApplication() {
|
||||
|
||||
app, err := object.GetApplicationByUser(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplication(app, userId)
|
||||
@@ -147,7 +153,8 @@ func (c *ApiController) GetOrganizationApplications() {
|
||||
if limit == "" || page == "" {
|
||||
applications, err := object.GetOrganizationApplications(owner, organization)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
||||
|
@@ -71,7 +71,7 @@ func (c *ApiController) HandleLoggedIn(application *object.Application, user *ob
|
||||
|
||||
if form.Password != "" && user.IsMfaEnabled() {
|
||||
c.setMfaSessionData(&object.MfaSessionData{UserId: userId})
|
||||
resp = &Response{Status: object.NextMfa, Data: user.GetPreferMfa(true)}
|
||||
resp = &Response{Status: object.NextMfa, Data: user.GetPreferredMfaProps(true)}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -656,15 +656,20 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
if authForm.Passcode != "" {
|
||||
MfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferMfa(false))
|
||||
err = MfaUtil.Verify(authForm.Passcode)
|
||||
mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false))
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
|
||||
err = mfaUtil.Verify(authForm.Passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if authForm.RecoveryCode != "" {
|
||||
err = object.RecoverTfs(user, authForm.RecoveryCode)
|
||||
err = object.MfaRecover(user, authForm.RecoveryCode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -751,7 +756,8 @@ func (c *ApiController) HandleSamlLogin() {
|
||||
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
var data struct {
|
||||
@@ -761,7 +767,8 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
}
|
||||
err = xml.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
|
@@ -79,7 +79,8 @@ func (c *ApiController) getCurrentUser() *object.User {
|
||||
} else {
|
||||
user, err = object.GetUser(userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return user
|
||||
@@ -112,7 +113,8 @@ func (c *ApiController) GetSessionApplication() *object.Application {
|
||||
}
|
||||
application, err := object.GetApplicationByClientId(clientId.(string))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return nil
|
||||
}
|
||||
|
||||
return application
|
||||
@@ -177,6 +179,10 @@ func (c *ApiController) SetSessionData(s *SessionData) {
|
||||
}
|
||||
|
||||
func (c *ApiController) setMfaSessionData(data *object.MfaSessionData) {
|
||||
if data == nil {
|
||||
c.SetSession(object.MfaSessionUserId, nil)
|
||||
return
|
||||
}
|
||||
c.SetSession(object.MfaSessionUserId, data.UserId)
|
||||
}
|
||||
|
||||
|
@@ -23,6 +23,13 @@ import (
|
||||
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() {
|
||||
owner := c.Input().Get("owner")
|
||||
limit := c.Input().Get("pageSize")
|
||||
@@ -31,9 +38,9 @@ func (c *ApiController) GetCasbinAdapters() {
|
||||
value := c.Input().Get("value")
|
||||
sortField := c.Input().Get("sortField")
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
organization := c.Input().Get("organization")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
adapters, err := object.GetCasbinAdapters(owner, organization)
|
||||
adapters, err := object.GetCasbinAdapters(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -42,14 +49,14 @@ func (c *ApiController) GetCasbinAdapters() {
|
||||
c.ResponseOk(adapters)
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetCasbinAdapterCount(owner, organization, field, value)
|
||||
count, err := object.GetCasbinAdapterCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
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 {
|
||||
c.ResponseError(err.Error())
|
||||
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() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
adapter, err := object.GetCasbinAdapter(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -70,6 +85,14 @@ func (c *ApiController) GetCasbinAdapter() {
|
||||
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() {
|
||||
id := c.Input().Get("id")
|
||||
|
||||
@@ -84,6 +107,13 @@ func (c *ApiController) UpdateCasbinAdapter() {
|
||||
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() {
|
||||
var casbinAdapter object.CasbinAdapter
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
|
||||
@@ -96,6 +126,13 @@ func (c *ApiController) AddCasbinAdapter() {
|
||||
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() {
|
||||
var casbinAdapter object.CasbinAdapter
|
||||
err := json.Unmarshal(c.Ctx.Input.RequestBody, &casbinAdapter)
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetCerts() {
|
||||
if limit == "" || page == "" {
|
||||
maskedCerts, err := object.GetMaskedCerts(object.GetCerts(owner))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedCerts
|
||||
@@ -50,13 +51,15 @@ func (c *ApiController) GetCerts() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetCertCount(owner, field, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
certs, err := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(certs, paginator.Nums())
|
||||
@@ -80,7 +83,8 @@ func (c *ApiController) GetGlobleCerts() {
|
||||
if limit == "" || page == "" {
|
||||
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedCerts
|
||||
@@ -89,13 +93,15 @@ func (c *ApiController) GetGlobleCerts() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetGlobalCertsCount(field, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
certs, err := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(certs, paginator.Nums())
|
||||
@@ -113,7 +119,8 @@ func (c *ApiController) GetCert() {
|
||||
id := c.Input().Get("id")
|
||||
cert, err := object.GetCert(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedCert(cert)
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetChats() {
|
||||
if limit == "" || page == "" {
|
||||
maskedChats, err := object.GetMaskedChats(object.GetChats(owner))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedChats
|
||||
@@ -77,7 +78,8 @@ func (c *ApiController) GetChat() {
|
||||
|
||||
maskedChat, err := object.GetMaskedChat(object.GetChat(id))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedChat
|
||||
|
@@ -44,14 +44,26 @@ func (c *ApiController) Enforce() {
|
||||
}
|
||||
|
||||
if permissionId != "" {
|
||||
enforceResult, err := object.Enforce(permissionId, &request)
|
||||
permission, err := object.GetPermission(permissionId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res := []bool{}
|
||||
res = append(res, enforceResult)
|
||||
|
||||
if permission == nil {
|
||||
res = append(res, false)
|
||||
} else {
|
||||
enforceResult, err := object.Enforce(permission, &request)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
return
|
||||
}
|
||||
@@ -76,8 +88,16 @@ func (c *ApiController) Enforce() {
|
||||
}
|
||||
|
||||
res := []bool{}
|
||||
for _, permission := range permissions {
|
||||
enforceResult, err := object.Enforce(permission.GetId(), &request)
|
||||
|
||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||
for _, permissionIds := range listPermissionIdMap {
|
||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
enforceResult, err := object.Enforce(firstPermission, &request, permissionIds...)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -85,6 +105,7 @@ func (c *ApiController) Enforce() {
|
||||
|
||||
res = append(res, enforceResult)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
}
|
||||
|
||||
@@ -109,14 +130,32 @@ func (c *ApiController) BatchEnforce() {
|
||||
}
|
||||
|
||||
if permissionId != "" {
|
||||
enforceResult, err := object.BatchEnforce(permissionId, &requests)
|
||||
permission, err := object.GetPermission(permissionId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res := [][]bool{}
|
||||
res = append(res, enforceResult)
|
||||
|
||||
if permission == nil {
|
||||
l := len(requests)
|
||||
resRequest := make([]bool, l)
|
||||
for i := 0; i < l; i++ {
|
||||
resRequest[i] = false
|
||||
}
|
||||
|
||||
res = append(res, resRequest)
|
||||
} else {
|
||||
enforceResult, err := object.BatchEnforce(permission, &requests)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
return
|
||||
}
|
||||
@@ -135,8 +174,16 @@ func (c *ApiController) BatchEnforce() {
|
||||
}
|
||||
|
||||
res := [][]bool{}
|
||||
for _, permission := range permissions {
|
||||
enforceResult, err := object.BatchEnforce(permission.GetId(), &requests)
|
||||
|
||||
listPermissionIdMap := object.GroupPermissionsByModelAdapter(permissions)
|
||||
for _, permissionIds := range listPermissionIdMap {
|
||||
firstPermission, err := object.GetPermission(permissionIds[0])
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
enforceResult, err := object.BatchEnforce(firstPermission, &requests, permissionIds...)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -144,6 +191,7 @@ func (c *ApiController) BatchEnforce() {
|
||||
|
||||
res = append(res, enforceResult)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
}
|
||||
|
||||
|
@@ -53,7 +53,8 @@ func (c *ApiController) GetMessages() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedMessages(messages)
|
||||
@@ -89,7 +90,8 @@ func (c *ApiController) GetMessage() {
|
||||
id := c.Input().Get("id")
|
||||
message, err := object.GetMessage(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedMessage(message)
|
||||
@@ -100,7 +102,8 @@ func (c *ApiController) ResponseErrorStream(errorText string) {
|
||||
event := fmt.Sprintf("event: myerror\ndata: %s\n\n", errorText)
|
||||
_, err := c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
@@ -196,7 +199,8 @@ func (c *ApiController) GetMessageAnswer() {
|
||||
event := fmt.Sprintf("event: end\ndata: %s\n\n", "end")
|
||||
_, err = c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
answer := stringBuilder.String()
|
||||
@@ -204,7 +208,8 @@ func (c *ApiController) GetMessageAnswer() {
|
||||
message.Text = answer
|
||||
_, err = object.UpdateMessage(message.GetId(), message)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,7 +17,6 @@ package controllers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/beego/beego"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@@ -34,7 +33,7 @@ import (
|
||||
func (c *ApiController) MfaSetupInitiate() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
userId := util.GetId(owner, name)
|
||||
|
||||
if len(userId) == 0 {
|
||||
@@ -42,10 +41,11 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
return
|
||||
}
|
||||
|
||||
MfaUtil := object.GetMfaUtil(authType, nil)
|
||||
MfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
if MfaUtil == nil {
|
||||
c.ResponseError("Invalid auth type")
|
||||
}
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -57,10 +57,7 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
return
|
||||
}
|
||||
|
||||
issuer := beego.AppConfig.String("appname")
|
||||
accountName := user.GetId()
|
||||
|
||||
mfaProps, err := MfaUtil.Initiate(c.Ctx, issuer, accountName)
|
||||
mfaProps, err := MfaUtil.Initiate(c.Ctx, user.GetId())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -79,16 +76,20 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
// @Success 200 {object} Response object
|
||||
// @router /mfa/setup/verify [post]
|
||||
func (c *ApiController) MfaSetupVerify() {
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
passcode := c.Ctx.Request.Form.Get("passcode")
|
||||
|
||||
if authType == "" || passcode == "" {
|
||||
if mfaType == "" || passcode == "" {
|
||||
c.ResponseError("missing auth type or passcode")
|
||||
return
|
||||
}
|
||||
MfaUtil := object.GetMfaUtil(authType, nil)
|
||||
mfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
|
||||
err := MfaUtil.SetupVerify(c.Ctx, passcode)
|
||||
err := mfaUtil.SetupVerify(c.Ctx, passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
} else {
|
||||
@@ -108,7 +109,7 @@ func (c *ApiController) MfaSetupVerify() {
|
||||
func (c *ApiController) MfaSetupEnable() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
|
||||
user, err := object.GetUser(util.GetId(owner, name))
|
||||
if err != nil {
|
||||
@@ -121,8 +122,13 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
return
|
||||
}
|
||||
|
||||
twoFactor := object.GetMfaUtil(authType, nil)
|
||||
err = twoFactor.Enable(c.Ctx, user)
|
||||
mfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
|
||||
err = mfaUtil.Enable(c.Ctx, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -137,11 +143,9 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
// @Description: Delete MFA
|
||||
// @param owner form string true "owner of user"
|
||||
// @param name form string true "name of user"
|
||||
// @param id form string true "id of user's MFA props"
|
||||
// @Success 200 {object} Response object
|
||||
// @router /delete-mfa/ [post]
|
||||
func (c *ApiController) DeleteMfa() {
|
||||
id := c.Ctx.Request.Form.Get("id")
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
userId := util.GetId(owner, name)
|
||||
@@ -151,28 +155,18 @@ func (c *ApiController) DeleteMfa() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
mfaProps := user.MultiFactorAuths[:0]
|
||||
i := 0
|
||||
for _, mfaProp := range mfaProps {
|
||||
if mfaProp.Id != id {
|
||||
mfaProps[i] = mfaProp
|
||||
i++
|
||||
}
|
||||
}
|
||||
user.MultiFactorAuths = mfaProps
|
||||
_, err = object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
|
||||
err = object.DisabledMultiFactorAuth(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(user.MultiFactorAuths)
|
||||
c.ResponseOk(object.GetAllMfaProps(user, true))
|
||||
}
|
||||
|
||||
// SetPreferredMfa
|
||||
@@ -185,7 +179,7 @@ func (c *ApiController) DeleteMfa() {
|
||||
// @Success 200 {object} Response object
|
||||
// @router /set-preferred-mfa [post]
|
||||
func (c *ApiController) SetPreferredMfa() {
|
||||
id := c.Ctx.Request.Form.Get("id")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
userId := util.GetId(owner, name)
|
||||
@@ -195,29 +189,15 @@ func (c *ApiController) SetPreferredMfa() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
mfaProps := user.MultiFactorAuths
|
||||
for i, mfaProp := range user.MultiFactorAuths {
|
||||
if mfaProp.Id == id {
|
||||
mfaProps[i].IsPreferred = true
|
||||
} else {
|
||||
mfaProps[i].IsPreferred = false
|
||||
}
|
||||
}
|
||||
|
||||
_, err = object.UpdateUser(userId, user, []string{"multi_factor_auths"}, user.IsAdminUser())
|
||||
err = object.SetPreferredMultiFactorAuth(user, mfaType)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for i, mfaProp := range mfaProps {
|
||||
mfaProps[i] = object.GetMaskedProps(mfaProp)
|
||||
}
|
||||
c.ResponseOk(mfaProps)
|
||||
c.ResponseOk(object.GetAllMfaProps(user, true))
|
||||
}
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetModels() {
|
||||
if limit == "" || page == "" {
|
||||
models, err := object.GetModels(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = models
|
||||
@@ -77,7 +78,8 @@ func (c *ApiController) GetModel() {
|
||||
|
||||
model, err := object.GetModel(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = model
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetOrganizations() {
|
||||
if limit == "" || page == "" {
|
||||
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedOrganizations
|
||||
|
@@ -42,7 +42,8 @@ func (c *ApiController) GetPayments() {
|
||||
if limit == "" || page == "" {
|
||||
payments, err := object.GetPayments(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = payments
|
||||
@@ -51,13 +52,15 @@ func (c *ApiController) GetPayments() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetPaymentCount(owner, organization, field, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
payments, err := object.GetPaginationPayments(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(payments, paginator.Nums())
|
||||
@@ -99,7 +102,8 @@ func (c *ApiController) GetPayment() {
|
||||
|
||||
payment, err := object.GetPayment(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = payment
|
||||
@@ -190,7 +194,8 @@ func (c *ApiController) NotifyPayment() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetPermissions() {
|
||||
if limit == "" || page == "" {
|
||||
permissions, err := object.GetPermissions(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = permissions
|
||||
@@ -50,13 +51,15 @@ func (c *ApiController) GetPermissions() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetPermissionCount(owner, field, value)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
permissions, err := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(permissions, paginator.Nums())
|
||||
@@ -116,7 +119,8 @@ func (c *ApiController) GetPermission() {
|
||||
|
||||
permission, err := object.GetPermission(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = permission
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetPlans() {
|
||||
if limit == "" || page == "" {
|
||||
plans, err := object.GetPlans(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = plans
|
||||
@@ -79,13 +80,15 @@ func (c *ApiController) GetPlan() {
|
||||
|
||||
plan, err := object.GetPlan(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if includeOption {
|
||||
options, err := object.GetPermissionsByRole(plan.Role)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetPricings() {
|
||||
if limit == "" || page == "" {
|
||||
pricings, err := object.GetPricings(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = pricings
|
||||
@@ -77,7 +78,8 @@ func (c *ApiController) GetPricing() {
|
||||
|
||||
pricing, err := object.GetPricing(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = pricing
|
||||
|
@@ -42,7 +42,8 @@ func (c *ApiController) GetProducts() {
|
||||
if limit == "" || page == "" {
|
||||
products, err := object.GetProducts(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = products
|
||||
@@ -78,12 +79,14 @@ func (c *ApiController) GetProduct() {
|
||||
|
||||
product, err := object.GetProduct(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
err = object.ExtendProductWithProviders(product)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = product
|
||||
|
@@ -46,7 +46,8 @@ func (c *ApiController) GetProviders() {
|
||||
if limit == "" || page == "" {
|
||||
providers, err := object.GetProviders(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedProviders(providers, isMaskEnabled))
|
||||
@@ -92,7 +93,8 @@ func (c *ApiController) GetGlobalProviders() {
|
||||
if limit == "" || page == "" {
|
||||
globalProviders, err := object.GetGlobalProviders()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedProviders(globalProviders, isMaskEnabled))
|
||||
|
@@ -46,7 +46,8 @@ func (c *ApiController) GetRecords() {
|
||||
if limit == "" || page == "" {
|
||||
records, err := object.GetRecords()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = records
|
||||
@@ -84,12 +85,14 @@ func (c *ApiController) GetRecordsByFilter() {
|
||||
record := &object.Record{}
|
||||
err := util.JsonToStruct(body, record)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
records, err := object.GetRecordsByField(record)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = records
|
||||
|
@@ -53,7 +53,8 @@ func (c *ApiController) GetResources() {
|
||||
if limit == "" || page == "" {
|
||||
resources, err := object.GetResources(owner, user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = resources
|
||||
@@ -86,7 +87,8 @@ func (c *ApiController) GetResource() {
|
||||
|
||||
resource, err := object.GetResource(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = resource
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetRoles() {
|
||||
if limit == "" || page == "" {
|
||||
roles, err := object.GetRoles(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = roles
|
||||
@@ -77,7 +78,8 @@ func (c *ApiController) GetRole() {
|
||||
|
||||
role, err := object.GetRole(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = role
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetSessions() {
|
||||
if limit == "" || page == "" {
|
||||
sessions, err := object.GetSessions(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = sessions
|
||||
@@ -76,7 +77,8 @@ func (c *ApiController) GetSingleSession() {
|
||||
|
||||
session, err := object.GetSingleSession(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = session
|
||||
@@ -155,7 +157,8 @@ func (c *ApiController) IsSessionDuplicated() {
|
||||
|
||||
isUserSessionDuplicated, err := object.IsSessionDuplicated(id, sessionId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = &Response{Status: "ok", Msg: "", Data: isUserSessionDuplicated}
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetSubscriptions() {
|
||||
if limit == "" || page == "" {
|
||||
subscriptions, err := object.GetSubscriptions(owner)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = subscriptions
|
||||
@@ -77,7 +78,8 @@ func (c *ApiController) GetSubscription() {
|
||||
|
||||
subscription, err := object.GetSubscription(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = subscription
|
||||
|
@@ -42,7 +42,8 @@ func (c *ApiController) GetSyncers() {
|
||||
if limit == "" || page == "" {
|
||||
organizationSyncers, err := object.GetOrganizationSyncers(owner, organization)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = organizationSyncers
|
||||
@@ -78,7 +79,8 @@ func (c *ApiController) GetSyncer() {
|
||||
|
||||
syncer, err := object.GetSyncer(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = syncer
|
||||
|
@@ -43,7 +43,8 @@ func (c *ApiController) GetTokens() {
|
||||
if limit == "" || page == "" {
|
||||
token, err := object.GetTokens(owner, organization)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = token
|
||||
@@ -78,7 +79,8 @@ func (c *ApiController) GetToken() {
|
||||
id := c.Input().Get("id")
|
||||
token, err := object.GetToken(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = token
|
||||
@@ -193,7 +195,8 @@ func (c *ApiController) GetOAuthToken() {
|
||||
host := c.Ctx.Request.Host
|
||||
oAuthtoken, err := object.GetOAuthToken(grantType, clientId, clientSecret, code, verifier, scope, username, password, host, refreshToken, tag, avatar, c.GetAcceptLanguage())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = oAuthtoken
|
||||
@@ -236,7 +239,8 @@ func (c *ApiController) RefreshToken() {
|
||||
|
||||
refreshToken2, err := object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = refreshToken2
|
||||
@@ -276,7 +280,8 @@ func (c *ApiController) IntrospectToken() {
|
||||
}
|
||||
application, err := object.GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
@@ -289,7 +294,8 @@ func (c *ApiController) IntrospectToken() {
|
||||
}
|
||||
token, err := object.GetTokenByTokenAndApplication(tokenValue, application.Name)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
|
@@ -41,7 +41,8 @@ func (c *ApiController) GetGlobalUsers() {
|
||||
if limit == "" || page == "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGlobalUsers())
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUsers
|
||||
@@ -80,7 +81,7 @@ func (c *ApiController) GetGlobalUsers() {
|
||||
// @router /get-users [get]
|
||||
func (c *ApiController) GetUsers() {
|
||||
owner := c.Input().Get("owner")
|
||||
groupId := c.Input().Get("groupId")
|
||||
groupName := c.Input().Get("groupName")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
@@ -89,8 +90,8 @@ func (c *ApiController) GetUsers() {
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
if groupId != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupId))
|
||||
if groupName != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -101,21 +102,22 @@ func (c *ApiController) GetUsers() {
|
||||
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUsers
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetUserCount(owner, field, value, groupId)
|
||||
count, err := object.GetUserCount(owner, field, value, groupName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupId)
|
||||
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -153,7 +155,8 @@ func (c *ApiController) GetUser() {
|
||||
if userId != "" && owner != "" {
|
||||
userFromUserId, err = object.GetUserByUserId(owner, userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
|
||||
@@ -165,7 +168,8 @@ func (c *ApiController) GetUser() {
|
||||
|
||||
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if !organization.IsProfilePublic {
|
||||
@@ -190,17 +194,21 @@ func (c *ApiController) GetUser() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
|
||||
err = object.ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
maskedUser, err := object.GetMaskedUser(user)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUser
|
||||
@@ -410,15 +418,12 @@ func (c *ApiController) SetPassword() {
|
||||
c.ResponseError(c.T("user:New password cannot contain blank space."))
|
||||
return
|
||||
}
|
||||
if len(newPassword) <= 5 {
|
||||
c.ResponseError(c.T("user:New password must have at least 6 characters"))
|
||||
return
|
||||
}
|
||||
|
||||
userId := util.GetId(userOwner, userName)
|
||||
|
||||
requestUserId := c.GetSessionUsername()
|
||||
if requestUserId == "" && code == "" {
|
||||
c.ResponseError(c.T("general:Please login first"), "Please login first")
|
||||
return
|
||||
} else if code == "" {
|
||||
hasPermission, err := object.CheckUserPermission(requestUserId, userId, true, c.GetAcceptLanguage())
|
||||
@@ -428,7 +433,7 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
} else {
|
||||
if code != c.GetSession("verifiedCode") {
|
||||
c.ResponseError("")
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
return
|
||||
}
|
||||
c.SetSession("verifiedCode", "")
|
||||
@@ -448,6 +453,12 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
}
|
||||
|
||||
msg := object.CheckPasswordComplexity(targetUser, newPassword)
|
||||
if msg != "" {
|
||||
c.ResponseError(msg)
|
||||
return
|
||||
}
|
||||
|
||||
targetUser.Password = newPassword
|
||||
_, err = object.SetUserField(targetUser, "password", targetUser.Password)
|
||||
if err != nil {
|
||||
@@ -494,7 +505,8 @@ func (c *ApiController) GetSortedUsers() {
|
||||
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUsers
|
||||
@@ -554,8 +566,8 @@ func (c *ApiController) AddUserkeys() {
|
||||
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")
|
||||
groupName := c.Ctx.Request.Form.Get("groupName")
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupId))
|
||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupName))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
@@ -19,13 +19,14 @@ import (
|
||||
"io"
|
||||
"mime/multipart"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
|
||||
func saveFile(path string, file *multipart.File) (err error) {
|
||||
f, err := os.Create(path)
|
||||
f, err := os.Create(filepath.Clean(path))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -55,6 +55,9 @@ func (c *ApiController) T(error string) string {
|
||||
// GetAcceptLanguage ...
|
||||
func (c *ApiController) GetAcceptLanguage() string {
|
||||
language := c.Ctx.Request.Header.Get("Accept-Language")
|
||||
if len(language) > 2 {
|
||||
language = language[0:2]
|
||||
}
|
||||
return conf.GetLanguage(language)
|
||||
}
|
||||
|
||||
@@ -94,7 +97,8 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return nil, false
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
|
@@ -93,9 +93,10 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
}
|
||||
|
||||
// mfaSessionData != nil, means method is MfaSetupVerification
|
||||
// mfaSessionData != nil, means method is MfaAuthVerification
|
||||
if mfaSessionData := c.getMfaSessionData(); mfaSessionData != nil {
|
||||
user, err = object.GetUser(mfaSessionData.UserId)
|
||||
c.setMfaSessionData(nil)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -129,7 +130,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
} else if vform.Method == ResetVerification {
|
||||
user = c.getCurrentUser()
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferMfa(false)
|
||||
mfaProps := user.GetPreferredMfaProps(false)
|
||||
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
@@ -157,12 +158,14 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
} else if vform.Method == ResetVerification {
|
||||
if user = c.getCurrentUser(); user != nil {
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
} else if vform.Method == ResetVerification || vform.Method == MfaSetupVerification {
|
||||
if vform.CountryCode == "" {
|
||||
if user = c.getCurrentUser(); user != nil {
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
}
|
||||
}
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferMfa(false)
|
||||
mfaProps := user.GetPreferredMfaProps(false)
|
||||
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
|
@@ -42,7 +42,8 @@ func (c *ApiController) GetWebhooks() {
|
||||
if limit == "" || page == "" {
|
||||
webhooks, err := object.GetWebhooks(owner, organization)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = webhooks
|
||||
@@ -79,7 +80,8 @@ func (c *ApiController) GetWebhook() {
|
||||
|
||||
webhook, err := object.GetWebhook(id)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.Data["json"] = webhook
|
||||
|
@@ -17,6 +17,7 @@ package deployment
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -45,7 +46,7 @@ func uploadFolder(storageProvider oss.StorageInterface, folder string) {
|
||||
continue
|
||||
}
|
||||
|
||||
file, err := os.Open(path + filename)
|
||||
file, err := os.Open(filepath.Clean(path + filename))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
4
go.mod
4
go.mod
@@ -42,7 +42,7 @@ require (
|
||||
github.com/nu7hatch/gouuid v0.0.0-20131221200532-179d4d0c4d8d // indirect
|
||||
github.com/nyaruka/phonenumbers v1.1.5
|
||||
github.com/pkoukk/tiktoken-go v0.1.1
|
||||
github.com/plutov/paypal/v4 v4.7.0
|
||||
github.com/pquerna/otp v1.4.0
|
||||
github.com/prometheus/client_golang v1.11.1
|
||||
github.com/prometheus/client_model v0.2.0
|
||||
github.com/qiangmzsx/string-adapter/v2 v2.1.0
|
||||
@@ -59,7 +59,7 @@ require (
|
||||
github.com/tealeg/xlsx v1.0.5
|
||||
github.com/thanhpk/randstr v1.0.4
|
||||
github.com/tklauser/go-sysconf v0.3.10 // indirect
|
||||
github.com/xorm-io/builder v0.3.13 // indirect
|
||||
github.com/xorm-io/builder v0.3.13
|
||||
github.com/xorm-io/core v0.7.4
|
||||
github.com/xorm-io/xorm v1.1.6
|
||||
github.com/yusufpapurcu/wmi v1.2.2 // indirect
|
||||
|
7
go.sum
7
go.sum
@@ -105,6 +105,8 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc h1:biVzkmvwrH8WK8raXaxBx6fRVTlJILwEwQGL1I/ByEI=
|
||||
github.com/boombuler/barcode v1.0.1-0.20190219062509-6c824513bacc/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8=
|
||||
github.com/bradfitz/gomemcache v0.0.0-20180710155616-bc664df96737/go.mod h1:PmM6Mmwb0LSuEubjR8N7PtNe1KxZLtOUHtbeikc5h60=
|
||||
github.com/bwesterb/go-ristretto v1.2.0/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
|
||||
github.com/casbin/casbin v1.7.0/go.mod h1:c67qKN6Oum3UF5Q1+BByfFxkwKvhwW57ITjqwtzR1KE=
|
||||
@@ -495,10 +497,10 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkoukk/tiktoken-go v0.1.1 h1:jtkYlIECjyM9OW1w4rjPmTohK4arORP9V25y6TM6nXo=
|
||||
github.com/pkoukk/tiktoken-go v0.1.1/go.mod h1:boMWvk9pQCOTx11pgu0DrIdrAKgQzzJKUP6vLXaz7Rw=
|
||||
github.com/plutov/paypal/v4 v4.7.0 h1:6TRvYD4ny6yQfHaABeStNf43GFM1wpW5jU/XEDGQmq0=
|
||||
github.com/plutov/paypal/v4 v4.7.0/go.mod h1:D56boafCRGcF/fEM0w282kj0fCDKIyrwOPX/Te1jCmw=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pquerna/otp v1.4.0 h1:wZvl1TIVxKRThZIBiwOOHOGP/1+nZyWBil9Y2XNEDzg=
|
||||
github.com/pquerna/otp v1.4.0/go.mod h1:dkJfzwRKNiegxyNb54X/3fLwhCynbMspSyWKnvi1AEg=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
@@ -595,7 +597,6 @@ github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXf
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "Fehlender Parameter",
|
||||
"Please login first": "Bitte zuerst einloggen",
|
||||
"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 server exist": "Es gibt einen LDAP-Server"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"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 must have at least 6 characters": "Das neue Passwort muss mindestens 6 Zeichen haben"
|
||||
"New password cannot contain blank space.": "Das neue Passwort darf keine Leerzeichen enthalten."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Fehler beim Importieren von Benutzern"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"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: "
|
||||
"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"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"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": "New password must have at least 6 characters"
|
||||
"New password cannot contain blank space.": "New password cannot contain blank space."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Failed to import users"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "Parámetro faltante",
|
||||
"Please login first": "Por favor, inicia sesión primero",
|
||||
"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 server exist": "El servidor LDAP existe"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"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 must have at least 6 characters": "La nueva contraseña debe tener al menos 6 caracteres"
|
||||
"New password cannot contain blank space.": "La nueva contraseña no puede contener espacios en blanco."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Error al importar usuarios"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "Paramètre manquant",
|
||||
"Please login first": "Veuillez d'abord vous connecter",
|
||||
"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 server exist": "Le serveur LDAP existe"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"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 must have at least 6 characters": "Le nouveau mot de passe doit comporter au moins 6 caractères"
|
||||
"New password cannot contain blank space.": "Le nouveau mot de passe ne peut pas contenir d'espace."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Échec de l'importation des utilisateurs"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "Parameter hilang",
|
||||
"Please login first": "Silahkan login terlebih dahulu",
|
||||
"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 server exist": "Server ldap ada"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"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 must have at least 6 characters": "Kata sandi baru harus memiliki setidaknya 6 karakter"
|
||||
"New password cannot contain blank space.": "Kata sandi baru tidak boleh mengandung spasi kosong."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Gagal mengimpor pengguna"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "不足しているパラメーター",
|
||||
"Please login first": "最初にログインしてください",
|
||||
"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 server exist": "LDAPサーバーは存在します"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "表示名は空にできません",
|
||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。",
|
||||
"New password must have at least 6 characters": "新しいパスワードは少なくとも6文字必要です"
|
||||
"New password cannot contain blank space.": "新しいパスワードにはスペースを含めることはできません。"
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "ユーザーのインポートに失敗しました"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "누락된 매개변수",
|
||||
"Please login first": "먼저 로그인 하십시오",
|
||||
"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 server exist": "LDAP 서버가 존재합니다"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "디스플레이 이름은 비어 있을 수 없습니다",
|
||||
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다.",
|
||||
"New password must have at least 6 characters": "새로운 비밀번호는 최소 6자 이상이어야 합니다"
|
||||
"New password cannot contain blank space.": "새 비밀번호에는 공백이 포함될 수 없습니다."
|
||||
},
|
||||
"user_upload": {
|
||||
"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": "Отсутствующий параметр",
|
||||
"Please login first": "Пожалуйста, сначала войдите в систему",
|
||||
"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 server exist": "LDAP-сервер существует"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "Отображаемое имя не может быть пустым",
|
||||
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы.",
|
||||
"New password must have at least 6 characters": "Новый пароль должен содержать не менее 6 символов"
|
||||
"New password cannot contain blank space.": "Новый пароль не может содержать пробелы."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Не удалось импортировать пользователей"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "Thiếu tham số",
|
||||
"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",
|
||||
"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 server exist": "Máy chủ LDAP tồn tại"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"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 must have at least 6 characters": "Mật khẩu mới phải có ít nhất 6 ký tự"
|
||||
"New password cannot contain blank space.": "Mật khẩu mới không thể chứa dấu trắng."
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "Không thể nhập người dùng"
|
||||
|
@@ -68,7 +68,8 @@
|
||||
"Missing parameter": "缺少参数",
|
||||
"Please login first": "请先登录",
|
||||
"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 server exist": "LDAP服务器已存在"
|
||||
@@ -119,8 +120,7 @@
|
||||
},
|
||||
"user": {
|
||||
"Display name cannot be empty": "显示名称不可为空",
|
||||
"New password cannot contain blank space.": "新密码不可以包含空格",
|
||||
"New password must have at least 6 characters": "新密码至少需要6位字符"
|
||||
"New password cannot contain blank space.": "新密码不可以包含空格"
|
||||
},
|
||||
"user_upload": {
|
||||
"Failed to import users": "导入用户失败"
|
||||
|
@@ -8,6 +8,7 @@
|
||||
"favicon": "",
|
||||
"passwordType": "plain",
|
||||
"passwordSalt": "",
|
||||
"passwordOptions": ["AtLeast6"],
|
||||
"countryCodes": ["US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"],
|
||||
"defaultAvatar": "",
|
||||
"defaultApplication": "",
|
||||
|
@@ -145,11 +145,6 @@ func (a *Adapter) createTable() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(UserGroupRelation))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = a.Engine.Sync2(new(Role))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
|
@@ -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"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
|
||||
Organization string `xorm:"varchar(100)" json:"organization"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Model string `xorm:"varchar(100)" json:"model"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
Model string `xorm:"varchar(100)" json:"model"`
|
||||
|
||||
Host string `xorm:"varchar(100)" json:"host"`
|
||||
Port int `json:"port"`
|
||||
@@ -46,14 +45,14 @@ type CasbinAdapter struct {
|
||||
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, "", "")
|
||||
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{}
|
||||
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 {
|
||||
return adapters, err
|
||||
}
|
||||
@@ -61,10 +60,10 @@ func GetCasbinAdapters(owner string, organization string) ([]*CasbinAdapter, err
|
||||
return adapters, nil
|
||||
}
|
||||
|
||||
func GetPaginationCasbinAdapters(owner, organization string, page, limit int, field, value, sort, order string) ([]*CasbinAdapter, error) {
|
||||
session := GetSession(owner, page, limit, field, value, sort, order)
|
||||
func GetPaginationCasbinAdapters(owner string, offset, limit int, field, value, sortField, sortOrder string) ([]*CasbinAdapter, error) {
|
||||
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 {
|
||||
return adapters, err
|
||||
}
|
||||
@@ -214,6 +213,10 @@ func SyncPolicies(casbinAdapter *CasbinAdapter) ([]*xormadapter.CasbinRule, erro
|
||||
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)
|
||||
if err != nil {
|
||||
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 {
|
||||
ldaps, err := GetLdaps(user.Owner)
|
||||
if err != nil {
|
||||
@@ -353,7 +363,7 @@ func CheckAccessPermission(userId string, application *Application) (bool, error
|
||||
|
||||
allowed := true
|
||||
for _, permission := range permissions {
|
||||
if !permission.IsEnabled || len(permission.Users) == 0 {
|
||||
if !permission.IsEnabled {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -386,11 +396,6 @@ func CheckUsername(username string, lang string) string {
|
||||
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
|
||||
}
|
||||
|
||||
exclude, _ := regexp.Compile("^[\u0021-\u007E]+$")
|
||||
if !exclude.MatchString(username) {
|
||||
return ""
|
||||
}
|
||||
|
||||
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
|
||||
re, _ := regexp.Compile("^[a-zA-Z0-9]+((?:-[a-zA-Z0-9]+)|(?:_[a-zA-Z0-9]+))*$")
|
||||
if !re.MatchString(username) {
|
||||
|
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 ""
|
||||
}
|
184
object/group.go
184
object/group.go
@@ -19,23 +19,23 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/builder"
|
||||
"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"`
|
||||
Name string `xorm:"varchar(100) notnull pk unique index" 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"`
|
||||
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 []*User `xorm:"-" json:"users"`
|
||||
|
||||
Title string `json:"title,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
@@ -95,24 +95,6 @@ func getGroup(owner string, name string) (*Group, error) {
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -125,7 +107,13 @@ func UpdateGroup(id string, group *Group) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
group.UpdatedTime = util.GetCurrentTime()
|
||||
if name != group.Name {
|
||||
err := GroupChangeTrigger(name, group.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(group)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -135,10 +123,6 @@ func UpdateGroup(id string, group *Group) (bool, error) {
|
||||
}
|
||||
|
||||
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
|
||||
@@ -164,37 +148,20 @@ func DeleteGroup(group *Group) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if count, err := adapter.Engine.Where("parent_id = ?", group.Id).Count(&Group{}); err != nil {
|
||||
if count, err := adapter.Engine.Where("parent_id = ?", group.Name).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 {
|
||||
if count, err := GetGroupUserCount(group.Name, "", ""); 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{})
|
||||
affected, err := adapter.Engine.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
|
||||
}
|
||||
|
||||
@@ -215,9 +182,8 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||
Key: group.Name,
|
||||
Type: group.Type,
|
||||
Owner: group.Owner,
|
||||
Id: group.Id,
|
||||
}
|
||||
children := ConvertToTreeData(groups, group.Id)
|
||||
children := ConvertToTreeData(groups, group.Name)
|
||||
if len(children) > 0 {
|
||||
node.Children = children
|
||||
}
|
||||
@@ -226,3 +192,113 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||
}
|
||||
return treeData
|
||||
}
|
||||
|
||||
func RemoveUserFromGroup(owner, name, groupName string) (bool, error) {
|
||||
user, err := getUser(owner, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
if user == nil {
|
||||
return false, errors.New("user not exist")
|
||||
}
|
||||
|
||||
user.Groups = util.DeleteVal(user.Groups, groupName)
|
||||
affected, err := updateUser(user.GetId(), user, []string{"groups"})
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return affected != 0, err
|
||||
}
|
||||
|
||||
func GetGroupUserCount(groupName string, field, value string) (int64, error) {
|
||||
if field == "" && value == "" {
|
||||
return adapter.Engine.Where(builder.Like{"`groups`", groupName}).
|
||||
Count(&User{})
|
||||
} else {
|
||||
return adapter.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName}).
|
||||
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||
Count()
|
||||
}
|
||||
}
|
||||
|
||||
func GetPaginationGroupUsers(groupName string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
session := adapter.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName})
|
||||
|
||||
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(groupName string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
err := adapter.Engine.Table("user").
|
||||
Where(builder.Like{"`groups`", groupName}).
|
||||
Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GroupChangeTrigger(oldName, newName string) error {
|
||||
session := adapter.Engine.NewSession()
|
||||
defer session.Close()
|
||||
err := session.Begin()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
users := []*User{}
|
||||
err = session.Where(builder.Like{"`groups`", oldName}).Find(&users)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
user.Groups = util.ReplaceVal(user.Groups, oldName, newName)
|
||||
_, err := updateUser(user.GetId(), user, []string{"groups"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
groups := []*Group{}
|
||||
err = session.Where("parent_id = ?", oldName).Find(&groups)
|
||||
for _, group := range groups {
|
||||
group.ParentId = newName
|
||||
_, err := session.ID(core.PK{group.Owner, group.Name}).Cols("parent_id").Update(group)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
err = session.Commit()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
@@ -92,6 +92,7 @@ func initBuiltInOrganization() bool {
|
||||
WebsiteUrl: "https://example.com",
|
||||
Favicon: fmt.Sprintf("%s/img/casbin/favicon.ico", conf.GetConfigString("staticBaseUrl")),
|
||||
PasswordType: "plain",
|
||||
PasswordOptions: []string{"AtLeast6"},
|
||||
CountryCodes: []string{"US", "ES", "CN", "FR", "DE", "GB", "JP", "KR", "VN", "ID", "SG", "IN"},
|
||||
DefaultAvatar: fmt.Sprintf("%s/img/casbin.svg", conf.GetConfigString("staticBaseUrl")),
|
||||
Tags: []string{},
|
||||
|
143
object/mfa.go
143
object/mfa.go
@@ -22,14 +22,16 @@ import (
|
||||
"github.com/beego/beego/context"
|
||||
)
|
||||
|
||||
const MfaRecoveryCodesSession = "mfa_recovery_codes"
|
||||
|
||||
type MfaSessionData struct {
|
||||
UserId string
|
||||
}
|
||||
|
||||
type MfaProps struct {
|
||||
Id string `json:"id"`
|
||||
Enabled bool `json:"enabled"`
|
||||
IsPreferred bool `json:"isPreferred"`
|
||||
AuthType string `json:"type" form:"type"`
|
||||
MfaType string `json:"mfaType" form:"mfaType"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
CountryCode string `json:"countryCode,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
@@ -37,15 +39,16 @@ type MfaProps struct {
|
||||
}
|
||||
|
||||
type MfaInterface interface {
|
||||
SetupVerify(ctx *context.Context, passCode string) error
|
||||
Verify(passCode string) error
|
||||
Initiate(ctx *context.Context, name1 string, name2 string) (*MfaProps, error)
|
||||
Initiate(ctx *context.Context, userId string) (*MfaProps, error)
|
||||
SetupVerify(ctx *context.Context, passcode string) error
|
||||
Enable(ctx *context.Context, user *User) error
|
||||
Verify(passcode string) error
|
||||
}
|
||||
|
||||
const (
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
EmailType = "email"
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -54,28 +57,30 @@ const (
|
||||
RequiredMfa = "RequiredMfa"
|
||||
)
|
||||
|
||||
func GetMfaUtil(providerType string, config *MfaProps) MfaInterface {
|
||||
switch providerType {
|
||||
func GetMfaUtil(mfaType string, config *MfaProps) MfaInterface {
|
||||
switch mfaType {
|
||||
case SmsType:
|
||||
return NewSmsTwoFactor(config)
|
||||
return NewSmsMfaUtil(config)
|
||||
case EmailType:
|
||||
return NewEmailMfaUtil(config)
|
||||
case TotpType:
|
||||
return nil
|
||||
return NewTotpMfaUtil(config)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func RecoverTfs(user *User, recoveryCode string) error {
|
||||
func MfaRecover(user *User, recoveryCode string) error {
|
||||
hit := false
|
||||
|
||||
twoFactor := user.GetPreferMfa(false)
|
||||
if len(twoFactor.RecoveryCodes) == 0 {
|
||||
if len(user.RecoveryCodes) == 0 {
|
||||
return fmt.Errorf("do not have recovery codes")
|
||||
}
|
||||
|
||||
for _, code := range twoFactor.RecoveryCodes {
|
||||
for _, code := range user.RecoveryCodes {
|
||||
if code == recoveryCode {
|
||||
hit = true
|
||||
user.RecoveryCodes = util.DeleteVal(user.RecoveryCodes, code)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -83,30 +88,106 @@ func RecoverTfs(user *User, recoveryCode string) error {
|
||||
return fmt.Errorf("recovery code not found")
|
||||
}
|
||||
|
||||
affected, err := UpdateUser(user.GetId(), user, []string{"two_factor_auth"}, user.IsAdminUser())
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, user.IsAdminUser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !affected {
|
||||
return fmt.Errorf("")
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetAllMfaProps(user *User, masked bool) []*MfaProps {
|
||||
mfaProps := []*MfaProps{}
|
||||
|
||||
for _, mfaType := range []string{SmsType, EmailType, TotpType} {
|
||||
mfaProps = append(mfaProps, user.GetMfaProps(mfaType, masked))
|
||||
}
|
||||
return mfaProps
|
||||
}
|
||||
|
||||
func (user *User) GetMfaProps(mfaType string, masked bool) *MfaProps {
|
||||
mfaProps := &MfaProps{}
|
||||
|
||||
if mfaType == SmsType {
|
||||
if !user.MfaPhoneEnabled {
|
||||
return &MfaProps{
|
||||
Enabled: false,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
}
|
||||
|
||||
mfaProps = &MfaProps{
|
||||
Enabled: user.MfaPhoneEnabled,
|
||||
MfaType: mfaType,
|
||||
CountryCode: user.CountryCode,
|
||||
}
|
||||
if masked {
|
||||
mfaProps.Secret = util.GetMaskedPhone(user.Phone)
|
||||
} else {
|
||||
mfaProps.Secret = user.Phone
|
||||
}
|
||||
} else if mfaType == EmailType {
|
||||
if !user.MfaEmailEnabled {
|
||||
return &MfaProps{
|
||||
Enabled: false,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
}
|
||||
|
||||
mfaProps = &MfaProps{
|
||||
Enabled: user.MfaEmailEnabled,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
if masked {
|
||||
mfaProps.Secret = util.GetMaskedEmail(user.Email)
|
||||
} else {
|
||||
mfaProps.Secret = user.Email
|
||||
}
|
||||
} else if mfaType == TotpType {
|
||||
if user.TotpSecret == "" {
|
||||
return &MfaProps{
|
||||
Enabled: false,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
}
|
||||
|
||||
mfaProps = &MfaProps{
|
||||
Enabled: true,
|
||||
MfaType: mfaType,
|
||||
}
|
||||
if masked {
|
||||
mfaProps.Secret = ""
|
||||
} else {
|
||||
mfaProps.Secret = user.TotpSecret
|
||||
}
|
||||
}
|
||||
|
||||
if user.PreferredMfaType == mfaType {
|
||||
mfaProps.IsPreferred = true
|
||||
}
|
||||
return mfaProps
|
||||
}
|
||||
|
||||
func DisabledMultiFactorAuth(user *User) error {
|
||||
user.PreferredMfaType = ""
|
||||
user.RecoveryCodes = []string{}
|
||||
user.MfaPhoneEnabled = false
|
||||
user.MfaEmailEnabled = false
|
||||
user.TotpSecret = ""
|
||||
|
||||
_, err := updateUser(user.GetId(), user, []string{"preferred_mfa_type", "recovery_codes", "mfa_phone_enabled", "mfa_email_enabled", "totp_secret"})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func GetMaskedProps(props *MfaProps) *MfaProps {
|
||||
maskedProps := &MfaProps{
|
||||
AuthType: props.AuthType,
|
||||
Id: props.Id,
|
||||
IsPreferred: props.IsPreferred,
|
||||
}
|
||||
func SetPreferredMultiFactorAuth(user *User, mfaType string) error {
|
||||
user.PreferredMfaType = mfaType
|
||||
|
||||
if props.AuthType == SmsType {
|
||||
if !util.IsEmailValid(props.Secret) {
|
||||
maskedProps.Secret = util.GetMaskedPhone(props.Secret)
|
||||
} else {
|
||||
maskedProps.Secret = util.GetMaskedEmail(props.Secret)
|
||||
}
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, user.IsAdminUser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return maskedProps
|
||||
return nil
|
||||
}
|
||||
|
@@ -18,22 +18,35 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaSmsCountryCodeSession = "mfa_country_code"
|
||||
MfaSmsDestSession = "mfa_dest"
|
||||
MfaSmsRecoveryCodesSession = "mfa_recovery_codes"
|
||||
MfaSmsCountryCodeSession = "mfa_country_code"
|
||||
MfaSmsDestSession = "mfa_dest"
|
||||
)
|
||||
|
||||
type SmsMfa struct {
|
||||
Config *MfaProps
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
|
||||
recoveryCode := uuid.NewString()
|
||||
|
||||
err := ctx.Input.CruSession.Set(MfaRecoveryCodesSession, []string{recoveryCode})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mfaProps := MfaProps{
|
||||
MfaType: mfa.Config.MfaType,
|
||||
RecoveryCodes: []string{recoveryCode},
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
|
||||
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
|
||||
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
|
||||
@@ -47,6 +60,45 @@ func (mfa *SmsMfa) SetupVerify(ctx *context.Context, passCode string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
|
||||
recoveryCodes := ctx.Input.CruSession.Get(MfaRecoveryCodesSession).([]string)
|
||||
if len(recoveryCodes) == 0 {
|
||||
return fmt.Errorf("recovery codes is missing")
|
||||
}
|
||||
|
||||
columns := []string{"recovery_codes", "preferred_mfa_type"}
|
||||
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
|
||||
if user.PreferredMfaType == "" {
|
||||
user.PreferredMfaType = mfa.Config.MfaType
|
||||
}
|
||||
|
||||
if mfa.Config.MfaType == SmsType {
|
||||
user.MfaPhoneEnabled = true
|
||||
columns = append(columns, "mfa_phone_enabled")
|
||||
|
||||
if user.Phone == "" {
|
||||
user.Phone = ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
|
||||
user.CountryCode = ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
|
||||
columns = append(columns, "phone", "country_code")
|
||||
}
|
||||
} else if mfa.Config.MfaType == EmailType {
|
||||
user.MfaEmailEnabled = true
|
||||
columns = append(columns, "mfa_email_enabled")
|
||||
|
||||
if user.Email == "" {
|
||||
user.Email = ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
|
||||
columns = append(columns, "email")
|
||||
}
|
||||
}
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, columns, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Verify(passCode string) error {
|
||||
if !util.IsEmailValid(mfa.Config.Secret) {
|
||||
mfa.Config.Secret, _ = util.GetE164Number(mfa.Config.Secret, mfa.Config.CountryCode)
|
||||
@@ -57,65 +109,21 @@ func (mfa *SmsMfa) Verify(passCode string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Initiate(ctx *context.Context, name string, secret string) (*MfaProps, error) {
|
||||
recoveryCode, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.Input.CruSession.Set(MfaSmsRecoveryCodesSession, []string{recoveryCode.String()})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mfaProps := MfaProps{
|
||||
AuthType: SmsType,
|
||||
RecoveryCodes: []string{recoveryCode.String()},
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *SmsMfa) Enable(ctx *context.Context, user *User) error {
|
||||
dest := ctx.Input.CruSession.Get(MfaSmsDestSession).(string)
|
||||
recoveryCodes := ctx.Input.CruSession.Get(MfaSmsRecoveryCodesSession).([]string)
|
||||
countryCode := ctx.Input.CruSession.Get(MfaSmsCountryCodeSession).(string)
|
||||
|
||||
if dest == "" || len(recoveryCodes) == 0 {
|
||||
return fmt.Errorf("MFA dest or recovery codes is empty")
|
||||
}
|
||||
|
||||
if !util.IsEmailValid(dest) {
|
||||
mfa.Config.CountryCode = countryCode
|
||||
}
|
||||
|
||||
mfa.Config.AuthType = SmsType
|
||||
mfa.Config.Id = uuid.NewString()
|
||||
mfa.Config.Secret = dest
|
||||
mfa.Config.RecoveryCodes = recoveryCodes
|
||||
|
||||
for i, mfaProp := range user.MultiFactorAuths {
|
||||
if mfaProp.Secret == mfa.Config.Secret {
|
||||
user.MultiFactorAuths = append(user.MultiFactorAuths[:i], user.MultiFactorAuths[i+1:]...)
|
||||
}
|
||||
}
|
||||
user.MultiFactorAuths = append(user.MultiFactorAuths, mfa.Config)
|
||||
|
||||
affected, err := UpdateUser(user.GetId(), user, []string{"multi_factor_auths"}, user.IsAdminUser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !affected {
|
||||
return fmt.Errorf("failed to enable two factor authentication")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSmsTwoFactor(config *MfaProps) *SmsMfa {
|
||||
func NewSmsMfaUtil(config *MfaProps) *SmsMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
AuthType: SmsType,
|
||||
MfaType: SmsType,
|
||||
}
|
||||
}
|
||||
return &SmsMfa{
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
func NewEmailMfaUtil(config *MfaProps) *SmsMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
MfaType: EmailType,
|
||||
}
|
||||
}
|
||||
return &SmsMfa{
|
||||
|
133
object/mfa_totp.go
Normal file
133
object/mfa_totp.go
Normal file
@@ -0,0 +1,133 @@
|
||||
// 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/beego/beego"
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/google/uuid"
|
||||
"github.com/pquerna/otp"
|
||||
"github.com/pquerna/otp/totp"
|
||||
)
|
||||
|
||||
const MfaTotpSecretSession = "mfa_totp_secret"
|
||||
|
||||
type TotpMfa struct {
|
||||
Config *MfaProps
|
||||
period uint
|
||||
secretSize uint
|
||||
digits otp.Digits
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Initiate(ctx *context.Context, userId string) (*MfaProps, error) {
|
||||
issuer := beego.AppConfig.String("appname")
|
||||
if issuer == "" {
|
||||
issuer = "casdoor"
|
||||
}
|
||||
|
||||
key, err := totp.Generate(totp.GenerateOpts{
|
||||
Issuer: issuer,
|
||||
AccountName: userId,
|
||||
Period: mfa.period,
|
||||
SecretSize: mfa.secretSize,
|
||||
Digits: mfa.digits,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
err = ctx.Input.CruSession.Set(MfaTotpSecretSession, key.Secret())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
recoveryCode := uuid.NewString()
|
||||
err = ctx.Input.CruSession.Set(MfaRecoveryCodesSession, []string{recoveryCode})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mfaProps := MfaProps{
|
||||
MfaType: mfa.Config.MfaType,
|
||||
RecoveryCodes: []string{recoveryCode},
|
||||
Secret: key.Secret(),
|
||||
URL: key.URL(),
|
||||
}
|
||||
return &mfaProps, nil
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) SetupVerify(ctx *context.Context, passcode string) error {
|
||||
secret := ctx.Input.CruSession.Get(MfaTotpSecretSession).(string)
|
||||
result := totp.Validate(passcode, secret)
|
||||
|
||||
if result {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("totp passcode error")
|
||||
}
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Enable(ctx *context.Context, user *User) error {
|
||||
recoveryCodes := ctx.Input.CruSession.Get(MfaRecoveryCodesSession).([]string)
|
||||
if len(recoveryCodes) == 0 {
|
||||
return fmt.Errorf("recovery codes is missing")
|
||||
}
|
||||
secret := ctx.Input.CruSession.Get(MfaTotpSecretSession).(string)
|
||||
if secret == "" {
|
||||
return fmt.Errorf("totp secret is missing")
|
||||
}
|
||||
|
||||
columns := []string{"recovery_codes", "preferred_mfa_type", "totp_secret"}
|
||||
|
||||
user.RecoveryCodes = append(user.RecoveryCodes, recoveryCodes...)
|
||||
user.TotpSecret = secret
|
||||
if user.PreferredMfaType == "" {
|
||||
user.PreferredMfaType = mfa.Config.MfaType
|
||||
}
|
||||
|
||||
_, err := updateUser(user.GetId(), user, columns)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (mfa *TotpMfa) Verify(passcode string) error {
|
||||
result := totp.Validate(passcode, mfa.Config.Secret)
|
||||
|
||||
if result {
|
||||
return nil
|
||||
} else {
|
||||
return errors.New("totp passcode error")
|
||||
}
|
||||
}
|
||||
|
||||
func NewTotpMfaUtil(config *MfaProps) *TotpMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
MfaType: TotpType,
|
||||
}
|
||||
}
|
||||
|
||||
return &TotpMfa{
|
||||
Config: config,
|
||||
period: 30,
|
||||
secretSize: 20,
|
||||
digits: otp.DigitsSix,
|
||||
}
|
||||
}
|
@@ -56,6 +56,7 @@ type Organization struct {
|
||||
Favicon string `xorm:"varchar(100)" json:"favicon"`
|
||||
PasswordType string `xorm:"varchar(100)" json:"passwordType"`
|
||||
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
|
||||
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
|
||||
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
|
||||
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
|
||||
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
|
||||
@@ -401,7 +402,6 @@ func organizationChangeTrigger(oldName string, newName string) error {
|
||||
|
||||
casbinAdapter := new(CasbinAdapter)
|
||||
casbinAdapter.Owner = newName
|
||||
casbinAdapter.Organization = newName
|
||||
_, err = session.Where("owner=?", oldName).Update(casbinAdapter)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@@ -264,18 +264,48 @@ func DeletePermission(permission *Permission) (bool, error) {
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func GetPermissionsByUser(userId string) ([]*Permission, error) {
|
||||
func GetPermissionsAndRolesByUser(userId string) ([]*Permission, []*Role, error) {
|
||||
permissions := []*Permission{}
|
||||
err := adapter.Engine.Where("users like ?", "%"+userId+"\"%").Find(&permissions)
|
||||
if err != nil {
|
||||
return permissions, err
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
for i := range permissions {
|
||||
permissions[i].Users = nil
|
||||
existedPerms := map[string]struct{}{}
|
||||
|
||||
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) {
|
||||
@@ -340,3 +370,24 @@ func GetMaskedPermissions(permissions []*Permission) []*Permission {
|
||||
|
||||
return permissions
|
||||
}
|
||||
|
||||
// GroupPermissionsByModelAdapter group permissions by model and adapter.
|
||||
// Every model and adapter will be a key, and the value is a list of permission ids.
|
||||
// With each list of permission ids have the same key, we just need to init the
|
||||
// enforcer and do the enforce/batch-enforce once (with list of permission ids
|
||||
// as the policyFilter when the enforcer load policy).
|
||||
func GroupPermissionsByModelAdapter(permissions []*Permission) map[string][]string {
|
||||
m := make(map[string][]string)
|
||||
|
||||
for _, permission := range permissions {
|
||||
key := permission.Model + permission.Adapter
|
||||
permissionIds, ok := m[key]
|
||||
if !ok {
|
||||
m[key] = []string{permission.GetId()}
|
||||
} else {
|
||||
m[key] = append(permissionIds, permission.GetId())
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
@@ -26,7 +26,7 @@ import (
|
||||
xormadapter "github.com/casdoor/xorm-adapter/v3"
|
||||
)
|
||||
|
||||
func getEnforcer(permission *Permission) *casbin.Enforcer {
|
||||
func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforcer {
|
||||
tableName := "permission_rule"
|
||||
if len(permission.Adapter) != 0 {
|
||||
adapterObj, err := getCasbinAdapter(permission.Owner, permission.Adapter)
|
||||
@@ -77,8 +77,13 @@ func getEnforcer(permission *Permission) *casbin.Enforcer {
|
||||
|
||||
enforcer.SetAdapter(adapter)
|
||||
|
||||
policyFilterV5 := []string{permission.GetId()}
|
||||
if len(permissionIDs) != 0 {
|
||||
policyFilterV5 = permissionIDs
|
||||
}
|
||||
|
||||
policyFilter := xormadapter.Filter{
|
||||
V5: []string{permission.GetId()},
|
||||
V5: policyFilterV5,
|
||||
}
|
||||
|
||||
if !HasRoleDefinition(m) {
|
||||
@@ -241,33 +246,18 @@ func removePolicies(permission *Permission) {
|
||||
|
||||
type CasbinRequest = []interface{}
|
||||
|
||||
func Enforce(permissionId string, request *CasbinRequest) (bool, error) {
|
||||
permission, err := GetPermission(permissionId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
enforcer := getEnforcer(permission)
|
||||
func Enforce(permission *Permission, request *CasbinRequest, permissionIds ...string) (bool, error) {
|
||||
enforcer := getEnforcer(permission, permissionIds...)
|
||||
return enforcer.Enforce(*request...)
|
||||
}
|
||||
|
||||
func BatchEnforce(permissionId string, requests *[]CasbinRequest) ([]bool, error) {
|
||||
permission, err := GetPermission(permissionId)
|
||||
if err != nil {
|
||||
res := []bool{}
|
||||
for i := 0; i < len(*requests); i++ {
|
||||
res = append(res, false)
|
||||
}
|
||||
|
||||
return res, err
|
||||
}
|
||||
|
||||
enforcer := getEnforcer(permission)
|
||||
func BatchEnforce(permission *Permission, requests *[]CasbinRequest, permissionIds ...string) ([]bool, error) {
|
||||
enforcer := getEnforcer(permission, permissionIds...)
|
||||
return enforcer.BatchEnforce(*requests)
|
||||
}
|
||||
|
||||
func getAllValues(userId string, fn func(enforcer *casbin.Enforcer) []string) []string {
|
||||
permissions, err := GetPermissionsByUser(userId)
|
||||
permissions, _, err := GetPermissionsAndRolesByUser(userId)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -43,6 +43,7 @@ func UploadPermissions(owner string, fileId string) (bool, error) {
|
||||
|
||||
newPermissions := []*Permission{}
|
||||
for index, line := range table {
|
||||
line := line
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
@@ -259,11 +259,22 @@ func GetRolesByUser(userId string) ([]*Role, error) {
|
||||
return roles, err
|
||||
}
|
||||
|
||||
for i := range roles {
|
||||
roles[i].Users = nil
|
||||
allRolesIds := make([]string, 0, len(roles))
|
||||
|
||||
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 {
|
||||
@@ -335,14 +346,22 @@ func GetRolesByNamePrefix(owner string, prefix string) ([]*Role, error) {
|
||||
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 (
|
||||
result []*Role
|
||||
result = []*Role{}
|
||||
roleMap = make(map[string]*Role)
|
||||
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)
|
||||
if err != nil {
|
||||
@@ -360,7 +379,7 @@ func GetAncestorRoles(roleId string) ([]*Role, error) {
|
||||
result = append(result, r)
|
||||
} else if !ok {
|
||||
rId := r.GetId()
|
||||
visited[rId] = containsRole(r, roleId, roleMap, visited)
|
||||
visited[rId] = containsRole(r, roleMap, visited, roleIds...)
|
||||
if visited[rId] {
|
||||
result = append(result, r)
|
||||
}
|
||||
@@ -370,19 +389,19 @@ func GetAncestorRoles(roleId string) ([]*Role, error) {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// containsRole is a helper function to check if a slice of roles contains a specific roleId
|
||||
func containsRole(role *Role, roleId string, roleMap map[string]*Role, visited map[string]bool) bool {
|
||||
// containsRole is a helper function to check if a roles is related to any role in the given list roles
|
||||
func containsRole(role *Role, roleMap map[string]*Role, visited map[string]bool, roleIds ...string) bool {
|
||||
if isContain, ok := visited[role.GetId()]; ok {
|
||||
return isContain
|
||||
}
|
||||
|
||||
for _, subRole := range role.Roles {
|
||||
if subRole == roleId {
|
||||
if util.HasString(roleIds, subRole) {
|
||||
return true
|
||||
}
|
||||
|
||||
r, ok := roleMap[subRole]
|
||||
if ok && containsRole(r, roleId, roleMap, visited) {
|
||||
if ok && containsRole(r, roleMap, visited, roleIds...) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
@@ -43,6 +43,7 @@ func UploadRoles(owner string, fileId string) (bool, error) {
|
||||
|
||||
newRoles := []*Role{}
|
||||
for index, line := range table {
|
||||
line := line
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
@@ -260,10 +260,17 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
// decompress
|
||||
var buffer bytes.Buffer
|
||||
rdr := flate.NewReader(bytes.NewReader(defated))
|
||||
_, err = io.Copy(&buffer, rdr)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
|
||||
for {
|
||||
_, err := io.CopyN(&buffer, rdr, 1024)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", "", "", err
|
||||
}
|
||||
}
|
||||
|
||||
var authnRequest saml.AuthnRequest
|
||||
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
||||
if err != nil {
|
||||
|
@@ -50,6 +50,7 @@ type Syncer struct {
|
||||
AvatarBaseUrl string `xorm:"varchar(100)" json:"avatarBaseUrl"`
|
||||
ErrorText string `xorm:"mediumtext" json:"errorText"`
|
||||
SyncInterval int `json:"syncInterval"`
|
||||
IsReadOnly bool `json:"isReadOnly"`
|
||||
IsEnabled bool `json:"isEnabled"`
|
||||
|
||||
Adapter *Adapter `xorm:"-" json:"-"`
|
||||
|
@@ -63,9 +63,11 @@ func (syncer *Syncer) syncUsers() {
|
||||
}
|
||||
} else {
|
||||
if user.PreHash == oHash {
|
||||
updatedOUser := syncer.createOriginalUserFromUser(user)
|
||||
syncer.updateUser(updatedOUser)
|
||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||
if !syncer.IsReadOnly {
|
||||
updatedOUser := syncer.createOriginalUserFromUser(user)
|
||||
syncer.updateUser(updatedOUser)
|
||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||
}
|
||||
|
||||
// update preHash
|
||||
user.PreHash = user.Hash
|
||||
@@ -91,15 +93,17 @@ func (syncer *Syncer) syncUsers() {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
id := user.Id
|
||||
if _, ok := oUserMap[id]; !ok {
|
||||
newOUser := syncer.createOriginalUserFromUser(user)
|
||||
_, err = syncer.addUser(newOUser)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
if !syncer.IsReadOnly {
|
||||
for _, user := range users {
|
||||
id := user.Id
|
||||
if _, ok := oUserMap[id]; !ok {
|
||||
newOUser := syncer.createOriginalUserFromUser(user)
|
||||
_, err = syncer.addUser(newOUser)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
fmt.Printf("New oUser: %v\n", newOUser)
|
||||
}
|
||||
fmt.Printf("New oUser: %v\n", newOUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -170,6 +170,7 @@ func (syncer *Syncer) getOriginalUsersFromMap(results []map[string]string) []*Or
|
||||
originalUser := &OriginalUser{
|
||||
Address: []string{},
|
||||
Properties: map[string]string{},
|
||||
Groups: []string{},
|
||||
}
|
||||
|
||||
for _, tableColumn := range syncer.TableColumns {
|
||||
|
@@ -794,7 +794,7 @@ func GetWechatMiniProgramToken(application *Application, code string, host strin
|
||||
ErrorDescription: "the wechat mini program session is invalid",
|
||||
}, nil
|
||||
}
|
||||
user, err := getUserByWechatId(openId, unionId)
|
||||
user, err := getUserByWechatId(application.Organization, openId, unionId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@@ -216,6 +216,9 @@ func refineUser(user *User) *User {
|
||||
if user.Permissions == nil {
|
||||
user.Permissions = []*Permission{}
|
||||
}
|
||||
if user.Groups == nil {
|
||||
user.Groups = []string{}
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
170
object/user.go
170
object/user.go
@@ -15,12 +15,10 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/conf"
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/go-webauthn/webauthn/webauthn"
|
||||
"github.com/xorm-io/core"
|
||||
@@ -46,6 +44,7 @@ type User struct {
|
||||
FirstName string `xorm:"varchar(100)" json:"firstName"`
|
||||
LastName string `xorm:"varchar(100)" json:"lastName"`
|
||||
Avatar string `xorm:"varchar(500)" json:"avatar"`
|
||||
AvatarType string `xorm:"varchar(100)" json:"avatarType"`
|
||||
PermanentAvatar string `xorm:"varchar(500)" json:"permanentAvatar"`
|
||||
Email string `xorm:"varchar(100) index" json:"email"`
|
||||
EmailVerified bool `json:"emailVerified"`
|
||||
@@ -77,9 +76,8 @@ type User struct {
|
||||
SignupApplication string `xorm:"varchar(100)" json:"signupApplication"`
|
||||
Hash string `xorm:"varchar(100)" json:"hash"`
|
||||
PreHash string `xorm:"varchar(100)" json:"preHash"`
|
||||
Groups []string `xorm:"varchar(1000)" json:"groups"`
|
||||
AccessKey string `xorm:"varchar(100)" json:"accessKey"`
|
||||
SecretKey string `xorm:"varchar(100)" json:"secretKey"`
|
||||
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
|
||||
|
||||
CreatedIp string `xorm:"varchar(100)" json:"createdIp"`
|
||||
LastSigninTime string `xorm:"varchar(100)" json:"lastSigninTime"`
|
||||
@@ -161,13 +159,19 @@ type User struct {
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
|
||||
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
|
||||
MultiFactorAuths []*MfaProps `json:"multiFactorAuths"`
|
||||
PreferredMfaType string `xorm:"varchar(100)" json:"preferredMfaType"`
|
||||
RecoveryCodes []string `xorm:"varchar(1000)" json:"recoveryCodes,omitempty"`
|
||||
TotpSecret string `xorm:"varchar(100)" json:"totpSecret,omitempty"`
|
||||
MfaPhoneEnabled bool `json:"mfaPhoneEnabled"`
|
||||
MfaEmailEnabled bool `json:"mfaEmailEnabled"`
|
||||
MultiFactorAuths []*MfaProps `xorm:"-" json:"multiFactorAuths,omitempty"`
|
||||
|
||||
Ldap string `xorm:"ldap varchar(100)" json:"ldap"`
|
||||
Properties map[string]string `json:"properties"`
|
||||
|
||||
Roles []*Role `json:"roles"`
|
||||
Permissions []*Permission `json:"permissions"`
|
||||
Groups []string `xorm:"groups varchar(1000)" json:"groups"`
|
||||
|
||||
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
|
||||
SigninWrongTimes int `json:"signinWrongTimes"`
|
||||
@@ -221,11 +225,11 @@ func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOr
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUserCount(owner, field, value string, groupId string) (int64, error) {
|
||||
func GetUserCount(owner, field, value string, groupName string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
|
||||
if groupId != "" {
|
||||
return GetGroupUserCount(groupId, field, value)
|
||||
if groupName != "" {
|
||||
return GetGroupUserCount(groupName, field, value)
|
||||
}
|
||||
|
||||
return session.Count(&User{})
|
||||
@@ -265,11 +269,11 @@ func GetSortedUsers(owner string, sorter string, limit int) ([]*User, error) {
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string, groupId string) ([]*User, error) {
|
||||
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string, groupName string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
|
||||
if groupId != "" {
|
||||
return GetPaginationGroupUsers(groupId, offset, limit, field, value, sortField, sortOrder)
|
||||
if groupName != "" {
|
||||
return GetPaginationGroupUsers(groupName, offset, limit, field, value, sortField, sortOrder)
|
||||
}
|
||||
|
||||
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
@@ -316,12 +320,12 @@ func getUserById(owner string, id string) (*User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func getUserByWechatId(wechatOpenId string, wechatUnionId string) (*User, error) {
|
||||
func getUserByWechatId(owner string, wechatOpenId string, wechatUnionId string) (*User, error) {
|
||||
if wechatUnionId == "" {
|
||||
wechatUnionId = wechatOpenId
|
||||
}
|
||||
user := &User{}
|
||||
existed, err := adapter.Engine.Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
|
||||
existed, err := adapter.Engine.Where("owner = ?", owner).Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -426,18 +430,22 @@ func GetMaskedUser(user *User, errs ...error) (*User, error) {
|
||||
if user.Password != "" {
|
||||
user.Password = "***"
|
||||
}
|
||||
|
||||
if user.AccessSecret != "" {
|
||||
user.AccessSecret = "***"
|
||||
}
|
||||
if user.ManagedAccounts != nil {
|
||||
for _, manageAccount := range user.ManagedAccounts {
|
||||
manageAccount.Password = "***"
|
||||
}
|
||||
}
|
||||
|
||||
if user.MultiFactorAuths != nil {
|
||||
for i, props := range user.MultiFactorAuths {
|
||||
user.MultiFactorAuths[i] = GetMaskedProps(props)
|
||||
}
|
||||
if user.TotpSecret != "" {
|
||||
user.TotpSecret = ""
|
||||
}
|
||||
if user.RecoveryCodes != nil {
|
||||
user.RecoveryCodes = nil
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
@@ -484,17 +492,13 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
if name != user.Name {
|
||||
err := userChangeTrigger(name, user.Name)
|
||||
if err != nil {
|
||||
return false, nil
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
if user.Password == "***" {
|
||||
user.Password = oldUser.Password
|
||||
}
|
||||
err = user.UpdateUserHash()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if user.Avatar != oldUser.Avatar && user.Avatar != "" && user.PermanentAvatar != "*" {
|
||||
user.PermanentAvatar, err = getPermanentAvatarUrl(user.Owner, user.Name, user.Avatar, false)
|
||||
@@ -508,7 +512,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
"owner", "display_name", "avatar",
|
||||
"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",
|
||||
"signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "secret_key",
|
||||
"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",
|
||||
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
|
||||
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
|
||||
@@ -522,7 +526,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
columns = append(columns, "name", "email", "phone", "country_code")
|
||||
}
|
||||
|
||||
affected, err := updateUser(oldUser, user, columns)
|
||||
affected, err := updateUser(id, user, columns)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -530,32 +534,17 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
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)
|
||||
func updateUser(id string, user *User, columns []string) (int64, error) {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
err := user.UpdateUserHash()
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return affected, err
|
||||
}
|
||||
|
||||
err = session.Commit()
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
@@ -726,11 +715,6 @@ func DeleteUser(user *User) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err = deleteRelationByUser(user.Id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
@@ -746,7 +730,7 @@ func GetUserInfo(user *User, scope string, aud string, host string) *Userinfo {
|
||||
resp.Name = user.Name
|
||||
resp.DisplayName = user.DisplayName
|
||||
resp.Avatar = user.Avatar
|
||||
resp.Groups = []string{user.Owner}
|
||||
resp.Groups = user.Groups
|
||||
}
|
||||
if strings.Contains(scope, "email") {
|
||||
resp.Email = user.Email
|
||||
@@ -777,12 +761,15 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
||||
return
|
||||
}
|
||||
|
||||
user.Roles, err = GetRolesByUser(user.GetId())
|
||||
user.Permissions, user.Roles, err = GetPermissionsAndRolesByUser(user.GetId())
|
||||
if err != nil {
|
||||
return
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Groups == nil {
|
||||
user.Groups = []string{}
|
||||
}
|
||||
|
||||
user.Permissions, err = GetPermissionsByUser(user.GetId())
|
||||
return
|
||||
}
|
||||
|
||||
@@ -844,76 +831,15 @@ func userChangeTrigger(oldName string, newName string) error {
|
||||
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 {
|
||||
return len(user.MultiFactorAuths) > 0
|
||||
return user.PreferredMfaType != ""
|
||||
}
|
||||
|
||||
func (user *User) GetPreferMfa(masked bool) *MfaProps {
|
||||
if len(user.MultiFactorAuths) == 0 {
|
||||
func (user *User) GetPreferredMfaProps(masked bool) *MfaProps {
|
||||
if user.PreferredMfaType == "" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if masked {
|
||||
if len(user.MultiFactorAuths) == 1 {
|
||||
return GetMaskedProps(user.MultiFactorAuths[0])
|
||||
}
|
||||
for _, v := range user.MultiFactorAuths {
|
||||
if v.IsPreferred {
|
||||
return GetMaskedProps(v)
|
||||
}
|
||||
}
|
||||
return GetMaskedProps(user.MultiFactorAuths[0])
|
||||
} else {
|
||||
if len(user.MultiFactorAuths) == 1 {
|
||||
return user.MultiFactorAuths[0]
|
||||
}
|
||||
for _, v := range user.MultiFactorAuths {
|
||||
if v.IsPreferred {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return user.MultiFactorAuths[0]
|
||||
}
|
||||
return user.GetMfaProps(user.PreferredMfaType, masked)
|
||||
}
|
||||
|
||||
func AddUserkeys(user *User, isAdmin bool) (bool, error) {
|
||||
@@ -922,7 +848,7 @@ func AddUserkeys(user *User, isAdmin bool) (bool, error) {
|
||||
}
|
||||
|
||||
user.AccessKey = util.GenerateId()
|
||||
user.SecretKey = 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") || strings.Contains(err.Error(), "no such host") {
|
||||
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
|
||||
}
|
218
object/user_avatar_favicon.go
Normal file
218
object/user_avatar_favicon.go
Normal file
@@ -0,0 +1,218 @@
|
||||
// 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"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"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 {
|
||||
link := link
|
||||
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 = util.UrlJoin(htmlUrl, faviconUrl)
|
||||
}
|
||||
}
|
||||
|
||||
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 (
|
||||
"fmt"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/casdoor/casdoor/proxy"
|
||||
@@ -58,7 +57,11 @@ func TestUpdateAvatars(t *testing.T) {
|
||||
}
|
||||
|
||||
for _, user := range users {
|
||||
if strings.HasPrefix(user.Avatar, "http") {
|
||||
//if strings.HasPrefix(user.Avatar, "http") {
|
||||
// continue
|
||||
//}
|
||||
|
||||
if user.AvatarType != "Auto" {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -69,7 +72,7 @@ func TestUpdateAvatars(t *testing.T) {
|
||||
|
||||
if updated {
|
||||
user.PermanentAvatar = "*"
|
||||
_, err = UpdateUser(user.GetId(), user, []string{"avatar"}, true)
|
||||
_, err = UpdateUser(user.GetId(), user, []string{"avatar", "avatar_type"}, true)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
@@ -1,157 +0,0 @@
|
||||
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
|
||||
}
|
@@ -83,6 +83,7 @@ func UploadUsers(owner string, fileId string) (bool, error) {
|
||||
|
||||
newUsers := []*User{}
|
||||
for index, line := range table {
|
||||
line := line
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
@@ -288,9 +288,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
oldUserTwoFactorAuthJson, _ := json.Marshal(oldUser.MultiFactorAuths)
|
||||
newUserTwoFactorAuthJson, _ := json.Marshal(newUser.MultiFactorAuths)
|
||||
if string(oldUserTwoFactorAuthJson) != string(newUserTwoFactorAuthJson) {
|
||||
if oldUser.PreferredMfaType != newUser.PreferredMfaType {
|
||||
item := GetAccountItemByName("Multi-factor authentication", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
@@ -15,6 +15,7 @@
|
||||
package proxy
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
@@ -71,7 +72,7 @@ func getProxyHttpClient() *http.Client {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
tr := &http.Transport{Dial: dialer.Dial}
|
||||
tr := &http.Transport{Dial: dialer.Dial, TLSClientConfig: &tls.Config{InsecureSkipVerify: true}}
|
||||
return &http.Client{
|
||||
Transport: tr,
|
||||
}
|
||||
|
@@ -26,10 +26,10 @@ import (
|
||||
)
|
||||
|
||||
type Object struct {
|
||||
Owner string `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
SecretKey string `json:"secretKey"`
|
||||
Owner string `json:"owner"`
|
||||
Name string `json:"name"`
|
||||
AccessKey string `json:"accessKey"`
|
||||
AccessSecret string `json:"accessSecret"`
|
||||
}
|
||||
|
||||
func getUsername(ctx *context.Context) (username string) {
|
||||
@@ -107,14 +107,14 @@ func getKeys(ctx *context.Context) (string, string) {
|
||||
method := ctx.Request.Method
|
||||
|
||||
if method == http.MethodGet {
|
||||
accessKey := ctx.Input.Query("accesskey")
|
||||
secretKey := ctx.Input.Query("secretkey")
|
||||
return accessKey, secretKey
|
||||
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("secretkey")
|
||||
return ctx.Request.Form.Get("accessKey"), ctx.Request.Form.Get("accessSecret")
|
||||
}
|
||||
|
||||
var obj Object
|
||||
@@ -123,7 +123,7 @@ func getKeys(ctx *context.Context) (string, string) {
|
||||
return "", ""
|
||||
}
|
||||
|
||||
return obj.AccessKey, obj.SecretKey
|
||||
return obj.AccessKey, obj.AccessSecret
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -85,12 +85,13 @@ func getUsernameByClientIdSecret(ctx *context.Context) string {
|
||||
}
|
||||
|
||||
func getUsernameByKeys(ctx *context.Context) string {
|
||||
accessKey, secretKey := getKeys(ctx)
|
||||
accessKey, accessSecret := getKeys(ctx)
|
||||
user, err := object.GetUserByAccessKey(accessKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
if user != nil && secretKey == user.SecretKey {
|
||||
|
||||
if user != nil && accessSecret == user.AccessSecret {
|
||||
return user.GetId()
|
||||
}
|
||||
return ""
|
||||
|
@@ -19,6 +19,7 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -72,7 +73,7 @@ func StaticFilter(ctx *context.Context) {
|
||||
}
|
||||
|
||||
func serveFileWithReplace(w http.ResponseWriter, r *http.Request, name string, old string, new string) {
|
||||
f, err := os.Open(name)
|
||||
f, err := os.Open(filepath.Clean(name))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -70,7 +70,7 @@ func (fileSystem FileSystem) Put(path string, reader io.Reader) (*oss.Object, er
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dst, err := os.Create(fullPath)
|
||||
dst, err := os.Create(filepath.Clean(fullPath))
|
||||
|
||||
if err == nil {
|
||||
if seeker, ok := reader.(io.ReadSeeker); ok {
|
||||
|
@@ -29,7 +29,7 @@ func NewMinIOS3StorageProvider(clientId string, clientSecret string, region stri
|
||||
Endpoint: endpoint,
|
||||
S3Endpoint: endpoint,
|
||||
ACL: awss3.BucketCannedACLPublicRead,
|
||||
S3ForcePathStyle: false,
|
||||
S3ForcePathStyle: true,
|
||||
})
|
||||
|
||||
return sp
|
||||
|
@@ -26,6 +26,18 @@ func DeleteVal(values []string, val string) []string {
|
||||
return newValues
|
||||
}
|
||||
|
||||
func ReplaceVal(values []string, oldVal string, newVal string) []string {
|
||||
newValues := []string{}
|
||||
for _, v := range values {
|
||||
if v == oldVal {
|
||||
newValues = append(newValues, newVal)
|
||||
} else {
|
||||
newValues = append(newValues, v)
|
||||
}
|
||||
}
|
||||
return newValues
|
||||
}
|
||||
|
||||
func ContainsString(values []string, val string) bool {
|
||||
sort.Strings(values)
|
||||
return sort.SearchStrings(values, val) != len(values)
|
||||
|
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -201,7 +202,7 @@ func GetMinLenStr(strs ...string) string {
|
||||
}
|
||||
|
||||
func ReadStringFromPath(path string) string {
|
||||
data, err := os.ReadFile(path)
|
||||
data, err := os.ReadFile(filepath.Clean(path))
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
@@ -278,3 +279,13 @@ func GetEndPoint(endpoint string) string {
|
||||
}
|
||||
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
|
||||
}
|
||||
|
@@ -18,6 +18,7 @@ import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -155,7 +156,7 @@ func GetVersionInfoFromFile() (*VersionInfo, error) {
|
||||
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
rootPath := path.Dir(path.Dir(filename))
|
||||
file, err := os.Open(path.Join(rootPath, "version_info.txt"))
|
||||
file, err := os.Open(filepath.Clean(path.Join(rootPath, "version_info.txt")))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@@ -24,8 +24,6 @@
|
||||
"i18next": "^19.8.9",
|
||||
"libphonenumber-js": "^1.10.19",
|
||||
"moment": "^2.29.1",
|
||||
"qrcode.react": "^3.1.0",
|
||||
"qs": "^6.10.2",
|
||||
"react": "^18.2.0",
|
||||
"react-app-polyfill": "^3.0.0",
|
||||
"react-codemirror2": "^7.2.1",
|
||||
|
@@ -32,7 +32,7 @@ class AdapterEditPage extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
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,
|
||||
adapter: null,
|
||||
organizations: [],
|
||||
@@ -47,7 +47,7 @@ class AdapterEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getAdapter() {
|
||||
AdapterBackend.getAdapter("admin", this.state.adapterName)
|
||||
AdapterBackend.getAdapter(this.state.organizationName, this.state.adapterName)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
if (res.data === null) {
|
||||
@@ -59,13 +59,13 @@ class AdapterEditPage extends React.Component {
|
||||
adapter: res.data,
|
||||
});
|
||||
|
||||
this.getModels(this.state.owner);
|
||||
this.getModels(this.state.organizationName);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
getOrganizations() {
|
||||
OrganizationBackend.getOrganizations(this.state.organizationName)
|
||||
OrganizationBackend.getOrganizations("admin")
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
organizations: (res.msg === undefined) ? res : [],
|
||||
@@ -76,6 +76,10 @@ class AdapterEditPage extends React.Component {
|
||||
getModels(organizationName) {
|
||||
ModelBackend.getModels(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
models: res,
|
||||
});
|
||||
@@ -114,9 +118,8 @@ class AdapterEditPage extends React.Component {
|
||||
{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.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.updateAdapterField("organization", value);
|
||||
this.updateAdapterField("owner", value);
|
||||
})}>
|
||||
{
|
||||
@@ -253,7 +256,7 @@ class AdapterEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("adapter:Policies"), i18next.t("adapter:Policies - Tooltip"))} :
|
||||
</Col>
|
||||
<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>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
@@ -272,7 +275,7 @@ class AdapterEditPage extends React.Component {
|
||||
|
||||
submitAdapterEdit(willExist) {
|
||||
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) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully saved"));
|
||||
@@ -283,7 +286,7 @@ class AdapterEditPage extends React.Component {
|
||||
if (willExist) {
|
||||
this.props.history.push("/adapters");
|
||||
} 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 {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
|
||||
|
@@ -26,10 +26,9 @@ class AdapterListPage extends BaseListPage {
|
||||
newAdapter() {
|
||||
const randomName = Setting.getRandomName();
|
||||
return {
|
||||
owner: "admin",
|
||||
owner: this.props.account.owner,
|
||||
name: `adapter_${randomName}`,
|
||||
createdTime: moment().format(),
|
||||
organization: this.props.account.owner,
|
||||
type: "Database",
|
||||
host: "localhost",
|
||||
port: 3306,
|
||||
@@ -47,7 +46,7 @@ class AdapterListPage extends BaseListPage {
|
||||
AdapterBackend.addAdapter(newAdapter)
|
||||
.then((res) => {
|
||||
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"));
|
||||
} else {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to add")}: ${res.msg}`);
|
||||
@@ -96,11 +95,11 @@ class AdapterListPage extends BaseListPage {
|
||||
},
|
||||
{
|
||||
title: i18next.t("general:Organization"),
|
||||
dataIndex: "organization",
|
||||
key: "organization",
|
||||
dataIndex: "owner",
|
||||
key: "owner",
|
||||
width: "120px",
|
||||
sorter: true,
|
||||
...this.getColumnSearchProps("organization"),
|
||||
...this.getColumnSearchProps("owner"),
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<Link to={`/organizations/${text}`}>
|
||||
@@ -247,7 +246,7 @@ class AdapterListPage extends BaseListPage {
|
||||
value = params.type;
|
||||
}
|
||||
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) => {
|
||||
this.setState({
|
||||
loading: false,
|
||||
|
@@ -343,9 +343,11 @@ class App extends Component {
|
||||
items.push(Setting.getItem(<><SettingOutlined /> {i18next.t("account:My Account")}</>,
|
||||
"/account"
|
||||
));
|
||||
items.push(Setting.getItem(<><CommentOutlined /> {i18next.t("account:Chats & Messages")}</>,
|
||||
"/chat"
|
||||
));
|
||||
if (Conf.EnableChatPages) {
|
||||
items.push(Setting.getItem(<><CommentOutlined /> {i18next.t("account:Chats & Messages")}</>,
|
||||
"/chat"
|
||||
));
|
||||
}
|
||||
items.push(Setting.getItem(<><LogoutOutlined /> {i18next.t("account:Logout")}</>,
|
||||
"/logout"));
|
||||
|
||||
@@ -411,6 +413,14 @@ class App extends Component {
|
||||
res.push(Setting.getItem(<Link to="/">{i18next.t("general:Home")}</Link>, "/"));
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
if (Conf.ShowGithubCorner) {
|
||||
res.push(Setting.getItem(<a href={"https://casdoor.com"}>
|
||||
<span style={{fontWeight: "bold", backgroundColor: "rgba(87,52,211,0.4)", marginTop: "12px", paddingLeft: "5px", paddingRight: "5px", display: "flex", alignItems: "center", height: "40px", borderRadius: "5px"}}>
|
||||
🚀 SaaS Hosting 🔥
|
||||
</span>
|
||||
</a>, "#"));
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/organizations">{i18next.t("general:Organizations")}</Link>,
|
||||
"/organizations"));
|
||||
|
||||
@@ -449,13 +459,15 @@ class App extends Component {
|
||||
"/providers"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/chats">{i18next.t("general:Chats")}</Link>,
|
||||
"/chats"
|
||||
));
|
||||
if (Conf.EnableChatPages) {
|
||||
res.push(Setting.getItem(<Link to="/chats">{i18next.t("general:Chats")}</Link>,
|
||||
"/chats"
|
||||
));
|
||||
|
||||
res.push(Setting.getItem(<Link to="/messages">{i18next.t("general:Messages")}</Link>,
|
||||
"/messages"
|
||||
));
|
||||
res.push(Setting.getItem(<Link to="/messages">{i18next.t("general:Messages")}</Link>,
|
||||
"/messages"
|
||||
));
|
||||
}
|
||||
|
||||
res.push(Setting.getItem(<Link to="/resources">{i18next.t("general:Resources")}</Link>,
|
||||
"/resources"
|
||||
@@ -476,7 +488,6 @@ class App extends Component {
|
||||
res.push(Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>,
|
||||
"/subscriptions"
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
@@ -501,7 +512,6 @@ class App extends Component {
|
||||
));
|
||||
|
||||
if (Conf.EnableExtraPages) {
|
||||
|
||||
res.push(Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>,
|
||||
"/products"
|
||||
));
|
||||
@@ -510,8 +520,8 @@ class App extends Component {
|
||||
"/payments"
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (Setting.isAdminUser(this.state.account)) {
|
||||
res.push(Setting.getItem(<Link to="/sysinfo">{i18next.t("general:System Info")}</Link>,
|
||||
"/sysinfo"
|
||||
|
@@ -118,20 +118,25 @@ class ApplicationEditPage extends React.Component {
|
||||
|
||||
getApplication() {
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((application) => {
|
||||
if (application === null) {
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
|
||||
application.grantTypes = ["authorization_code"];
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.grantTypes === null || res.grantTypes === undefined || res.grantTypes.length === 0) {
|
||||
res.grantTypes = ["authorization_code"];
|
||||
}
|
||||
this.setState({
|
||||
application: application,
|
||||
application: res,
|
||||
});
|
||||
|
||||
this.getCerts(application.organization);
|
||||
this.getCerts(res.organization);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -44,14 +44,19 @@ class CertEditPage extends React.Component {
|
||||
|
||||
getCert() {
|
||||
CertBackend.getCert(this.state.owner, this.state.certName)
|
||||
.then((cert) => {
|
||||
if (cert === null) {
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
cert: cert,
|
||||
cert: res,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -40,17 +40,21 @@ class ChatEditPage extends React.Component {
|
||||
|
||||
getChat() {
|
||||
ChatBackend.getChat("admin", this.state.chatName)
|
||||
.then((chat) => {
|
||||
if (chat === null) {
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
chat: chat,
|
||||
chat: res,
|
||||
});
|
||||
|
||||
this.getUsers(chat.organization);
|
||||
this.getUsers(res.organization);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -66,6 +70,11 @@ class ChatEditPage extends React.Component {
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
users: res,
|
||||
});
|
||||
|
@@ -21,6 +21,8 @@ export const DefaultLanguage = "en";
|
||||
|
||||
export const EnableExtraPages = true;
|
||||
|
||||
export const EnableChatPages = true;
|
||||
|
||||
export const InitThemeAlgorithm = true;
|
||||
export const ThemeDefault = {
|
||||
themeType: "default",
|
||||
|
@@ -74,8 +74,12 @@ class EntryPage extends React.Component {
|
||||
});
|
||||
|
||||
ApplicationBackend.getApplication("admin", pricing.application)
|
||||
.then((application) => {
|
||||
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
const themeData = res !== null ? Setting.getThemeData(res.organizationObj, res) : Conf.ThemeDefault;
|
||||
this.props.updataThemeData(themeData);
|
||||
});
|
||||
};
|
||||
|
@@ -44,11 +44,6 @@ class GroupEditPage extends React.Component {
|
||||
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,
|
||||
});
|
||||
@@ -96,12 +91,12 @@ class GroupEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getParentIdOptions() {
|
||||
const groups = this.state.groups.filter((group) => group.id !== this.state.group.id);
|
||||
const groups = this.state.groups.filter((group) => group.name !== this.state.group.name);
|
||||
const organization = this.state.organizations.find((organization) => organization.name === this.state.group.owner);
|
||||
if (organization !== undefined) {
|
||||
groups.push({id: organization.name, displayName: organization.displayName});
|
||||
groups.push({name: organization.name, displayName: organization.displayName});
|
||||
}
|
||||
return groups.map((group) => ({label: group.displayName, value: group.id}));
|
||||
return groups.map((group) => ({label: group.displayName, value: group.name}));
|
||||
}
|
||||
|
||||
renderGroup() {
|
||||
|
@@ -185,7 +185,7 @@ class GroupListPage extends BaseListPage {
|
||||
{record.parentId}
|
||||
</Link>;
|
||||
}
|
||||
const parentGroup = this.state.groups.find((group) => group.id === text);
|
||||
const parentGroup = this.state.groups.find((group) => group.name === text);
|
||||
if (parentGroup === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
@@ -30,7 +30,6 @@ class GroupTreePage extends React.Component {
|
||||
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],
|
||||
};
|
||||
@@ -55,7 +54,6 @@ class GroupTreePage extends React.Component {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
treeData: res.data,
|
||||
groupId: this.findNodeId({children: res.data}, this.state.groupName),
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", res.msg);
|
||||
@@ -63,26 +61,10 @@ class GroupTreePage extends React.Component {
|
||||
});
|
||||
}
|
||||
|
||||
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 />}
|
||||
@@ -201,7 +183,6 @@ class GroupTreePage extends React.Component {
|
||||
this.setState({
|
||||
selectedKeys: selectedKeys,
|
||||
groupName: info.node.key,
|
||||
groupId: info.node.id,
|
||||
});
|
||||
this.props.history.push(`/trees/${this.state.organizationName}/${info.node.key}`);
|
||||
};
|
||||
@@ -257,7 +238,7 @@ class GroupTreePage extends React.Component {
|
||||
updatedTime: moment().format(),
|
||||
displayName: `New Group - ${randomName}`,
|
||||
type: "Virtual",
|
||||
parentId: isRoot ? this.state.organizationName : this.state.groupId,
|
||||
parentId: isRoot ? this.state.organizationName : this.state.groupName,
|
||||
isTopGroup: isRoot,
|
||||
isEnabled: true,
|
||||
};
|
||||
@@ -300,8 +281,7 @@ class GroupTreePage extends React.Component {
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
selectedKeys: [],
|
||||
groupName: null,
|
||||
groupId: undefined,
|
||||
groupName: undefined,
|
||||
});
|
||||
this.props.history.push(`/trees/${this.state.organizationName}`);
|
||||
}}
|
||||
@@ -323,7 +303,6 @@ class GroupTreePage extends React.Component {
|
||||
<UserListPage
|
||||
organizationName={this.state.organizationName}
|
||||
groupName={this.state.groupName}
|
||||
groupId={this.state.groupId}
|
||||
{...this.props}
|
||||
/>
|
||||
</Col>
|
||||
|
@@ -45,17 +45,20 @@ class MessageEditPage extends React.Component {
|
||||
|
||||
getMessage() {
|
||||
MessageBackend.getMessage("admin", this.state.messageName)
|
||||
.then((message) => {
|
||||
if (message === null) {
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
message: message,
|
||||
message: res,
|
||||
});
|
||||
|
||||
this.getUsers(message.organization);
|
||||
this.getUsers(res.organization);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,6 +83,10 @@ class MessageEditPage extends React.Component {
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
users: res,
|
||||
});
|
||||
|
@@ -47,14 +47,19 @@ class ModelEditPage extends React.Component {
|
||||
|
||||
getModel() {
|
||||
ModelBackend.getModel(this.state.organizationName, this.state.modelName)
|
||||
.then((model) => {
|
||||
if (model === null) {
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
model: model,
|
||||
model: res,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user