mirror of
https://github.com/casdoor/casdoor.git
synced 2025-08-26 00:50:33 +08:00
Compare commits
1 Commits
v1.350.0
...
revert-181
Author | SHA1 | Date | |
---|---|---|---|
![]() |
138ca9d0c9 |
@@ -110,7 +110,7 @@ func GetLanguage(language string) string {
|
||||
return "en"
|
||||
}
|
||||
|
||||
if len(language) != 2 || language == "nu" {
|
||||
if len(language) != 2 {
|
||||
return "en"
|
||||
} else {
|
||||
return language
|
||||
|
@@ -370,7 +370,6 @@ 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,8 +50,7 @@ func (c *ApiController) GetApplications() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplications(applications, userId)
|
||||
@@ -60,15 +59,13 @@ func (c *ApiController) GetApplications() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetApplicationCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
app, err := object.GetPaginationApplications(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
applications := object.GetMaskedApplications(app, userId)
|
||||
@@ -88,8 +85,7 @@ func (c *ApiController) GetApplication() {
|
||||
id := c.Input().Get("id")
|
||||
app, err := object.GetApplication(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplication(app, userId)
|
||||
@@ -108,8 +104,7 @@ func (c *ApiController) GetUserApplication() {
|
||||
id := c.Input().Get("id")
|
||||
user, err := object.GetUser(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
@@ -119,8 +114,7 @@ func (c *ApiController) GetUserApplication() {
|
||||
|
||||
app, err := object.GetApplicationByUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedApplication(app, userId)
|
||||
@@ -153,8 +147,7 @@ func (c *ApiController) GetOrganizationApplications() {
|
||||
if limit == "" || page == "" {
|
||||
applications, err := object.GetOrganizationApplications(owner, organization)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
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.GetPreferredMfaProps(true)}
|
||||
resp = &Response{Status: object.NextMfa, Data: user.GetPreferMfa(true)}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -656,20 +656,15 @@ func (c *ApiController) Login() {
|
||||
}
|
||||
|
||||
if 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)
|
||||
MfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferMfa(false))
|
||||
err = MfaUtil.Verify(authForm.Passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
}
|
||||
if authForm.RecoveryCode != "" {
|
||||
err = object.MfaRecover(user, authForm.RecoveryCode)
|
||||
err = object.RecoverTfs(user, authForm.RecoveryCode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -756,8 +751,7 @@ func (c *ApiController) HandleSamlLogin() {
|
||||
func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
respBytes, err := ioutil.ReadAll(c.Ctx.Request.Body)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var data struct {
|
||||
@@ -767,8 +761,7 @@ func (c *ApiController) HandleOfficialAccountEvent() {
|
||||
}
|
||||
err = xml.Unmarshal(respBytes, &data)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
lock.Lock()
|
||||
|
@@ -79,8 +79,7 @@ func (c *ApiController) getCurrentUser() *object.User {
|
||||
} else {
|
||||
user, err = object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return nil
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
return user
|
||||
@@ -113,8 +112,7 @@ func (c *ApiController) GetSessionApplication() *object.Application {
|
||||
}
|
||||
application, err := object.GetApplicationByClientId(clientId.(string))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return nil
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return application
|
||||
@@ -179,10 +177,6 @@ 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)
|
||||
}
|
||||
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetCerts() {
|
||||
if limit == "" || page == "" {
|
||||
maskedCerts, err := object.GetMaskedCerts(object.GetCerts(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedCerts
|
||||
@@ -51,15 +50,13 @@ func (c *ApiController) GetCerts() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetCertCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
certs, err := object.GetMaskedCerts(object.GetPaginationCerts(owner, paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.ResponseOk(certs, paginator.Nums())
|
||||
@@ -83,8 +80,7 @@ func (c *ApiController) GetGlobleCerts() {
|
||||
if limit == "" || page == "" {
|
||||
maskedCerts, err := object.GetMaskedCerts(object.GetGlobleCerts())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedCerts
|
||||
@@ -93,15 +89,13 @@ func (c *ApiController) GetGlobleCerts() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetGlobalCertsCount(field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
certs, err := object.GetMaskedCerts(object.GetPaginationGlobalCerts(paginator.Offset(), limit, field, value, sortField, sortOrder))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.ResponseOk(certs, paginator.Nums())
|
||||
@@ -119,8 +113,7 @@ func (c *ApiController) GetCert() {
|
||||
id := c.Input().Get("id")
|
||||
cert, err := object.GetCert(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedCert(cert)
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetChats() {
|
||||
if limit == "" || page == "" {
|
||||
maskedChats, err := object.GetMaskedChats(object.GetChats(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedChats
|
||||
@@ -78,8 +77,7 @@ func (c *ApiController) GetChat() {
|
||||
|
||||
maskedChat, err := object.GetMaskedChat(object.GetChat(id))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedChat
|
||||
|
@@ -44,26 +44,14 @@ func (c *ApiController) Enforce() {
|
||||
}
|
||||
|
||||
if permissionId != "" {
|
||||
permission, err := object.GetPermission(permissionId)
|
||||
enforceResult, err := object.Enforce(permissionId, &request)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res := []bool{}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
c.ResponseOk(res)
|
||||
return
|
||||
}
|
||||
@@ -88,16 +76,8 @@ func (c *ApiController) Enforce() {
|
||||
}
|
||||
|
||||
res := []bool{}
|
||||
|
||||
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...)
|
||||
for _, permission := range permissions {
|
||||
enforceResult, err := object.Enforce(permission.GetId(), &request)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -105,7 +85,6 @@ func (c *ApiController) Enforce() {
|
||||
|
||||
res = append(res, enforceResult)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
}
|
||||
|
||||
@@ -130,32 +109,14 @@ func (c *ApiController) BatchEnforce() {
|
||||
}
|
||||
|
||||
if permissionId != "" {
|
||||
permission, err := object.GetPermission(permissionId)
|
||||
enforceResult, err := object.BatchEnforce(permissionId, &requests)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
res := [][]bool{}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
res = append(res, enforceResult)
|
||||
c.ResponseOk(res)
|
||||
return
|
||||
}
|
||||
@@ -174,16 +135,8 @@ func (c *ApiController) BatchEnforce() {
|
||||
}
|
||||
|
||||
res := [][]bool{}
|
||||
|
||||
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...)
|
||||
for _, permission := range permissions {
|
||||
enforceResult, err := object.BatchEnforce(permission.GetId(), &requests)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -191,7 +144,6 @@ func (c *ApiController) BatchEnforce() {
|
||||
|
||||
res = append(res, enforceResult)
|
||||
}
|
||||
|
||||
c.ResponseOk(res)
|
||||
}
|
||||
|
||||
|
@@ -53,8 +53,7 @@ func (c *ApiController) GetMessages() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedMessages(messages)
|
||||
@@ -90,8 +89,7 @@ func (c *ApiController) GetMessage() {
|
||||
id := c.Input().Get("id")
|
||||
message, err := object.GetMessage(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = object.GetMaskedMessage(message)
|
||||
@@ -102,8 +100,7 @@ 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 {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,8 +196,7 @@ func (c *ApiController) GetMessageAnswer() {
|
||||
event := fmt.Sprintf("event: end\ndata: %s\n\n", "end")
|
||||
_, err = c.Ctx.ResponseWriter.Write([]byte(event))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
answer := stringBuilder.String()
|
||||
@@ -208,8 +204,7 @@ func (c *ApiController) GetMessageAnswer() {
|
||||
message.Text = answer
|
||||
_, err = object.UpdateMessage(message.GetId(), message)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -17,6 +17,7 @@ package controllers
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/beego/beego"
|
||||
"github.com/casdoor/casdoor/object"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
)
|
||||
@@ -33,7 +34,7 @@ import (
|
||||
func (c *ApiController) MfaSetupInitiate() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
userId := util.GetId(owner, name)
|
||||
|
||||
if len(userId) == 0 {
|
||||
@@ -41,11 +42,10 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
return
|
||||
}
|
||||
|
||||
MfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
MfaUtil := object.GetMfaUtil(authType, nil)
|
||||
if MfaUtil == nil {
|
||||
c.ResponseError("Invalid auth type")
|
||||
}
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
@@ -57,7 +57,10 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
return
|
||||
}
|
||||
|
||||
mfaProps, err := MfaUtil.Initiate(c.Ctx, user.GetId())
|
||||
issuer := beego.AppConfig.String("appname")
|
||||
accountName := user.GetId()
|
||||
|
||||
mfaProps, err := MfaUtil.Initiate(c.Ctx, issuer, accountName)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -76,20 +79,16 @@ func (c *ApiController) MfaSetupInitiate() {
|
||||
// @Success 200 {object} Response object
|
||||
// @router /mfa/setup/verify [post]
|
||||
func (c *ApiController) MfaSetupVerify() {
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
passcode := c.Ctx.Request.Form.Get("passcode")
|
||||
|
||||
if mfaType == "" || passcode == "" {
|
||||
if authType == "" || passcode == "" {
|
||||
c.ResponseError("missing auth type or passcode")
|
||||
return
|
||||
}
|
||||
mfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
MfaUtil := object.GetMfaUtil(authType, nil)
|
||||
|
||||
err := mfaUtil.SetupVerify(c.Ctx, passcode)
|
||||
err := MfaUtil.SetupVerify(c.Ctx, passcode)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
} else {
|
||||
@@ -109,7 +108,7 @@ func (c *ApiController) MfaSetupVerify() {
|
||||
func (c *ApiController) MfaSetupEnable() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
authType := c.Ctx.Request.Form.Get("type")
|
||||
|
||||
user, err := object.GetUser(util.GetId(owner, name))
|
||||
if err != nil {
|
||||
@@ -122,13 +121,8 @@ func (c *ApiController) MfaSetupEnable() {
|
||||
return
|
||||
}
|
||||
|
||||
mfaUtil := object.GetMfaUtil(mfaType, nil)
|
||||
if mfaUtil == nil {
|
||||
c.ResponseError("Invalid multi-factor authentication type")
|
||||
return
|
||||
}
|
||||
|
||||
err = mfaUtil.Enable(c.Ctx, user)
|
||||
twoFactor := object.GetMfaUtil(authType, nil)
|
||||
err = twoFactor.Enable(c.Ctx, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -143,9 +137,11 @@ 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)
|
||||
@@ -155,18 +151,28 @@ func (c *ApiController) DeleteMfa() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
err = object.DisabledMultiFactorAuth(user)
|
||||
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())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetAllMfaProps(user, true))
|
||||
c.ResponseOk(user.MultiFactorAuths)
|
||||
}
|
||||
|
||||
// SetPreferredMfa
|
||||
@@ -179,7 +185,7 @@ func (c *ApiController) DeleteMfa() {
|
||||
// @Success 200 {object} Response object
|
||||
// @router /set-preferred-mfa [post]
|
||||
func (c *ApiController) SetPreferredMfa() {
|
||||
mfaType := c.Ctx.Request.Form.Get("mfaType")
|
||||
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)
|
||||
@@ -189,15 +195,29 @@ func (c *ApiController) SetPreferredMfa() {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
c.ResponseError("User doesn't exist")
|
||||
return
|
||||
}
|
||||
|
||||
err = object.SetPreferredMultiFactorAuth(user, mfaType)
|
||||
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())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
}
|
||||
c.ResponseOk(object.GetAllMfaProps(user, true))
|
||||
|
||||
for i, mfaProp := range mfaProps {
|
||||
mfaProps[i] = object.GetMaskedProps(mfaProp)
|
||||
}
|
||||
c.ResponseOk(mfaProps)
|
||||
}
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetModels() {
|
||||
if limit == "" || page == "" {
|
||||
models, err := object.GetModels(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = models
|
||||
@@ -78,8 +77,7 @@ func (c *ApiController) GetModel() {
|
||||
|
||||
model, err := object.GetModel(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = model
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetOrganizations() {
|
||||
if limit == "" || page == "" {
|
||||
maskedOrganizations, err := object.GetMaskedOrganizations(object.GetOrganizations(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedOrganizations
|
||||
|
@@ -42,8 +42,7 @@ func (c *ApiController) GetPayments() {
|
||||
if limit == "" || page == "" {
|
||||
payments, err := object.GetPayments(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = payments
|
||||
@@ -52,15 +51,13 @@ func (c *ApiController) GetPayments() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetPaymentCount(owner, organization, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
payments, err := object.GetPaginationPayments(owner, organization, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.ResponseOk(payments, paginator.Nums())
|
||||
@@ -102,8 +99,7 @@ func (c *ApiController) GetPayment() {
|
||||
|
||||
payment, err := object.GetPayment(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = payment
|
||||
@@ -194,8 +190,7 @@ func (c *ApiController) NotifyPayment() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetPermissions() {
|
||||
if limit == "" || page == "" {
|
||||
permissions, err := object.GetPermissions(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = permissions
|
||||
@@ -51,15 +50,13 @@ func (c *ApiController) GetPermissions() {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetPermissionCount(owner, field, value)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
paginator := pagination.SetPaginator(c.Ctx, limit, count)
|
||||
permissions, err := object.GetPaginationPermissions(owner, paginator.Offset(), limit, field, value, sortField, sortOrder)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.ResponseOk(permissions, paginator.Nums())
|
||||
@@ -119,8 +116,7 @@ func (c *ApiController) GetPermission() {
|
||||
|
||||
permission, err := object.GetPermission(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = permission
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetPlans() {
|
||||
if limit == "" || page == "" {
|
||||
plans, err := object.GetPlans(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = plans
|
||||
@@ -80,15 +79,13 @@ func (c *ApiController) GetPlan() {
|
||||
|
||||
plan, err := object.GetPlan(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if includeOption {
|
||||
options, err := object.GetPermissionsByRole(plan.Role)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetPricings() {
|
||||
if limit == "" || page == "" {
|
||||
pricings, err := object.GetPricings(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = pricings
|
||||
@@ -78,8 +77,7 @@ func (c *ApiController) GetPricing() {
|
||||
|
||||
pricing, err := object.GetPricing(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = pricing
|
||||
|
@@ -42,8 +42,7 @@ func (c *ApiController) GetProducts() {
|
||||
if limit == "" || page == "" {
|
||||
products, err := object.GetProducts(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = products
|
||||
@@ -79,14 +78,12 @@ func (c *ApiController) GetProduct() {
|
||||
|
||||
product, err := object.GetProduct(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
err = object.ExtendProductWithProviders(product)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = product
|
||||
|
@@ -46,8 +46,7 @@ func (c *ApiController) GetProviders() {
|
||||
if limit == "" || page == "" {
|
||||
providers, err := object.GetProviders(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedProviders(providers, isMaskEnabled))
|
||||
@@ -93,8 +92,7 @@ func (c *ApiController) GetGlobalProviders() {
|
||||
if limit == "" || page == "" {
|
||||
globalProviders, err := object.GetGlobalProviders()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.ResponseOk(object.GetMaskedProviders(globalProviders, isMaskEnabled))
|
||||
|
@@ -46,8 +46,7 @@ func (c *ApiController) GetRecords() {
|
||||
if limit == "" || page == "" {
|
||||
records, err := object.GetRecords()
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = records
|
||||
@@ -85,14 +84,12 @@ func (c *ApiController) GetRecordsByFilter() {
|
||||
record := &object.Record{}
|
||||
err := util.JsonToStruct(body, record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
records, err := object.GetRecordsByField(record)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = records
|
||||
|
@@ -53,8 +53,7 @@ func (c *ApiController) GetResources() {
|
||||
if limit == "" || page == "" {
|
||||
resources, err := object.GetResources(owner, user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = resources
|
||||
@@ -87,8 +86,7 @@ func (c *ApiController) GetResource() {
|
||||
|
||||
resource, err := object.GetResource(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = resource
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetRoles() {
|
||||
if limit == "" || page == "" {
|
||||
roles, err := object.GetRoles(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = roles
|
||||
@@ -78,8 +77,7 @@ func (c *ApiController) GetRole() {
|
||||
|
||||
role, err := object.GetRole(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = role
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetSessions() {
|
||||
if limit == "" || page == "" {
|
||||
sessions, err := object.GetSessions(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = sessions
|
||||
@@ -77,8 +76,7 @@ func (c *ApiController) GetSingleSession() {
|
||||
|
||||
session, err := object.GetSingleSession(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = session
|
||||
@@ -157,8 +155,7 @@ func (c *ApiController) IsSessionDuplicated() {
|
||||
|
||||
isUserSessionDuplicated, err := object.IsSessionDuplicated(id, sessionId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = &Response{Status: "ok", Msg: "", Data: isUserSessionDuplicated}
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetSubscriptions() {
|
||||
if limit == "" || page == "" {
|
||||
subscriptions, err := object.GetSubscriptions(owner)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = subscriptions
|
||||
@@ -78,8 +77,7 @@ func (c *ApiController) GetSubscription() {
|
||||
|
||||
subscription, err := object.GetSubscription(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = subscription
|
||||
|
@@ -42,8 +42,7 @@ func (c *ApiController) GetSyncers() {
|
||||
if limit == "" || page == "" {
|
||||
organizationSyncers, err := object.GetOrganizationSyncers(owner, organization)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = organizationSyncers
|
||||
@@ -79,8 +78,7 @@ func (c *ApiController) GetSyncer() {
|
||||
|
||||
syncer, err := object.GetSyncer(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = syncer
|
||||
|
@@ -43,8 +43,7 @@ func (c *ApiController) GetTokens() {
|
||||
if limit == "" || page == "" {
|
||||
token, err := object.GetTokens(owner, organization)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = token
|
||||
@@ -79,8 +78,7 @@ func (c *ApiController) GetToken() {
|
||||
id := c.Input().Get("id")
|
||||
token, err := object.GetToken(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = token
|
||||
@@ -195,8 +193,7 @@ 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 {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = oAuthtoken
|
||||
@@ -239,8 +236,7 @@ func (c *ApiController) RefreshToken() {
|
||||
|
||||
refreshToken2, err := object.RefreshToken(grantType, refreshToken, scope, clientId, clientSecret, host)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = refreshToken2
|
||||
@@ -280,8 +276,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
}
|
||||
application, err := object.GetApplicationByClientId(clientId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if application == nil || application.ClientSecret != clientSecret {
|
||||
@@ -294,8 +289,7 @@ func (c *ApiController) IntrospectToken() {
|
||||
}
|
||||
token, err := object.GetTokenByTokenAndApplication(tokenValue, application.Name)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if token == nil {
|
||||
|
@@ -41,8 +41,7 @@ func (c *ApiController) GetGlobalUsers() {
|
||||
if limit == "" || page == "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGlobalUsers())
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUsers
|
||||
@@ -81,7 +80,7 @@ func (c *ApiController) GetGlobalUsers() {
|
||||
// @router /get-users [get]
|
||||
func (c *ApiController) GetUsers() {
|
||||
owner := c.Input().Get("owner")
|
||||
groupName := c.Input().Get("groupName")
|
||||
groupId := c.Input().Get("groupId")
|
||||
limit := c.Input().Get("pageSize")
|
||||
page := c.Input().Get("p")
|
||||
field := c.Input().Get("field")
|
||||
@@ -90,8 +89,8 @@ func (c *ApiController) GetUsers() {
|
||||
sortOrder := c.Input().Get("sortOrder")
|
||||
|
||||
if limit == "" || page == "" {
|
||||
if groupName != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupName))
|
||||
if groupId != "" {
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetGroupUsers(groupId))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -102,22 +101,21 @@ func (c *ApiController) GetUsers() {
|
||||
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetUsers(owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUsers
|
||||
c.ServeJSON()
|
||||
} else {
|
||||
limit := util.ParseInt(limit)
|
||||
count, err := object.GetUserCount(owner, field, value, groupName)
|
||||
count, err := object.GetUserCount(owner, field, value, groupId)
|
||||
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, groupName)
|
||||
users, err := object.GetPaginationUsers(owner, paginator.Offset(), limit, field, value, sortField, sortOrder, groupId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -155,8 +153,7 @@ func (c *ApiController) GetUser() {
|
||||
if userId != "" && owner != "" {
|
||||
userFromUserId, err = object.GetUserByUserId(owner, userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
id = util.GetId(userFromUserId.Owner, userFromUserId.Name)
|
||||
@@ -168,8 +165,7 @@ func (c *ApiController) GetUser() {
|
||||
|
||||
organization, err := object.GetOrganization(util.GetId("admin", owner))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if !organization.IsProfilePublic {
|
||||
@@ -194,21 +190,17 @@ func (c *ApiController) GetUser() {
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
user.MultiFactorAuths = object.GetAllMfaProps(user, true)
|
||||
err = object.ExtendUserWithRolesAndPermissions(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
maskedUser, err := object.GetMaskedUser(user)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUser
|
||||
@@ -423,7 +415,6 @@ func (c *ApiController) SetPassword() {
|
||||
|
||||
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())
|
||||
@@ -433,7 +424,7 @@ func (c *ApiController) SetPassword() {
|
||||
}
|
||||
} else {
|
||||
if code != c.GetSession("verifiedCode") {
|
||||
c.ResponseError(c.T("general:Missing parameter"))
|
||||
c.ResponseError("")
|
||||
return
|
||||
}
|
||||
c.SetSession("verifiedCode", "")
|
||||
@@ -505,8 +496,7 @@ func (c *ApiController) GetSortedUsers() {
|
||||
|
||||
maskedUsers, err := object.GetMaskedUsers(object.GetSortedUsers(owner, sorter, limit))
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = maskedUsers
|
||||
@@ -566,8 +556,8 @@ func (c *ApiController) AddUserkeys() {
|
||||
func (c *ApiController) RemoveUserFromGroup() {
|
||||
owner := c.Ctx.Request.Form.Get("owner")
|
||||
name := c.Ctx.Request.Form.Get("name")
|
||||
groupName := c.Ctx.Request.Form.Get("groupName")
|
||||
groupId := c.Ctx.Request.Form.Get("groupId")
|
||||
|
||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupName))
|
||||
c.Data["json"] = wrapActionResponse(object.RemoveUserFromGroup(owner, name, groupId))
|
||||
c.ServeJSON()
|
||||
}
|
||||
|
@@ -19,14 +19,13 @@ 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(filepath.Clean(path))
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@@ -55,9 +55,6 @@ 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)
|
||||
}
|
||||
|
||||
@@ -97,8 +94,7 @@ func (c *ApiController) RequireSignedInUser() (*object.User, bool) {
|
||||
|
||||
user, err := object.GetUser(userId)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return nil, false
|
||||
panic(err)
|
||||
}
|
||||
|
||||
if user == nil {
|
||||
|
@@ -93,10 +93,9 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
}
|
||||
|
||||
// mfaSessionData != nil, means method is MfaAuthVerification
|
||||
// mfaSessionData != nil, means method is MfaSetupVerification
|
||||
if mfaSessionData := c.getMfaSessionData(); mfaSessionData != nil {
|
||||
user, err = object.GetUser(mfaSessionData.UserId)
|
||||
c.setMfaSessionData(nil)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
@@ -130,7 +129,7 @@ func (c *ApiController) SendVerificationCode() {
|
||||
} else if vform.Method == ResetVerification {
|
||||
user = c.getCurrentUser()
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferredMfaProps(false)
|
||||
mfaProps := user.GetPreferMfa(false)
|
||||
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
@@ -158,14 +157,12 @@ func (c *ApiController) SendVerificationCode() {
|
||||
}
|
||||
|
||||
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 == ResetVerification {
|
||||
if user = c.getCurrentUser(); user != nil {
|
||||
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
|
||||
}
|
||||
} else if vform.Method == MfaAuthVerification {
|
||||
mfaProps := user.GetPreferredMfaProps(false)
|
||||
mfaProps := user.GetPreferMfa(false)
|
||||
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
|
||||
vform.Dest = mfaProps.Secret
|
||||
}
|
||||
|
@@ -42,8 +42,7 @@ func (c *ApiController) GetWebhooks() {
|
||||
if limit == "" || page == "" {
|
||||
webhooks, err := object.GetWebhooks(owner, organization)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = webhooks
|
||||
@@ -80,8 +79,7 @@ func (c *ApiController) GetWebhook() {
|
||||
|
||||
webhook, err := object.GetWebhook(id)
|
||||
if err != nil {
|
||||
c.ResponseError(err.Error())
|
||||
return
|
||||
panic(err)
|
||||
}
|
||||
|
||||
c.Data["json"] = webhook
|
||||
|
@@ -17,7 +17,6 @@ package deployment
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/object"
|
||||
@@ -46,7 +45,7 @@ func uploadFolder(storageProvider oss.StorageInterface, folder string) {
|
||||
continue
|
||||
}
|
||||
|
||||
file, err := os.Open(filepath.Clean(path + filename))
|
||||
file, err := os.Open(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/pquerna/otp v1.4.0
|
||||
github.com/plutov/paypal/v4 v4.7.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
|
||||
github.com/xorm-io/builder v0.3.13 // indirect
|
||||
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,8 +105,6 @@ 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=
|
||||
@@ -497,10 +495,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=
|
||||
@@ -597,6 +595,7 @@ 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=
|
||||
|
@@ -145,6 +145,11 @@ 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)
|
||||
|
@@ -396,6 +396,11 @@ 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) {
|
||||
|
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 index" json:"name"`
|
||||
Name string `xorm:"varchar(100) notnull pk unique" json:"name"`
|
||||
CreatedTime string `xorm:"varchar(100)" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"varchar(100)" json:"updatedTime"`
|
||||
|
||||
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"`
|
||||
Id string `xorm:"varchar(100) not null index" json:"id"`
|
||||
DisplayName string `xorm:"varchar(100)" json:"displayName"`
|
||||
Manager string `xorm:"varchar(100)" json:"manager"`
|
||||
ContactEmail string `xorm:"varchar(100)" json:"contactEmail"`
|
||||
Type string `xorm:"varchar(100)" json:"type"`
|
||||
ParentId string `xorm:"varchar(100)" json:"parentId"`
|
||||
IsTopGroup bool `xorm:"bool" json:"isTopGroup"`
|
||||
Users *[]string `xorm:"-" json:"users"`
|
||||
|
||||
Title string `json:"title,omitempty"`
|
||||
Key string `json:"key,omitempty"`
|
||||
@@ -95,6 +95,24 @@ 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)
|
||||
@@ -107,13 +125,7 @@ func UpdateGroup(id string, group *Group) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if name != group.Name {
|
||||
err := GroupChangeTrigger(name, group.Name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
}
|
||||
|
||||
group.UpdatedTime = util.GetCurrentTime()
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).AllCols().Update(group)
|
||||
if err != nil {
|
||||
return false, err
|
||||
@@ -123,6 +135,10 @@ 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
|
||||
@@ -148,20 +164,37 @@ func DeleteGroup(group *Group) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if count, err := adapter.Engine.Where("parent_id = ?", group.Name).Count(&Group{}); err != nil {
|
||||
if count, err := adapter.Engine.Where("parent_id = ?", group.Id).Count(&Group{}); err != nil {
|
||||
return false, err
|
||||
} else if count > 0 {
|
||||
return false, errors.New("group has children group")
|
||||
}
|
||||
|
||||
if count, err := GetGroupUserCount(group.Name, "", ""); err != nil {
|
||||
if count, err := GetGroupUserCount(group.GetId(), "", ""); err != nil {
|
||||
return false, err
|
||||
} else if count > 0 {
|
||||
return false, errors.New("group has users")
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
|
||||
session := adapter.Engine.NewSession()
|
||||
defer session.Close()
|
||||
|
||||
if err := session.Begin(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
if _, err := session.Delete(&UserGroupRelation{GroupId: group.Id}); err != nil {
|
||||
session.Rollback()
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err := session.ID(core.PK{group.Owner, group.Name}).Delete(&Group{})
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return false, err
|
||||
}
|
||||
|
||||
if err := session.Commit(); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -182,8 +215,9 @@ func ConvertToTreeData(groups []*Group, parentId string) []*Group {
|
||||
Key: group.Name,
|
||||
Type: group.Type,
|
||||
Owner: group.Owner,
|
||||
Id: group.Id,
|
||||
}
|
||||
children := ConvertToTreeData(groups, group.Name)
|
||||
children := ConvertToTreeData(groups, group.Id)
|
||||
if len(children) > 0 {
|
||||
node.Children = children
|
||||
}
|
||||
@@ -192,113 +226,3 @@ 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
|
||||
}
|
||||
|
145
object/mfa.go
145
object/mfa.go
@@ -22,16 +22,14 @@ import (
|
||||
"github.com/beego/beego/context"
|
||||
)
|
||||
|
||||
const MfaRecoveryCodesSession = "mfa_recovery_codes"
|
||||
|
||||
type MfaSessionData struct {
|
||||
UserId string
|
||||
}
|
||||
|
||||
type MfaProps struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Id string `json:"id"`
|
||||
IsPreferred bool `json:"isPreferred"`
|
||||
MfaType string `json:"mfaType" form:"mfaType"`
|
||||
AuthType string `json:"type" form:"type"`
|
||||
Secret string `json:"secret,omitempty"`
|
||||
CountryCode string `json:"countryCode,omitempty"`
|
||||
URL string `json:"url,omitempty"`
|
||||
@@ -39,16 +37,15 @@ type MfaProps struct {
|
||||
}
|
||||
|
||||
type MfaInterface interface {
|
||||
Initiate(ctx *context.Context, userId string) (*MfaProps, error)
|
||||
SetupVerify(ctx *context.Context, passcode string) error
|
||||
SetupVerify(ctx *context.Context, passCode string) error
|
||||
Verify(passCode string) error
|
||||
Initiate(ctx *context.Context, name1 string, name2 string) (*MfaProps, error)
|
||||
Enable(ctx *context.Context, user *User) error
|
||||
Verify(passcode string) error
|
||||
}
|
||||
|
||||
const (
|
||||
EmailType = "email"
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
SmsType = "sms"
|
||||
TotpType = "app"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -57,30 +54,28 @@ const (
|
||||
RequiredMfa = "RequiredMfa"
|
||||
)
|
||||
|
||||
func GetMfaUtil(mfaType string, config *MfaProps) MfaInterface {
|
||||
switch mfaType {
|
||||
func GetMfaUtil(providerType string, config *MfaProps) MfaInterface {
|
||||
switch providerType {
|
||||
case SmsType:
|
||||
return NewSmsMfaUtil(config)
|
||||
case EmailType:
|
||||
return NewEmailMfaUtil(config)
|
||||
return NewSmsTwoFactor(config)
|
||||
case TotpType:
|
||||
return NewTotpMfaUtil(config)
|
||||
return nil
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func MfaRecover(user *User, recoveryCode string) error {
|
||||
func RecoverTfs(user *User, recoveryCode string) error {
|
||||
hit := false
|
||||
|
||||
if len(user.RecoveryCodes) == 0 {
|
||||
twoFactor := user.GetPreferMfa(false)
|
||||
if len(twoFactor.RecoveryCodes) == 0 {
|
||||
return fmt.Errorf("do not have recovery codes")
|
||||
}
|
||||
|
||||
for _, code := range user.RecoveryCodes {
|
||||
for _, code := range twoFactor.RecoveryCodes {
|
||||
if code == recoveryCode {
|
||||
hit = true
|
||||
user.RecoveryCodes = util.DeleteVal(user.RecoveryCodes, code)
|
||||
break
|
||||
}
|
||||
}
|
||||
@@ -88,106 +83,30 @@ func MfaRecover(user *User, recoveryCode string) error {
|
||||
return fmt.Errorf("recovery code not found")
|
||||
}
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"recovery_codes"}, user.IsAdminUser())
|
||||
affected, err := UpdateUser(user.GetId(), user, []string{"two_factor_auth"}, user.IsAdminUser())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
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
|
||||
if !affected {
|
||||
return fmt.Errorf("")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func SetPreferredMultiFactorAuth(user *User, mfaType string) error {
|
||||
user.PreferredMfaType = mfaType
|
||||
|
||||
_, err := UpdateUser(user.GetId(), user, []string{"preferred_mfa_type"}, user.IsAdminUser())
|
||||
if err != nil {
|
||||
return err
|
||||
func GetMaskedProps(props *MfaProps) *MfaProps {
|
||||
maskedProps := &MfaProps{
|
||||
AuthType: props.AuthType,
|
||||
Id: props.Id,
|
||||
IsPreferred: props.IsPreferred,
|
||||
}
|
||||
return nil
|
||||
|
||||
if props.AuthType == SmsType {
|
||||
if !util.IsEmailValid(props.Secret) {
|
||||
maskedProps.Secret = util.GetMaskedPhone(props.Secret)
|
||||
} else {
|
||||
maskedProps.Secret = util.GetMaskedEmail(props.Secret)
|
||||
}
|
||||
}
|
||||
return maskedProps
|
||||
}
|
||||
|
@@ -18,35 +18,22 @@ import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/casdoor/casdoor/util"
|
||||
|
||||
"github.com/beego/beego/context"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
const (
|
||||
MfaSmsCountryCodeSession = "mfa_country_code"
|
||||
MfaSmsDestSession = "mfa_dest"
|
||||
MfaSmsCountryCodeSession = "mfa_country_code"
|
||||
MfaSmsDestSession = "mfa_dest"
|
||||
MfaSmsRecoveryCodesSession = "mfa_recovery_codes"
|
||||
)
|
||||
|
||||
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)
|
||||
@@ -60,45 +47,6 @@ 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)
|
||||
@@ -109,21 +57,65 @@ func (mfa *SmsMfa) Verify(passCode string) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func NewSmsMfaUtil(config *MfaProps) *SmsMfa {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
MfaType: SmsType,
|
||||
}
|
||||
func (mfa *SmsMfa) Initiate(ctx *context.Context, name string, secret string) (*MfaProps, error) {
|
||||
recoveryCode, err := uuid.NewRandom()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &SmsMfa{
|
||||
Config: config,
|
||||
|
||||
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 NewEmailMfaUtil(config *MfaProps) *SmsMfa {
|
||||
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 {
|
||||
if config == nil {
|
||||
config = &MfaProps{
|
||||
MfaType: EmailType,
|
||||
AuthType: SmsType,
|
||||
}
|
||||
}
|
||||
return &SmsMfa{
|
||||
|
@@ -1,133 +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 (
|
||||
"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,
|
||||
}
|
||||
}
|
@@ -370,24 +370,3 @@ 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, permissionIDs ...string) *casbin.Enforcer {
|
||||
func getEnforcer(permission *Permission) *casbin.Enforcer {
|
||||
tableName := "permission_rule"
|
||||
if len(permission.Adapter) != 0 {
|
||||
adapterObj, err := getCasbinAdapter(permission.Owner, permission.Adapter)
|
||||
@@ -77,13 +77,8 @@ func getEnforcer(permission *Permission, permissionIDs ...string) *casbin.Enforc
|
||||
|
||||
enforcer.SetAdapter(adapter)
|
||||
|
||||
policyFilterV5 := []string{permission.GetId()}
|
||||
if len(permissionIDs) != 0 {
|
||||
policyFilterV5 = permissionIDs
|
||||
}
|
||||
|
||||
policyFilter := xormadapter.Filter{
|
||||
V5: policyFilterV5,
|
||||
V5: []string{permission.GetId()},
|
||||
}
|
||||
|
||||
if !HasRoleDefinition(m) {
|
||||
@@ -246,13 +241,28 @@ func removePolicies(permission *Permission) {
|
||||
|
||||
type CasbinRequest = []interface{}
|
||||
|
||||
func Enforce(permission *Permission, request *CasbinRequest, permissionIds ...string) (bool, error) {
|
||||
enforcer := getEnforcer(permission, permissionIds...)
|
||||
func Enforce(permissionId string, request *CasbinRequest) (bool, error) {
|
||||
permission, err := GetPermission(permissionId)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
enforcer := getEnforcer(permission)
|
||||
return enforcer.Enforce(*request...)
|
||||
}
|
||||
|
||||
func BatchEnforce(permission *Permission, requests *[]CasbinRequest, permissionIds ...string) ([]bool, error) {
|
||||
enforcer := getEnforcer(permission, permissionIds...)
|
||||
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)
|
||||
return enforcer.BatchEnforce(*requests)
|
||||
}
|
||||
|
||||
|
@@ -43,7 +43,6 @@ func UploadPermissions(owner string, fileId string) (bool, error) {
|
||||
|
||||
newPermissions := []*Permission{}
|
||||
for index, line := range table {
|
||||
line := line
|
||||
if index == 0 || parseLineItem(&line, 0) == "" {
|
||||
continue
|
||||
}
|
||||
|
@@ -43,7 +43,6 @@ 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,17 +260,10 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
|
||||
// decompress
|
||||
var buffer bytes.Buffer
|
||||
rdr := flate.NewReader(bytes.NewReader(defated))
|
||||
|
||||
for {
|
||||
_, err := io.CopyN(&buffer, rdr, 1024)
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
return "", "", "", err
|
||||
}
|
||||
_, err = io.Copy(&buffer, rdr)
|
||||
if err != nil {
|
||||
return "", "", "", err
|
||||
}
|
||||
|
||||
var authnRequest saml.AuthnRequest
|
||||
err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
|
||||
if err != nil {
|
||||
|
@@ -50,7 +50,6 @@ 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,11 +63,9 @@ func (syncer *Syncer) syncUsers() {
|
||||
}
|
||||
} else {
|
||||
if user.PreHash == oHash {
|
||||
if !syncer.IsReadOnly {
|
||||
updatedOUser := syncer.createOriginalUserFromUser(user)
|
||||
syncer.updateUser(updatedOUser)
|
||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||
}
|
||||
updatedOUser := syncer.createOriginalUserFromUser(user)
|
||||
syncer.updateUser(updatedOUser)
|
||||
fmt.Printf("Update from user to oUser: %v\n", updatedOUser)
|
||||
|
||||
// update preHash
|
||||
user.PreHash = user.Hash
|
||||
@@ -93,17 +91,15 @@ func (syncer *Syncer) syncUsers() {
|
||||
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)
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -170,7 +170,6 @@ 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(application.Organization, openId, unionId)
|
||||
user, err := getUserByWechatId(openId, unionId)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
@@ -216,9 +216,6 @@ func refineUser(user *User) *User {
|
||||
if user.Permissions == nil {
|
||||
user.Permissions = []*Permission{}
|
||||
}
|
||||
if user.Groups == nil {
|
||||
user.Groups = []string{}
|
||||
}
|
||||
|
||||
return user
|
||||
}
|
||||
|
116
object/user.go
116
object/user.go
@@ -76,6 +76,7 @@ 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"`
|
||||
AccessSecret string `xorm:"varchar(100)" json:"accessSecret"`
|
||||
|
||||
@@ -159,19 +160,13 @@ type User struct {
|
||||
Custom string `xorm:"custom varchar(100)" json:"custom"`
|
||||
|
||||
WebauthnCredentials []webauthn.Credential `xorm:"webauthnCredentials blob" json:"webauthnCredentials"`
|
||||
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"`
|
||||
MultiFactorAuths []*MfaProps `json:"multiFactorAuths"`
|
||||
|
||||
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"`
|
||||
@@ -225,11 +220,11 @@ func GetPaginationGlobalUsers(offset, limit int, field, value, sortField, sortOr
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetUserCount(owner, field, value string, groupName string) (int64, error) {
|
||||
func GetUserCount(owner, field, value string, groupId string) (int64, error) {
|
||||
session := GetSession(owner, -1, -1, field, value, "", "")
|
||||
|
||||
if groupName != "" {
|
||||
return GetGroupUserCount(groupName, field, value)
|
||||
if groupId != "" {
|
||||
return GetGroupUserCount(groupId, field, value)
|
||||
}
|
||||
|
||||
return session.Count(&User{})
|
||||
@@ -269,11 +264,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, groupName string) ([]*User, error) {
|
||||
func GetPaginationUsers(owner string, offset, limit int, field, value, sortField, sortOrder string, groupId string) ([]*User, error) {
|
||||
users := []*User{}
|
||||
|
||||
if groupName != "" {
|
||||
return GetPaginationGroupUsers(groupName, offset, limit, field, value, sortField, sortOrder)
|
||||
if groupId != "" {
|
||||
return GetPaginationGroupUsers(groupId, offset, limit, field, value, sortField, sortOrder)
|
||||
}
|
||||
|
||||
session := GetSessionForUser(owner, offset, limit, field, value, sortField, sortOrder)
|
||||
@@ -320,12 +315,12 @@ func getUserById(owner string, id string) (*User, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func getUserByWechatId(owner string, wechatOpenId string, wechatUnionId string) (*User, error) {
|
||||
func getUserByWechatId(wechatOpenId string, wechatUnionId string) (*User, error) {
|
||||
if wechatUnionId == "" {
|
||||
wechatUnionId = wechatOpenId
|
||||
}
|
||||
user := &User{}
|
||||
existed, err := adapter.Engine.Where("owner = ?", owner).Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
|
||||
existed, err := adapter.Engine.Where("wechat = ? OR wechat = ?", wechatOpenId, wechatUnionId).Get(user)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
@@ -430,22 +425,18 @@ 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.TotpSecret != "" {
|
||||
user.TotpSecret = ""
|
||||
if user.MultiFactorAuths != nil {
|
||||
for i, props := range user.MultiFactorAuths {
|
||||
user.MultiFactorAuths[i] = GetMaskedProps(props)
|
||||
}
|
||||
}
|
||||
if user.RecoveryCodes != nil {
|
||||
user.RecoveryCodes = nil
|
||||
}
|
||||
|
||||
return user, nil
|
||||
}
|
||||
|
||||
@@ -492,13 +483,17 @@ 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, err
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
@@ -526,7 +521,7 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
columns = append(columns, "name", "email", "phone", "country_code")
|
||||
}
|
||||
|
||||
affected, err := updateUser(id, user, columns)
|
||||
affected, err := updateUser(oldUser, user, columns)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
@@ -534,17 +529,32 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
func updateUser(id string, user *User, columns []string) (int64, error) {
|
||||
owner, name := util.GetOwnerAndNameFromIdNoCheck(id)
|
||||
err := user.UpdateUserHash()
|
||||
func updateUser(oldUser, user *User, columns []string) (int64, error) {
|
||||
session := adapter.Engine.NewSession()
|
||||
defer session.Close()
|
||||
|
||||
session.Begin()
|
||||
|
||||
if util.ContainsString(columns, "groups") {
|
||||
affected, err := updateUserGroupRelation(session, user)
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return affected, err
|
||||
}
|
||||
}
|
||||
|
||||
affected, err := session.ID(core.PK{oldUser.Owner, oldUser.Name}).Cols(columns...).Update(user)
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return affected, err
|
||||
}
|
||||
|
||||
err = session.Commit()
|
||||
if err != nil {
|
||||
session.Rollback()
|
||||
return 0, err
|
||||
}
|
||||
|
||||
affected, err := adapter.Engine.ID(core.PK{owner, name}).Cols(columns...).Update(user)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return affected, nil
|
||||
}
|
||||
|
||||
@@ -715,6 +725,11 @@ func DeleteUser(user *User) (bool, error) {
|
||||
return false, err
|
||||
}
|
||||
|
||||
affected, err = deleteRelationByUser(user.Id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
return affected != 0, nil
|
||||
}
|
||||
|
||||
@@ -730,7 +745,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 = user.Groups
|
||||
resp.Groups = []string{user.Owner}
|
||||
}
|
||||
if strings.Contains(scope, "email") {
|
||||
resp.Email = user.Email
|
||||
@@ -766,10 +781,6 @@ func ExtendUserWithRolesAndPermissions(user *User) (err error) {
|
||||
return err
|
||||
}
|
||||
|
||||
if user.Groups == nil {
|
||||
user.Groups = []string{}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -832,14 +843,35 @@ func userChangeTrigger(oldName string, newName string) error {
|
||||
}
|
||||
|
||||
func (user *User) IsMfaEnabled() bool {
|
||||
return user.PreferredMfaType != ""
|
||||
return len(user.MultiFactorAuths) > 0
|
||||
}
|
||||
|
||||
func (user *User) GetPreferredMfaProps(masked bool) *MfaProps {
|
||||
if user.PreferredMfaType == "" {
|
||||
func (user *User) GetPreferMfa(masked bool) *MfaProps {
|
||||
if len(user.MultiFactorAuths) == 0 {
|
||||
return nil
|
||||
}
|
||||
return user.GetMfaProps(user.PreferredMfaType, masked)
|
||||
|
||||
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]
|
||||
}
|
||||
}
|
||||
|
||||
func AddUserkeys(user *User, isAdmin bool) (bool, error) {
|
||||
|
@@ -34,7 +34,7 @@ func downloadImage(client *http.Client, url string) (*bytes.Buffer, string, erro
|
||||
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") {
|
||||
if strings.Contains(err.Error(), "EOF") {
|
||||
return nil, "", nil
|
||||
} else {
|
||||
return nil, "", err
|
||||
|
@@ -18,9 +18,9 @@ import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"golang.org/x/net/html"
|
||||
)
|
||||
|
||||
@@ -124,7 +124,6 @@ func chooseFaviconLinkBySizes(links []Link) *Link {
|
||||
var chosenLink *Link
|
||||
|
||||
for _, link := range links {
|
||||
link := link
|
||||
if chosenLink == nil || compareSizes(link.Sizes, chosenLink.Sizes) > 0 {
|
||||
chosenLink = &link
|
||||
}
|
||||
@@ -207,7 +206,10 @@ func getFaviconFileBuffer(client *http.Client, email string) (*bytes.Buffer, str
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(faviconUrl, "http") {
|
||||
faviconUrl = util.UrlJoin(htmlUrl, faviconUrl)
|
||||
faviconUrl, err = url.JoinPath(htmlUrl, faviconUrl)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
157
object/user_group.go
Normal file
157
object/user_group.go
Normal file
@@ -0,0 +1,157 @@
|
||||
package object
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/casdoor/casdoor/util"
|
||||
"github.com/xorm-io/core"
|
||||
"github.com/xorm-io/xorm"
|
||||
)
|
||||
|
||||
type UserGroupRelation struct {
|
||||
UserId string `xorm:"varchar(100) notnull pk" json:"userId"`
|
||||
GroupId string `xorm:"varchar(100) notnull pk" json:"groupId"`
|
||||
|
||||
CreatedTime string `xorm:"created" json:"createdTime"`
|
||||
UpdatedTime string `xorm:"updated" json:"updatedTime"`
|
||||
}
|
||||
|
||||
func updateUserGroupRelation(session *xorm.Session, user *User) (int64, error) {
|
||||
physicalGroupCount, err := session.Where("type = ?", "Physical").In("id", user.Groups).Count(Group{})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if physicalGroupCount > 1 {
|
||||
return 0, errors.New("user can only be in one physical group")
|
||||
}
|
||||
|
||||
groups := []*Group{}
|
||||
err = session.In("id", user.Groups).Find(&groups)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(groups) != len(user.Groups) {
|
||||
return 0, errors.New("group not found")
|
||||
}
|
||||
|
||||
_, err = session.Delete(&UserGroupRelation{UserId: user.Id})
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
relations := []*UserGroupRelation{}
|
||||
for _, group := range groups {
|
||||
relations = append(relations, &UserGroupRelation{UserId: user.Id, GroupId: group.Id})
|
||||
}
|
||||
if len(relations) == 0 {
|
||||
return 1, nil
|
||||
}
|
||||
_, err = session.Insert(relations)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
return 1, nil
|
||||
}
|
||||
|
||||
func RemoveUserFromGroup(owner, name, groupId string) (bool, error) {
|
||||
user, err := getUser(owner, name)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
groups := []string{}
|
||||
for _, group := range user.Groups {
|
||||
if group != groupId {
|
||||
groups = append(groups, group)
|
||||
}
|
||||
}
|
||||
user.Groups = groups
|
||||
|
||||
_, err = UpdateUser(util.GetId(owner, name), user, []string{"groups"}, false)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
return true, nil
|
||||
}
|
||||
|
||||
func deleteUserGroupRelation(session *xorm.Session, userId, groupId string) (int64, error) {
|
||||
affected, err := session.ID(core.PK{userId, groupId}).Delete(&UserGroupRelation{})
|
||||
return affected, err
|
||||
}
|
||||
|
||||
func deleteRelationByUser(id string) (int64, error) {
|
||||
affected, err := adapter.Engine.Delete(&UserGroupRelation{UserId: id})
|
||||
return affected, err
|
||||
}
|
||||
|
||||
func GetGroupUserCount(id string, field, value string) (int64, error) {
|
||||
group, err := GetGroup(id)
|
||||
if group == nil || err != nil {
|
||||
return 0, err
|
||||
}
|
||||
|
||||
if field == "" && value == "" {
|
||||
return adapter.Engine.Count(UserGroupRelation{GroupId: group.Id})
|
||||
} else {
|
||||
return adapter.Engine.Table("user").
|
||||
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
|
||||
Where("r.group_id = ?", group.Id).
|
||||
And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%").
|
||||
Count()
|
||||
}
|
||||
}
|
||||
|
||||
func GetPaginationGroupUsers(id string, offset, limit int, field, value, sortField, sortOrder string) ([]*User, error) {
|
||||
group, err := GetGroup(id)
|
||||
if group == nil || err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
users := []*User{}
|
||||
session := adapter.Engine.Table("user").
|
||||
Join("INNER", []string{"user_group_relation", "r"}, "user.id = r.user_id").
|
||||
Where("r.group_id = ?", group.Id)
|
||||
|
||||
if offset != -1 && limit != -1 {
|
||||
session.Limit(limit, offset)
|
||||
}
|
||||
|
||||
if field != "" && value != "" {
|
||||
session = session.And(fmt.Sprintf("user.%s LIKE ?", util.CamelToSnakeCase(field)), "%"+value+"%")
|
||||
}
|
||||
|
||||
if sortField == "" || sortOrder == "" {
|
||||
sortField = "created_time"
|
||||
}
|
||||
if sortOrder == "ascend" {
|
||||
session = session.Asc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||
} else {
|
||||
session = session.Desc(fmt.Sprintf("user.%s", util.SnakeString(sortField)))
|
||||
}
|
||||
|
||||
err = session.Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
||||
|
||||
func GetGroupUsers(id string) ([]*User, error) {
|
||||
group, err := GetGroup(id)
|
||||
if group == nil || err != nil {
|
||||
return []*User{}, err
|
||||
}
|
||||
|
||||
users := []*User{}
|
||||
err = adapter.Engine.Table("user_group_relation").Join("INNER", []string{"user", "u"}, "user_group_relation.user_id = u.id").
|
||||
Where("user_group_relation.group_id = ?", group.Id).
|
||||
Find(&users)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return users, nil
|
||||
}
|
@@ -83,7 +83,6 @@ 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,7 +288,9 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
||||
if oldUser.PreferredMfaType != newUser.PreferredMfaType {
|
||||
oldUserTwoFactorAuthJson, _ := json.Marshal(oldUser.MultiFactorAuths)
|
||||
newUserTwoFactorAuthJson, _ := json.Marshal(newUser.MultiFactorAuths)
|
||||
if string(oldUserTwoFactorAuthJson) != string(newUserTwoFactorAuthJson) {
|
||||
item := GetAccountItemByName("Multi-factor authentication", organization)
|
||||
itemsChanged = append(itemsChanged, item)
|
||||
}
|
||||
|
@@ -19,7 +19,6 @@ import (
|
||||
"io"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
@@ -73,7 +72,7 @@ func StaticFilter(ctx *context.Context) {
|
||||
}
|
||||
|
||||
func serveFileWithReplace(w http.ResponseWriter, r *http.Request, name string, old string, new string) {
|
||||
f, err := os.Open(filepath.Clean(name))
|
||||
f, err := os.Open(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(filepath.Clean(fullPath))
|
||||
dst, err := os.Create(fullPath)
|
||||
|
||||
if err == nil {
|
||||
if seeker, ok := reader.(io.ReadSeeker); ok {
|
||||
|
@@ -26,18 +26,6 @@ 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,7 +22,6 @@ import (
|
||||
"fmt"
|
||||
"math/rand"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@@ -202,7 +201,7 @@ func GetMinLenStr(strs ...string) string {
|
||||
}
|
||||
|
||||
func ReadStringFromPath(path string) string {
|
||||
data, err := os.ReadFile(filepath.Clean(path))
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
@@ -18,7 +18,6 @@ import (
|
||||
"bufio"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@@ -156,7 +155,7 @@ func GetVersionInfoFromFile() (*VersionInfo, error) {
|
||||
|
||||
_, filename, _, _ := runtime.Caller(0)
|
||||
rootPath := path.Dir(path.Dir(filename))
|
||||
file, err := os.Open(filepath.Clean(path.Join(rootPath, "version_info.txt")))
|
||||
file, err := os.Open(path.Join(rootPath, "version_info.txt"))
|
||||
if err != nil {
|
||||
return res, err
|
||||
}
|
||||
|
@@ -24,6 +24,8 @@
|
||||
"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",
|
||||
|
@@ -76,10 +76,6 @@ 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,
|
||||
});
|
||||
|
@@ -343,11 +343,9 @@ class App extends Component {
|
||||
items.push(Setting.getItem(<><SettingOutlined /> {i18next.t("account:My Account")}</>,
|
||||
"/account"
|
||||
));
|
||||
if (Conf.EnableChatPages) {
|
||||
items.push(Setting.getItem(<><CommentOutlined /> {i18next.t("account:Chats & Messages")}</>,
|
||||
"/chat"
|
||||
));
|
||||
}
|
||||
items.push(Setting.getItem(<><CommentOutlined /> {i18next.t("account:Chats & Messages")}</>,
|
||||
"/chat"
|
||||
));
|
||||
items.push(Setting.getItem(<><LogoutOutlined /> {i18next.t("account:Logout")}</>,
|
||||
"/logout"));
|
||||
|
||||
@@ -413,14 +411,6 @@ 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"));
|
||||
|
||||
@@ -459,15 +449,13 @@ class App extends Component {
|
||||
"/providers"
|
||||
));
|
||||
|
||||
if (Conf.EnableChatPages) {
|
||||
res.push(Setting.getItem(<Link to="/chats">{i18next.t("general:Chats")}</Link>,
|
||||
"/chats"
|
||||
));
|
||||
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"
|
||||
@@ -488,6 +476,7 @@ class App extends Component {
|
||||
res.push(Setting.getItem(<Link to="/subscriptions">{i18next.t("general:Subscriptions")}</Link>,
|
||||
"/subscriptions"
|
||||
));
|
||||
|
||||
}
|
||||
|
||||
if (Setting.isLocalAdminUser(this.state.account)) {
|
||||
@@ -512,6 +501,7 @@ class App extends Component {
|
||||
));
|
||||
|
||||
if (Conf.EnableExtraPages) {
|
||||
|
||||
res.push(Setting.getItem(<Link to="/products">{i18next.t("general:Products")}</Link>,
|
||||
"/products"
|
||||
));
|
||||
@@ -520,8 +510,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,25 +118,20 @@ class ApplicationEditPage extends React.Component {
|
||||
|
||||
getApplication() {
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((application) => {
|
||||
if (application === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
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"];
|
||||
if (application.grantTypes === null || application.grantTypes === undefined || application.grantTypes.length === 0) {
|
||||
application.grantTypes = ["authorization_code"];
|
||||
}
|
||||
this.setState({
|
||||
application: res,
|
||||
application: application,
|
||||
});
|
||||
|
||||
this.getCerts(res.organization);
|
||||
this.getCerts(application.organization);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -44,19 +44,14 @@ class CertEditPage extends React.Component {
|
||||
|
||||
getCert() {
|
||||
CertBackend.getCert(this.state.owner, this.state.certName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((cert) => {
|
||||
if (cert === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
cert: res,
|
||||
cert: cert,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -40,21 +40,17 @@ class ChatEditPage extends React.Component {
|
||||
|
||||
getChat() {
|
||||
ChatBackend.getChat("admin", this.state.chatName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((chat) => {
|
||||
if (chat === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
chat: res,
|
||||
chat: chat,
|
||||
});
|
||||
|
||||
this.getUsers(res.organization);
|
||||
this.getUsers(chat.organization);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -70,11 +66,6 @@ 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,8 +21,6 @@ export const DefaultLanguage = "en";
|
||||
|
||||
export const EnableExtraPages = true;
|
||||
|
||||
export const EnableChatPages = true;
|
||||
|
||||
export const InitThemeAlgorithm = true;
|
||||
export const ThemeDefault = {
|
||||
themeType: "default",
|
||||
|
@@ -74,12 +74,8 @@ class EntryPage extends React.Component {
|
||||
});
|
||||
|
||||
ApplicationBackend.getApplication("admin", pricing.application)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
const themeData = res !== null ? Setting.getThemeData(res.organizationObj, res) : Conf.ThemeDefault;
|
||||
.then((application) => {
|
||||
const themeData = application !== null ? Setting.getThemeData(application.organizationObj, application) : Conf.ThemeDefault;
|
||||
this.props.updataThemeData(themeData);
|
||||
});
|
||||
};
|
||||
|
@@ -44,6 +44,11 @@ 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,
|
||||
});
|
||||
@@ -91,12 +96,12 @@ class GroupEditPage extends React.Component {
|
||||
}
|
||||
|
||||
getParentIdOptions() {
|
||||
const groups = this.state.groups.filter((group) => group.name !== this.state.group.name);
|
||||
const groups = this.state.groups.filter((group) => group.id !== this.state.group.id);
|
||||
const organization = this.state.organizations.find((organization) => organization.name === this.state.group.owner);
|
||||
if (organization !== undefined) {
|
||||
groups.push({name: organization.name, displayName: organization.displayName});
|
||||
groups.push({id: organization.name, displayName: organization.displayName});
|
||||
}
|
||||
return groups.map((group) => ({label: group.displayName, value: group.name}));
|
||||
return groups.map((group) => ({label: group.displayName, value: group.id}));
|
||||
}
|
||||
|
||||
renderGroup() {
|
||||
|
@@ -185,7 +185,7 @@ class GroupListPage extends BaseListPage {
|
||||
{record.parentId}
|
||||
</Link>;
|
||||
}
|
||||
const parentGroup = this.state.groups.find((group) => group.name === text);
|
||||
const parentGroup = this.state.groups.find((group) => group.id === text);
|
||||
if (parentGroup === undefined) {
|
||||
return "";
|
||||
}
|
||||
|
@@ -30,6 +30,7 @@ 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],
|
||||
};
|
||||
@@ -54,6 +55,7 @@ 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);
|
||||
@@ -61,10 +63,26 @@ 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 />}
|
||||
@@ -183,6 +201,7 @@ 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}`);
|
||||
};
|
||||
@@ -238,7 +257,7 @@ class GroupTreePage extends React.Component {
|
||||
updatedTime: moment().format(),
|
||||
displayName: `New Group - ${randomName}`,
|
||||
type: "Virtual",
|
||||
parentId: isRoot ? this.state.organizationName : this.state.groupName,
|
||||
parentId: isRoot ? this.state.organizationName : this.state.groupId,
|
||||
isTopGroup: isRoot,
|
||||
isEnabled: true,
|
||||
};
|
||||
@@ -281,7 +300,8 @@ class GroupTreePage extends React.Component {
|
||||
onClick={() => {
|
||||
this.setState({
|
||||
selectedKeys: [],
|
||||
groupName: undefined,
|
||||
groupName: null,
|
||||
groupId: undefined,
|
||||
});
|
||||
this.props.history.push(`/trees/${this.state.organizationName}`);
|
||||
}}
|
||||
@@ -303,6 +323,7 @@ class GroupTreePage extends React.Component {
|
||||
<UserListPage
|
||||
organizationName={this.state.organizationName}
|
||||
groupName={this.state.groupName}
|
||||
groupId={this.state.groupId}
|
||||
{...this.props}
|
||||
/>
|
||||
</Col>
|
||||
|
@@ -45,20 +45,17 @@ class MessageEditPage extends React.Component {
|
||||
|
||||
getMessage() {
|
||||
MessageBackend.getMessage("admin", this.state.messageName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((message) => {
|
||||
if (message === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
message: res,
|
||||
message: message,
|
||||
});
|
||||
|
||||
this.getUsers(res.organization);
|
||||
this.getUsers(message.organization);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -83,10 +80,6 @@ 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,19 +47,14 @@ class ModelEditPage extends React.Component {
|
||||
|
||||
getModel() {
|
||||
ModelBackend.getModel(this.state.organizationName, this.state.modelName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((model) => {
|
||||
if (model === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
model: res,
|
||||
model: model,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -68,14 +68,9 @@ class OrganizationEditPage extends React.Component {
|
||||
|
||||
getApplications() {
|
||||
ApplicationBackend.getApplicationsByOrganization("admin", this.state.organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
.then((applications) => {
|
||||
this.setState({
|
||||
applications: res,
|
||||
applications: applications,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -49,26 +49,21 @@ class PermissionEditPage extends React.Component {
|
||||
|
||||
getPermission() {
|
||||
PermissionBackend.getPermission(this.state.organizationName, this.state.permissionName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((permission) => {
|
||||
if (permission === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
permission: res,
|
||||
permission: permission,
|
||||
});
|
||||
|
||||
this.getUsers(res.owner);
|
||||
this.getRoles(res.owner);
|
||||
this.getModels(res.owner);
|
||||
this.getResources(res.owner);
|
||||
this.getModel(res.owner, res.model);
|
||||
this.getUsers(permission.owner);
|
||||
this.getRoles(permission.owner);
|
||||
this.getModels(permission.owner);
|
||||
this.getResources(permission.owner);
|
||||
this.getModel(permission.owner, permission.model);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -84,10 +79,6 @@ class PermissionEditPage extends React.Component {
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
users: res,
|
||||
});
|
||||
@@ -97,10 +88,6 @@ class PermissionEditPage extends React.Component {
|
||||
getRoles(organizationName) {
|
||||
RoleBackend.getRoles(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
roles: res,
|
||||
});
|
||||
@@ -110,10 +97,6 @@ class PermissionEditPage extends React.Component {
|
||||
getModels(organizationName) {
|
||||
ModelBackend.getModels(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
models: res,
|
||||
});
|
||||
@@ -123,10 +106,6 @@ class PermissionEditPage extends React.Component {
|
||||
getModel(organizationName, modelName) {
|
||||
ModelBackend.getModel(organizationName, modelName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
model: res,
|
||||
});
|
||||
|
@@ -64,10 +64,6 @@ class PlanEditPage extends React.Component {
|
||||
getRoles(organizationName) {
|
||||
RoleBackend.getRoles(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
roles: res,
|
||||
});
|
||||
@@ -77,10 +73,6 @@ class PlanEditPage extends React.Component {
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
users: res,
|
||||
});
|
||||
|
@@ -49,31 +49,22 @@ class PricingEditPage extends React.Component {
|
||||
|
||||
getPricing() {
|
||||
PricingBackend.getPricing(this.state.organizationName, this.state.pricingName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((pricing) => {
|
||||
if (pricing === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
pricing: res,
|
||||
pricing: pricing,
|
||||
});
|
||||
this.getPlans(res.owner);
|
||||
this.getPlans(pricing.owner);
|
||||
});
|
||||
}
|
||||
|
||||
getPlans(organizationName) {
|
||||
PlanBackend.getPlans(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
plans: res,
|
||||
});
|
||||
@@ -118,13 +109,9 @@ class PricingEditPage extends React.Component {
|
||||
|
||||
getUserApplication() {
|
||||
ApplicationBackend.getUserApplication(this.state.organizationName, this.state.userName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
.then((application) => {
|
||||
this.setState({
|
||||
application: res,
|
||||
application: application,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -41,14 +41,9 @@ class ProductBuyPage extends React.Component {
|
||||
}
|
||||
|
||||
ProductBackend.getProduct(this.props.account.owner, this.state.productName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
.then((product) => {
|
||||
this.setState({
|
||||
product: res,
|
||||
product: product,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -42,22 +42,18 @@ class RoleEditPage extends React.Component {
|
||||
|
||||
getRole() {
|
||||
RoleBackend.getRole(this.state.organizationName, this.state.roleName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((role) => {
|
||||
if (role === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
role: res,
|
||||
role: role,
|
||||
});
|
||||
|
||||
this.getUsers(res.owner);
|
||||
this.getRoles(res.owner);
|
||||
this.getUsers(role.owner);
|
||||
this.getRoles(role.owner);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -73,10 +69,6 @@ class RoleEditPage extends React.Component {
|
||||
getUsers(organizationName) {
|
||||
UserBackend.getUsers(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
users: res,
|
||||
});
|
||||
@@ -86,10 +78,6 @@ class RoleEditPage extends React.Component {
|
||||
getRoles(organizationName) {
|
||||
RoleBackend.getRoles(organizationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.setState({
|
||||
roles: res,
|
||||
});
|
||||
|
@@ -46,23 +46,18 @@ class SubscriptionEditPage extends React.Component {
|
||||
|
||||
getSubscription() {
|
||||
SubscriptionBackend.getSubscription(this.state.organizationName, this.state.subscriptionName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((subscription) => {
|
||||
if (subscription === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
subscription: res,
|
||||
subscription: subscription,
|
||||
});
|
||||
|
||||
this.getUsers(res.owner);
|
||||
this.getPlanes(res.owner);
|
||||
this.getUsers(subscription.owner);
|
||||
this.getPlanes(subscription.owner);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -78,10 +73,6 @@ class SubscriptionEditPage 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,19 +47,14 @@ class SyncerEditPage extends React.Component {
|
||||
|
||||
getSyncer() {
|
||||
SyncerBackend.getSyncer("admin", this.state.syncerName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((syncer) => {
|
||||
if (syncer === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
syncer: res,
|
||||
syncer: syncer,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -381,16 +376,6 @@ class SyncerEditPage extends React.Component {
|
||||
</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||
{Setting.getLabel(i18next.t("syncer:Is read-only"), i18next.t("syncer:Is read-only - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={1} >
|
||||
<Switch checked={this.state.syncer.isReadOnly} onChange={checked => {
|
||||
this.updateSyncerField("isReadOnly", checked);
|
||||
}} />
|
||||
</Col>
|
||||
</Row>
|
||||
<Row style={{marginTop: "20px"}} >
|
||||
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
|
||||
{Setting.getLabel(i18next.t("general:Is enabled"), i18next.t("general:Is enabled - Tooltip"))} :
|
||||
|
@@ -42,7 +42,6 @@ class SyncerListPage extends BaseListPage {
|
||||
affiliationTable: "",
|
||||
avatarBaseUrl: "",
|
||||
syncInterval: 10,
|
||||
isReadOnly: false,
|
||||
isEnabled: false,
|
||||
};
|
||||
}
|
||||
|
@@ -35,19 +35,14 @@ class TokenEditPage extends React.Component {
|
||||
|
||||
getToken() {
|
||||
TokenBackend.getToken("admin", this.state.tokenName)
|
||||
.then((res) => {
|
||||
if (res === null) {
|
||||
.then((token) => {
|
||||
if (token === null) {
|
||||
this.props.history.push("/404");
|
||||
return;
|
||||
}
|
||||
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.setState({
|
||||
token: res,
|
||||
token: token,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -34,6 +34,7 @@ import {CountryCodeSelect} from "./common/select/CountryCodeSelect";
|
||||
import PopconfirmModal from "./common/modal/PopconfirmModal";
|
||||
import {DeleteMfa} from "./backend/MfaBackend";
|
||||
import {CheckCircleOutlined, HolderOutlined, UsergroupAddOutlined} from "@ant-design/icons";
|
||||
import {SmsMfaType} from "./auth/MfaSetupPage";
|
||||
import * as MfaBackend from "./backend/MfaBackend";
|
||||
|
||||
const {Option} = Select;
|
||||
@@ -121,17 +122,13 @@ class UserEditPage extends React.Component {
|
||||
|
||||
getUserApplication() {
|
||||
ApplicationBackend.getUserApplication(this.state.organizationName, this.state.userName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
.then((application) => {
|
||||
this.setState({
|
||||
application: res,
|
||||
application: application,
|
||||
});
|
||||
|
||||
this.setState({
|
||||
isGroupsVisible: res.organizationObj.accountItems?.some((item) => item.name === "Groups" && item.visible),
|
||||
isGroupsVisible: application.organizationObj.accountItems?.some((item) => item.name === "Groups" && item.visible),
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -192,6 +189,16 @@ class UserEditPage extends React.Component {
|
||||
return this.props.account.countryCode;
|
||||
}
|
||||
|
||||
getMfaProps(type = "") {
|
||||
if (!(this.state.multiFactorAuths?.length > 0)) {
|
||||
return [];
|
||||
}
|
||||
if (type === "") {
|
||||
return this.state.multiFactorAuths;
|
||||
}
|
||||
return this.state.multiFactorAuths.filter(mfaProps => mfaProps.type === type);
|
||||
}
|
||||
|
||||
loadMore = (table, type) => {
|
||||
return <div
|
||||
style={{
|
||||
@@ -209,12 +216,13 @@ class UserEditPage extends React.Component {
|
||||
</div>;
|
||||
};
|
||||
|
||||
deleteMfa = () => {
|
||||
deleteMfa = (id) => {
|
||||
this.setState({
|
||||
RemoveMfaLoading: true,
|
||||
});
|
||||
|
||||
DeleteMfa({
|
||||
id: id,
|
||||
owner: this.state.user.owner,
|
||||
name: this.state.user.name,
|
||||
}).then((res) => {
|
||||
@@ -301,7 +309,7 @@ class UserEditPage extends React.Component {
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Select virtual={false} mode="multiple" style={{width: "100%"}} disabled={disabled} value={this.state.user.groups ?? []} onChange={(value => {
|
||||
if (this.state.groups?.filter(group => value.includes(group.name))
|
||||
if (this.state.groups?.filter(group => value.includes(group.id))
|
||||
.filter(group => group.type === "Physical").length > 1) {
|
||||
Setting.showMessage("error", i18next.t("general:You can only select one physical group"));
|
||||
return;
|
||||
@@ -311,7 +319,7 @@ class UserEditPage extends React.Component {
|
||||
})}
|
||||
>
|
||||
{
|
||||
this.state.groups?.map((group) => <Option key={group.name} value={group.name}>
|
||||
this.state.groups?.map((group) => <Option key={group.id} value={group.id}>
|
||||
<Space>
|
||||
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
|
||||
{group.displayName}
|
||||
@@ -402,7 +410,7 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("general:Password"), i18next.t("general:Password - Tooltip"))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<PasswordModal user={this.state.user} organization={this.state.application?.organizationObj} account={this.props.account} disabled={disabled} />
|
||||
<PasswordModal user={this.state.user} account={this.props.account} disabled={disabled} />
|
||||
</Col>
|
||||
</Row>
|
||||
);
|
||||
@@ -439,7 +447,7 @@ class UserEditPage extends React.Component {
|
||||
<CountryCodeSelect
|
||||
style={{width: "30%"}}
|
||||
// disabled={!Setting.isLocalAdminUser(this.props.account) ? true : disabled}
|
||||
initValue={this.state.user.countryCode}
|
||||
value={this.state.user.countryCode}
|
||||
onChange={(value) => {
|
||||
this.updateUserField("countryCode", value);
|
||||
}}
|
||||
@@ -852,61 +860,61 @@ class UserEditPage extends React.Component {
|
||||
{Setting.getLabel(i18next.t("mfa:Multi-factor authentication"), i18next.t("mfa:Multi-factor authentication - Tooltip "))} :
|
||||
</Col>
|
||||
<Col span={22} >
|
||||
<Card title={i18next.t("mfa:Multi-factor methods")}
|
||||
extra={this.state.multiFactorAuths?.some(mfaProps => mfaProps.enabled) ?
|
||||
<PopconfirmModal
|
||||
text={i18next.t("general:Disable")}
|
||||
title={i18next.t("general:Sure to disable") + "?"}
|
||||
onConfirm={() => this.deleteMfa()}
|
||||
/> : null
|
||||
}>
|
||||
<List
|
||||
rowKey="mfaType"
|
||||
itemLayout="horizontal"
|
||||
dataSource={this.state.multiFactorAuths}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<Space>
|
||||
{i18next.t("general:Type")}: {item.mfaType}
|
||||
{item.secret}
|
||||
</Space>
|
||||
{item.enabled ? (
|
||||
<Space>
|
||||
{item.enabled ?
|
||||
<Card title={i18next.t("mfa:Multi-factor methods")}>
|
||||
<Card type="inner" title={i18next.t("mfa:SMS/Email message")}>
|
||||
<List
|
||||
itemLayout="horizontal"
|
||||
dataSource={this.getMfaProps(SmsMfaType)}
|
||||
loadMore={this.loadMore(this.state.multiFactorAuths, SmsMfaType)}
|
||||
renderItem={(item, index) => (
|
||||
<List.Item>
|
||||
<div>
|
||||
{item?.id === undefined ?
|
||||
<Button type={"default"} onClick={() => {
|
||||
Setting.goToLink("/mfa-authentication/setup");
|
||||
}}>
|
||||
{i18next.t("mfa:Setup")}
|
||||
</Button> :
|
||||
<Tag icon={<CheckCircleOutlined />} color="success">
|
||||
{i18next.t("general:Enabled")}
|
||||
</Tag> : null
|
||||
</Tag>
|
||||
}
|
||||
{item.isPreferred ?
|
||||
<Tag icon={<CheckCircleOutlined />} color="blue" style={{marginRight: 20}} >
|
||||
{i18next.t("mfa:preferred")}
|
||||
</Tag> :
|
||||
<Button type="primary" style={{marginRight: 20}} onClick={() => {
|
||||
const values = {
|
||||
owner: this.state.user.owner,
|
||||
name: this.state.user.name,
|
||||
mfaType: item.mfaType,
|
||||
};
|
||||
MfaBackend.SetPreferredMfa(values).then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
multiFactorAuths: res.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
}}>
|
||||
{i18next.t("mfa:Set preferred")}
|
||||
</Button>
|
||||
}
|
||||
</Space>
|
||||
) : <Button type={"default"} onClick={() => {
|
||||
Setting.goToLink(`/mfa-authentication/setup?mfaType=${item.mfaType}`);
|
||||
}}>
|
||||
{i18next.t("mfa:Setup")}
|
||||
</Button>}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
{item.secret}
|
||||
</div>
|
||||
{item?.id === undefined ? null :
|
||||
<div>
|
||||
{item.isPreferred ?
|
||||
<Tag icon={<CheckCircleOutlined />} color="blue" style={{marginRight: 20}} >
|
||||
{i18next.t("mfa:preferred")}
|
||||
</Tag> :
|
||||
<Button type="primary" style={{marginRight: 20}} onClick={() => {
|
||||
const values = {
|
||||
owner: this.state.user.owner,
|
||||
name: this.state.user.name,
|
||||
id: item.id,
|
||||
};
|
||||
MfaBackend.SetPreferredMfa(values).then((res) => {
|
||||
if (res.status === "ok") {
|
||||
this.setState({
|
||||
multiFactorAuths: res.data,
|
||||
});
|
||||
}
|
||||
});
|
||||
}}>
|
||||
{i18next.t("mfa:Set preferred")}
|
||||
</Button>
|
||||
}
|
||||
<PopconfirmModal
|
||||
title={i18next.t("general:Sure to delete") + "?"}
|
||||
onConfirm={() => this.deleteMfa(item.id)}
|
||||
>
|
||||
</PopconfirmModal>
|
||||
</div>
|
||||
}
|
||||
</List.Item>
|
||||
)}
|
||||
/>
|
||||
</Card>
|
||||
</Card>
|
||||
</Col>
|
||||
</Row>
|
||||
|
@@ -75,7 +75,7 @@ class UserListPage extends BaseListPage {
|
||||
phone: Setting.getRandomNumber(),
|
||||
countryCode: this.state.organization.countryCodes?.length > 0 ? this.state.organization.countryCodes[0] : "",
|
||||
address: [],
|
||||
groups: this.props.groupName ? [this.props.groupName] : [],
|
||||
groups: this.props.groupId ? [this.props.groupId] : [],
|
||||
affiliation: "Example Inc.",
|
||||
tag: "staff",
|
||||
region: "",
|
||||
@@ -126,8 +126,8 @@ class UserListPage extends BaseListPage {
|
||||
|
||||
removeUserFromGroup(i) {
|
||||
const user = this.state.data[i];
|
||||
const group = this.props.groupName;
|
||||
UserBackend.removeUserFromGroup({groupName: group, owner: user.owner, name: user.name})
|
||||
const group = this.props.groupId;
|
||||
UserBackend.removeUserFromGroup({groupId: group, owner: user.owner, name: user.name})
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("general:Successfully removed"));
|
||||
@@ -270,7 +270,7 @@ class UserListPage extends BaseListPage {
|
||||
render: (text, record, index) => {
|
||||
return (
|
||||
<a target="_blank" rel="noreferrer" href={text}>
|
||||
<img referrerPolicy="no-referrer" src={text} alt={text} width={50} />
|
||||
<img src={text} alt={text} width={50} />
|
||||
</a>
|
||||
);
|
||||
},
|
||||
@@ -396,7 +396,7 @@ class UserListPage extends BaseListPage {
|
||||
width: "190px",
|
||||
fixed: (Setting.isMobile()) ? "false" : "right",
|
||||
render: (text, record, index) => {
|
||||
const isTreePage = this.props.groupName !== undefined;
|
||||
const isTreePage = this.props.groupId !== undefined;
|
||||
const disabled = (record.owner === this.props.account.owner && record.name === this.props.account.name) || (record.owner === "built-in" && record.name === "admin");
|
||||
return (
|
||||
<Space>
|
||||
@@ -483,7 +483,7 @@ class UserListPage extends BaseListPage {
|
||||
});
|
||||
} else {
|
||||
(this.props.groupName ?
|
||||
UserBackend.getUsers(this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder, this.props.groupName) :
|
||||
UserBackend.getUsers(this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder, `${this.state.organizationName}/${this.props.groupName}`) :
|
||||
UserBackend.getUsers(this.state.organizationName, params.pagination.current, params.pagination.pageSize, field, value, sortField, sortOrder))
|
||||
.then((res) => {
|
||||
this.setState({
|
||||
|
@@ -63,12 +63,8 @@ class ForgetPage extends React.Component {
|
||||
}
|
||||
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.onUpdateApplication(res);
|
||||
.then((application) => {
|
||||
this.onUpdateApplication(application);
|
||||
});
|
||||
}
|
||||
getApplicationObj() {
|
||||
|
@@ -159,12 +159,8 @@ class LoginPage extends React.Component {
|
||||
|
||||
if (this.state.owner === null || this.state.type === "saml") {
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.onUpdateApplication(res);
|
||||
.then((application) => {
|
||||
this.onUpdateApplication(application);
|
||||
});
|
||||
} else {
|
||||
OrganizationBackend.getDefaultApplication("admin", this.state.owner)
|
||||
|
@@ -16,8 +16,8 @@ import React, {useState} from "react";
|
||||
import i18next from "i18next";
|
||||
import {Button, Input} from "antd";
|
||||
import * as AuthBackend from "./AuthBackend";
|
||||
import {EmailMfaType, RecoveryMfaType, SmsMfaType} from "./MfaSetupPage";
|
||||
import {MfaSmsVerifyForm, MfaTotpVerifyForm, mfaAuth} from "./MfaVerifyForm";
|
||||
import {SmsMfaType} from "./MfaSetupPage";
|
||||
import {MfaSmsVerifyForm} from "./MfaVerifyForm";
|
||||
|
||||
export const NextMfa = "NextMfa";
|
||||
export const RequiredMfa = "RequiredMfa";
|
||||
@@ -26,20 +26,20 @@ export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, applicatio
|
||||
formValues.password = "";
|
||||
formValues.username = "";
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [mfaType, setMfaType] = useState(mfaProps.mfaType);
|
||||
const [type, setType] = useState(mfaProps.type);
|
||||
const [recoveryCode, setRecoveryCode] = useState("");
|
||||
|
||||
const verify = ({passcode}) => {
|
||||
setLoading(true);
|
||||
const values = {...formValues, passcode, mfaType};
|
||||
const values = {...formValues, passcode, mfaType: type};
|
||||
AuthBackend.login(values, oAuthParams).then((res) => {
|
||||
if (res.status === "ok") {
|
||||
onSuccess(res);
|
||||
} else {
|
||||
onFail(res.msg);
|
||||
}
|
||||
}).catch((res) => {
|
||||
onFail(res.message);
|
||||
}).catch((reason) => {
|
||||
onFail(reason.message);
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
@@ -49,18 +49,19 @@ export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, applicatio
|
||||
setLoading(true);
|
||||
AuthBackend.login({...formValues, recoveryCode}, oAuthParams).then(res => {
|
||||
if (res.status === "ok") {
|
||||
onSuccess(res);
|
||||
onSuccess();
|
||||
} else {
|
||||
onFail(res.msg);
|
||||
}
|
||||
}).catch((res) => {
|
||||
onFail(res.message);
|
||||
}).catch((reason) => {
|
||||
onFail(reason.message);
|
||||
}).finally(() => {
|
||||
setLoading(false);
|
||||
});
|
||||
};
|
||||
|
||||
if (mfaType !== RecoveryMfaType) {
|
||||
switch (type) {
|
||||
case SmsMfaType:
|
||||
return (
|
||||
<div style={{width: 300, height: 350}}>
|
||||
<div style={{marginBottom: 24, textAlign: "center", fontSize: "24px"}}>
|
||||
@@ -69,29 +70,22 @@ export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, applicatio
|
||||
<div style={{marginBottom: 24}}>
|
||||
{i18next.t("mfa:Multi-factor authentication description")}
|
||||
</div>
|
||||
{mfaType === SmsMfaType || mfaType === EmailMfaType ? (
|
||||
<MfaSmsVerifyForm
|
||||
mfaProps={mfaProps}
|
||||
method={mfaAuth}
|
||||
onFinish={verify}
|
||||
application={application}
|
||||
/>) : (
|
||||
<MfaTotpVerifyForm
|
||||
mfaProps={mfaProps}
|
||||
onFinish={verify}
|
||||
/>
|
||||
)}
|
||||
<MfaSmsVerifyForm
|
||||
mfaProps={mfaProps}
|
||||
onFinish={verify}
|
||||
application={application}
|
||||
/>
|
||||
<span style={{float: "right"}}>
|
||||
{i18next.t("mfa:Have problems?")}
|
||||
<a onClick={() => {
|
||||
setMfaType("recovery");
|
||||
setType("recovery");
|
||||
}}>
|
||||
{i18next.t("mfa:Use a recovery code")}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
} else {
|
||||
case "recovery":
|
||||
return (
|
||||
<div style={{width: 300, height: 350}}>
|
||||
<div style={{marginBottom: 24, textAlign: "center", fontSize: "24px"}}>
|
||||
@@ -114,12 +108,14 @@ export function MfaAuthVerifyForm({formValues, oAuthParams, mfaProps, applicatio
|
||||
<span style={{float: "right"}}>
|
||||
{i18next.t("mfa:Have problems?")}
|
||||
<a onClick={() => {
|
||||
setMfaType(mfaProps.mfaType);
|
||||
setType(mfaProps.type);
|
||||
}}>
|
||||
{i18next.t("mfa:Use SMS verification code")}
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@@ -14,19 +14,18 @@
|
||||
|
||||
import React, {useState} from "react";
|
||||
import {Button, Col, Form, Input, Result, Row, Steps} from "antd";
|
||||
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
||||
import * as Setting from "../Setting";
|
||||
import i18next from "i18next";
|
||||
import * as MfaBackend from "../backend/MfaBackend";
|
||||
import {CheckOutlined, KeyOutlined, LockOutlined, UserOutlined} from "@ant-design/icons";
|
||||
|
||||
import * as UserBackend from "../backend/UserBackend";
|
||||
import {MfaSmsVerifyForm, MfaTotpVerifyForm, mfaSetup} from "./MfaVerifyForm";
|
||||
import {MfaSmsVerifyForm, MfaTotpVerifyForm} from "./MfaVerifyForm";
|
||||
import * as ApplicationBackend from "../backend/ApplicationBackend";
|
||||
|
||||
export const EmailMfaType = "email";
|
||||
const {Step} = Steps;
|
||||
export const SmsMfaType = "sms";
|
||||
export const TotpMfaType = "app";
|
||||
export const RecoveryMfaType = "recovery";
|
||||
|
||||
function CheckPasswordForm({user, onSuccess, onFail}) {
|
||||
const [form] = Form.useForm();
|
||||
@@ -79,9 +78,10 @@ function CheckPasswordForm({user, onSuccess, onFail}) {
|
||||
|
||||
export function MfaVerifyForm({mfaProps, application, user, onSuccess, onFail}) {
|
||||
const [form] = Form.useForm();
|
||||
mfaProps = mfaProps ?? {type: ""};
|
||||
|
||||
const onFinish = ({passcode}) => {
|
||||
const data = {passcode, mfaType: mfaProps.mfaType, ...user};
|
||||
const data = {passcode, type: mfaProps.type, ...user};
|
||||
MfaBackend.MfaSetupVerify(data)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@@ -98,24 +98,20 @@ export function MfaVerifyForm({mfaProps, application, user, onSuccess, onFail})
|
||||
});
|
||||
};
|
||||
|
||||
if (mfaProps === undefined || mfaProps === null) {
|
||||
return <div></div>;
|
||||
}
|
||||
|
||||
if (mfaProps.mfaType === SmsMfaType || mfaProps.mfaType === EmailMfaType) {
|
||||
return <MfaSmsVerifyForm mfaProps={mfaProps} onFinish={onFinish} application={application} method={mfaSetup} user={user} />;
|
||||
} else if (mfaProps.mfaType === TotpMfaType) {
|
||||
return <MfaTotpVerifyForm mfaProps={mfaProps} onFinish={onFinish} />;
|
||||
if (mfaProps.type === SmsMfaType) {
|
||||
return <MfaSmsVerifyForm onFinish={onFinish} application={application} />;
|
||||
} else if (mfaProps.type === TotpMfaType) {
|
||||
return <MfaTotpVerifyForm onFinish={onFinish} mfaProps={mfaProps} />;
|
||||
} else {
|
||||
return <div></div>;
|
||||
}
|
||||
}
|
||||
|
||||
function EnableMfaForm({user, mfaType, recoveryCodes, onSuccess, onFail}) {
|
||||
function EnableMfaForm({user, mfaProps, onSuccess, onFail}) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
const requestEnableTotp = () => {
|
||||
const data = {
|
||||
mfaType,
|
||||
type: mfaProps.type,
|
||||
...user,
|
||||
};
|
||||
setLoading(true);
|
||||
@@ -135,7 +131,7 @@ function EnableMfaForm({user, mfaType, recoveryCodes, onSuccess, onFail}) {
|
||||
<div style={{width: "400px"}}>
|
||||
<p>{i18next.t("mfa:Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code")}</p>
|
||||
<br />
|
||||
<code style={{fontStyle: "solid"}}>{recoveryCodes[0]}</code>
|
||||
<code style={{fontStyle: "solid"}}>{mfaProps.recoveryCodes[0]}</code>
|
||||
<Button style={{marginTop: 24}} loading={loading} onClick={() => {
|
||||
requestEnableTotp();
|
||||
}} block type="primary">
|
||||
@@ -150,13 +146,12 @@ class MfaSetupPage extends React.Component {
|
||||
super(props);
|
||||
this.state = {
|
||||
account: props.account,
|
||||
application: this.props.application ?? null,
|
||||
applicationName: props.account.signupApplication ?? "",
|
||||
applicationName: (props.applicationName ?? props.account?.signupApplication) ?? "",
|
||||
isAuthenticated: props.isAuthenticated ?? false,
|
||||
isPromptPage: props.isPromptPage,
|
||||
redirectUri: props.redirectUri,
|
||||
current: props.current ?? 0,
|
||||
mfaType: props.mfaType ?? new URLSearchParams(props.location?.search)?.get("mfaType") ?? SmsMfaType,
|
||||
type: props.type ?? SmsMfaType,
|
||||
mfaProps: null,
|
||||
};
|
||||
}
|
||||
@@ -166,9 +161,9 @@ class MfaSetupPage extends React.Component {
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState, snapshot) {
|
||||
if (this.state.isAuthenticated === true && (this.state.mfaProps === null || this.state.mfaType !== prevState.mfaType)) {
|
||||
if (this.state.isAuthenticated === true && this.state.mfaProps === null) {
|
||||
MfaBackend.MfaSetupInitiate({
|
||||
mfaType: this.state.mfaType,
|
||||
type: this.state.type,
|
||||
...this.getUser(),
|
||||
}).then((res) => {
|
||||
if (res.status === "ok") {
|
||||
@@ -183,19 +178,11 @@ class MfaSetupPage extends React.Component {
|
||||
}
|
||||
|
||||
getApplication() {
|
||||
if (this.state.application !== null) {
|
||||
return;
|
||||
}
|
||||
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((res) => {
|
||||
if (res !== null) {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
.then((application) => {
|
||||
if (application !== null) {
|
||||
this.setState({
|
||||
application: res,
|
||||
application: application,
|
||||
});
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t("mfa:Failed to get application"));
|
||||
@@ -213,89 +200,53 @@ class MfaSetupPage extends React.Component {
|
||||
renderStep() {
|
||||
switch (this.state.current) {
|
||||
case 0:
|
||||
return (
|
||||
<CheckPasswordForm
|
||||
user={this.getUser()}
|
||||
onSuccess={() => {
|
||||
this.setState({
|
||||
current: this.state.current + 1,
|
||||
isAuthenticated: true,
|
||||
});
|
||||
}}
|
||||
onFail={(res) => {
|
||||
Setting.showMessage("error", i18next.t("mfa:Failed to initiate MFA"));
|
||||
}}
|
||||
/>
|
||||
);
|
||||
return <CheckPasswordForm
|
||||
user={this.getUser()}
|
||||
onSuccess={() => {
|
||||
this.setState({
|
||||
current: this.state.current + 1,
|
||||
isAuthenticated: true,
|
||||
});
|
||||
}}
|
||||
onFail={(res) => {
|
||||
Setting.showMessage("error", i18next.t("mfa:Failed to initiate MFA"));
|
||||
}}
|
||||
/>;
|
||||
case 1:
|
||||
if (!this.state.isAuthenticated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<div>
|
||||
<MfaVerifyForm
|
||||
mfaProps={this.state.mfaProps}
|
||||
application={this.state.application}
|
||||
user={this.props.account}
|
||||
onSuccess={() => {
|
||||
this.setState({
|
||||
current: this.state.current + 1,
|
||||
});
|
||||
}}
|
||||
onFail={(res) => {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to verify"));
|
||||
}}
|
||||
/>
|
||||
<Col span={24} style={{display: "flex", justifyContent: "left"}}>
|
||||
{(this.state.mfaType === EmailMfaType || this.props.account.mfaEmailEnabled) ? null :
|
||||
<Button type={"link"} onClick={() => {
|
||||
this.setState({
|
||||
mfaType: EmailMfaType,
|
||||
});
|
||||
}
|
||||
}>{i18next.t("mfa:Use Email")}</Button>
|
||||
}
|
||||
{
|
||||
(this.state.mfaType === SmsMfaType || this.props.account.mfaPhoneEnabled) ? null :
|
||||
<Button type={"link"} onClick={() => {
|
||||
this.setState({
|
||||
mfaType: SmsMfaType,
|
||||
});
|
||||
}
|
||||
}>{i18next.t("mfa:Use SMS")}</Button>
|
||||
}
|
||||
{
|
||||
(this.state.mfaType === TotpMfaType) ? null :
|
||||
<Button type={"link"} onClick={() => {
|
||||
this.setState({
|
||||
mfaType: TotpMfaType,
|
||||
});
|
||||
}
|
||||
}>{i18next.t("mfa:Use Authenticator App")}</Button>
|
||||
}
|
||||
</Col>
|
||||
</div>
|
||||
);
|
||||
return <MfaVerifyForm
|
||||
mfaProps={this.state.mfaProps}
|
||||
application={this.state.application}
|
||||
user={this.getUser()}
|
||||
onSuccess={() => {
|
||||
this.setState({
|
||||
current: this.state.current + 1,
|
||||
});
|
||||
}}
|
||||
onFail={(res) => {
|
||||
Setting.showMessage("error", i18next.t("general:Failed to verify"));
|
||||
}}
|
||||
/>;
|
||||
case 2:
|
||||
if (!this.state.isAuthenticated) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<EnableMfaForm user={this.getUser()} mfaType={this.state.mfaType} recoveryCodes={this.state.mfaProps.recoveryCodes}
|
||||
onSuccess={() => {
|
||||
Setting.showMessage("success", i18next.t("general:Enabled successfully"));
|
||||
if (this.state.isPromptPage && this.state.redirectUri) {
|
||||
Setting.goToLink(this.state.redirectUri);
|
||||
} else {
|
||||
Setting.goToLink("/account");
|
||||
}
|
||||
}}
|
||||
onFail={(res) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to enable")}: ${res.msg}`);
|
||||
}} />
|
||||
);
|
||||
return <EnableMfaForm user={this.getUser()} mfaProps={{type: this.state.type, ...this.state.mfaProps}}
|
||||
onSuccess={() => {
|
||||
Setting.showMessage("success", i18next.t("general:Enabled successfully"));
|
||||
if (this.state.isPromptPage && this.state.redirectUri) {
|
||||
Setting.goToLink(this.state.redirectUri);
|
||||
} else {
|
||||
Setting.goToLink("/account");
|
||||
}
|
||||
}}
|
||||
onFail={(res) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to enable")}: ${res.msg}`);
|
||||
}} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@@ -318,20 +269,25 @@ class MfaSetupPage extends React.Component {
|
||||
<Col span={24} style={{justifyContent: "center"}}>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<p style={{textAlign: "center", fontSize: "28px"}}>
|
||||
{i18next.t("mfa:Protect your account with Multi-factor authentication")}</p>
|
||||
<p style={{textAlign: "center", fontSize: "16px", marginTop: "10px"}}>{i18next.t("mfa:Each time you sign in to your Account, you'll need your password and a authentication code")}</p>
|
||||
<div style={{textAlign: "center", fontSize: "28px"}}>
|
||||
{i18next.t("mfa:Protect your account with Multi-factor authentication")}</div>
|
||||
<div style={{textAlign: "center", fontSize: "16px", marginTop: "10px"}}>{i18next.t("mfa:Each time you sign in to your Account, you'll need your password and a authentication code")}</div>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row>
|
||||
<Col span={24}>
|
||||
<Steps current={this.state.current} style={{
|
||||
width: "90%",
|
||||
maxWidth: "500px",
|
||||
margin: "auto",
|
||||
marginTop: "80px",
|
||||
}} >
|
||||
<Step title={i18next.t("mfa:Verify Password")} icon={<UserOutlined />} />
|
||||
<Step title={i18next.t("mfa:Verify Code")} icon={<KeyOutlined />} />
|
||||
<Step title={i18next.t("general:Enable")} icon={<CheckOutlined />} />
|
||||
</Steps>
|
||||
</Col>
|
||||
</Row>
|
||||
<Steps current={this.state.current}
|
||||
items={[
|
||||
{title: i18next.t("mfa:Verify Password"), icon: <UserOutlined />},
|
||||
{title: i18next.t("mfa:Verify Code"), icon: <KeyOutlined />},
|
||||
{title: i18next.t("general:Enable"), icon: <CheckOutlined />},
|
||||
]}
|
||||
style={{width: "90%", maxWidth: "500px", margin: "auto", marginTop: "50px",
|
||||
}} >
|
||||
</Steps>
|
||||
</Col>
|
||||
<Col span={24} style={{display: "flex", justifyContent: "center"}}>
|
||||
<div style={{marginTop: "10px", textAlign: "center"}}>
|
||||
|
@@ -12,88 +12,60 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import {Button, Col, Form, Input, QRCode, Space} from "antd";
|
||||
import {Button, Col, Form, Input, Row} from "antd";
|
||||
import i18next from "i18next";
|
||||
import {CopyOutlined, UserOutlined} from "@ant-design/icons";
|
||||
import {SendCodeInput} from "../common/SendCodeInput";
|
||||
import * as Setting from "../Setting";
|
||||
import React, {useEffect} from "react";
|
||||
import React from "react";
|
||||
import copy from "copy-to-clipboard";
|
||||
import {CountryCodeSelect} from "../common/select/CountryCodeSelect";
|
||||
import {EmailMfaType, SmsMfaType} from "./MfaSetupPage";
|
||||
|
||||
export const mfaAuth = "mfaAuth";
|
||||
export const mfaSetup = "mfaSetup";
|
||||
|
||||
export const MfaSmsVerifyForm = ({mfaProps, application, onFinish, method, user}) => {
|
||||
const [dest, setDest] = React.useState(mfaProps.secret ?? "");
|
||||
export const MfaSmsVerifyForm = ({mfaProps, application, onFinish}) => {
|
||||
const [dest, setDest] = React.useState(mfaProps?.secret ?? "");
|
||||
const [form] = Form.useForm();
|
||||
|
||||
useEffect(() => {
|
||||
if (mfaProps.mfaType === SmsMfaType) {
|
||||
setDest(user.phone);
|
||||
}
|
||||
|
||||
if (mfaProps.mfaType === EmailMfaType) {
|
||||
setDest(user.email);
|
||||
}
|
||||
}, [mfaProps.mfaType]);
|
||||
|
||||
const isEmail = () => {
|
||||
return mfaProps.mfaType === EmailMfaType;
|
||||
};
|
||||
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
style={{width: "300px"}}
|
||||
onFinish={onFinish}
|
||||
initialValues={{
|
||||
countryCode: mfaProps.countryCode,
|
||||
}}
|
||||
>
|
||||
{dest !== "" ?
|
||||
<div style={{marginBottom: 20, textAlign: "left", gap: 8}}>
|
||||
{isEmail() ? i18next.t("mfa:Your email is") : i18next.t("mfa:Your phone is")} {dest}
|
||||
{mfaProps?.secret !== undefined ?
|
||||
<div style={{marginBottom: 20}}>
|
||||
{Setting.IsEmail(dest) ? i18next.t("mfa:Your email is") : i18next.t("mfa:Your phone is")} {dest}
|
||||
</div> :
|
||||
(<React.Fragment>
|
||||
<p>{isEmail() ? i18next.t("mfa:Please bind your email first, the system will automatically uses the mail for multi-factor authentication") :
|
||||
i18next.t("mfa:Please bind your phone first, the system automatically uses the phone for multi-factor authentication")}
|
||||
</p>
|
||||
<Input.Group compact style={{width: "300Px", marginBottom: "30px"}}>
|
||||
{isEmail() ? null :
|
||||
<Form.Item
|
||||
name="countryCode"
|
||||
noStyle
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: i18next.t("signup:Please select your country code!"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<CountryCodeSelect
|
||||
initValue={mfaProps.countryCode}
|
||||
style={{width: "30%"}}
|
||||
countryCodes={application.organizationObj.countryCodes}
|
||||
/>
|
||||
</Form.Item>
|
||||
}
|
||||
<Input.Group compact style={{width: "300Px", marginBottom: "30px"}}>
|
||||
{Setting.IsEmail(dest) ? null :
|
||||
<Form.Item
|
||||
name="dest"
|
||||
name="countryCode"
|
||||
noStyle
|
||||
rules={[{required: true, message: i18next.t("login:Please input your Email or Phone!")}]}
|
||||
rules={[
|
||||
{
|
||||
required: false,
|
||||
message: i18next.t("signup:Please select your country code!"),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Input
|
||||
style={{width: isEmail() ? "100% " : "70%"}}
|
||||
onChange={(e) => {setDest(e.target.value);}}
|
||||
prefix={<UserOutlined />}
|
||||
placeholder={isEmail() ? i18next.t("general:Email") : i18next.t("general:Phone")}
|
||||
<CountryCodeSelect
|
||||
style={{width: "30%"}}
|
||||
countryCodes={application.organizationObj.countryCodes}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Input.Group>
|
||||
</React.Fragment>
|
||||
)
|
||||
}
|
||||
<Form.Item
|
||||
name="dest"
|
||||
noStyle
|
||||
rules={[{required: true, message: i18next.t("login:Please input your Email or Phone!")}]}
|
||||
>
|
||||
<Input
|
||||
style={{width: Setting.IsEmail(dest) ? "100% " : "70%"}}
|
||||
onChange={(e) => {setDest(e.target.value);}}
|
||||
prefix={<UserOutlined />}
|
||||
placeholder={i18next.t("general:Phone or email")}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Input.Group>
|
||||
}
|
||||
<Form.Item
|
||||
name="passcode"
|
||||
@@ -101,8 +73,8 @@ export const MfaSmsVerifyForm = ({mfaProps, application, onFinish, method, user}
|
||||
>
|
||||
<SendCodeInput
|
||||
countryCode={form.getFieldValue("countryCode")}
|
||||
method={method}
|
||||
onButtonClickArgs={[mfaProps.secret || dest, isEmail() ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
method={mfaProps?.id === undefined ? "mfaSetup" : "mfaAuth"}
|
||||
onButtonClickArgs={[dest, Setting.IsEmail(dest) ? "email" : "phone", Setting.getApplicationName(application)]}
|
||||
application={application}
|
||||
/>
|
||||
</Form.Item>
|
||||
@@ -124,49 +96,44 @@ export const MfaSmsVerifyForm = ({mfaProps, application, onFinish, method, user}
|
||||
export const MfaTotpVerifyForm = ({mfaProps, onFinish}) => {
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const renderSecret = () => {
|
||||
if (!mfaProps.secret) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<React.Fragment>
|
||||
<Col span={24} style={{display: "flex", justifyContent: "center"}}>
|
||||
<QRCode
|
||||
errorLevel="H"
|
||||
value={mfaProps.url}
|
||||
icon={"https://cdn.casdoor.com/static/favicon.png"}
|
||||
/>
|
||||
</Col>
|
||||
<p style={{textAlign: "center"}}>{i18next.t("mfa:Scan the QR code with your Authenticator App")}</p>
|
||||
<p style={{textAlign: "center"}}>{i18next.t("mfa:Or copy the secret to your Authenticator App")}</p>
|
||||
<Col span={24}>
|
||||
<Space>
|
||||
<Input value={mfaProps.secret} />
|
||||
<Button
|
||||
type="primary"
|
||||
shape="round"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => {
|
||||
copy(`${mfaProps.secret}`);
|
||||
Setting.showMessage(
|
||||
"success",
|
||||
i18next.t("mfa:Multi-factor secret to clipboard successfully")
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Space>
|
||||
</Col>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
return (
|
||||
<Form
|
||||
form={form}
|
||||
style={{width: "300px"}}
|
||||
onFinish={onFinish}
|
||||
>
|
||||
{renderSecret()}
|
||||
<Row type="flex" justify="center" align="middle">
|
||||
<Col>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Row type="flex" justify="center" align="middle">
|
||||
<Col>
|
||||
{Setting.getLabel(
|
||||
i18next.t("mfa:Multi-factor secret"),
|
||||
i18next.t("mfa:Multi-factor secret - Tooltip")
|
||||
)}
|
||||
:
|
||||
</Col>
|
||||
<Col>
|
||||
<Input value={mfaProps.secret} />
|
||||
</Col>
|
||||
<Col>
|
||||
<Button
|
||||
type="primary"
|
||||
shape="round"
|
||||
icon={<CopyOutlined />}
|
||||
onClick={() => {
|
||||
copy(`${mfaProps.secret}`);
|
||||
Setting.showMessage(
|
||||
"success",
|
||||
i18next.t("mfa:Multi-factor secret to clipboard successfully")
|
||||
);
|
||||
}}
|
||||
/>
|
||||
</Col>
|
||||
</Row>
|
||||
|
||||
<Form.Item
|
||||
name="passcode"
|
||||
rules={[{required: true, message: "Please input your passcode"}]}
|
||||
@@ -177,6 +144,7 @@ export const MfaTotpVerifyForm = ({mfaProps, onFinish}) => {
|
||||
placeholder={i18next.t("mfa:Passcode")}
|
||||
/>
|
||||
</Form.Item>
|
||||
|
||||
<Form.Item>
|
||||
<Button
|
||||
style={{marginTop: 24}}
|
||||
|
@@ -28,13 +28,14 @@ import MfaSetupPage from "./MfaSetupPage";
|
||||
class PromptPage extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
const params = new URLSearchParams(this.props.location.search);
|
||||
this.state = {
|
||||
classes: props,
|
||||
type: props.type,
|
||||
applicationName: props.applicationName ?? (props.match === undefined ? null : props.match.params.applicationName),
|
||||
application: null,
|
||||
user: null,
|
||||
promptType: new URLSearchParams(this.props.location.search).get("promptType"),
|
||||
promptType: params.get("promptType"),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -49,14 +50,9 @@ class PromptPage extends React.Component {
|
||||
const organizationName = this.props.account.owner;
|
||||
const userName = this.props.account.name;
|
||||
UserBackend.getUser(organizationName, userName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
.then((user) => {
|
||||
this.setState({
|
||||
user: res,
|
||||
user: user,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -67,15 +63,10 @@ class PromptPage extends React.Component {
|
||||
}
|
||||
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.onUpdateApplication(res);
|
||||
.then((application) => {
|
||||
this.onUpdateApplication(application);
|
||||
this.setState({
|
||||
application: res,
|
||||
application: application,
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -242,22 +233,19 @@ class PromptPage extends React.Component {
|
||||
{this.renderContent(application)}
|
||||
<div style={{marginTop: "50px"}}>
|
||||
<Button disabled={!Setting.isPromptAnswered(this.state.user, application)} type="primary" size="large" onClick={() => {this.submitUserEdit(true);}}>{i18next.t("code:Submit and complete")}</Button>
|
||||
</div>
|
||||
</div>;
|
||||
</>;
|
||||
}
|
||||
|
||||
renderPromptMfa() {
|
||||
return (
|
||||
<MfaSetupPage
|
||||
application={this.getApplicationObj()}
|
||||
account={this.props.account}
|
||||
current={1}
|
||||
isAuthenticated={true}
|
||||
isPromptPage={true}
|
||||
redirectUri={this.getRedirectUrl()}
|
||||
{...this.props}
|
||||
/>
|
||||
);
|
||||
return <MfaSetupPage
|
||||
applicationName={this.getApplicationObj().name}
|
||||
account={this.props.account}
|
||||
current={1}
|
||||
isAuthenticated={true}
|
||||
isPromptPage={true}
|
||||
redirectUri={this.getRedirectUrl()}
|
||||
/>;
|
||||
}
|
||||
|
||||
render() {
|
||||
|
@@ -43,14 +43,10 @@ class ResultPage extends React.Component {
|
||||
}
|
||||
|
||||
ApplicationBackend.getApplication("admin", this.state.applicationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
this.onUpdateApplication(res);
|
||||
.then((application) => {
|
||||
this.onUpdateApplication(application);
|
||||
this.setState({
|
||||
application: res,
|
||||
application: application,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
@@ -108,13 +108,8 @@ class SignupPage extends React.Component {
|
||||
}
|
||||
|
||||
ApplicationBackend.getApplication("admin", applicationName)
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
return;
|
||||
}
|
||||
|
||||
this.onUpdateApplication(res);
|
||||
.then((application) => {
|
||||
this.onUpdateApplication(application);
|
||||
});
|
||||
}
|
||||
|
||||
|
@@ -18,7 +18,7 @@ export function MfaSetupInitiate(values) {
|
||||
const formData = new FormData();
|
||||
formData.append("owner", values.owner);
|
||||
formData.append("name", values.name);
|
||||
formData.append("mfaType", values.mfaType);
|
||||
formData.append("type", values.type);
|
||||
return fetch(`${Setting.ServerUrl}/api/mfa/setup/initiate`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
@@ -30,7 +30,7 @@ export function MfaSetupVerify(values) {
|
||||
const formData = new FormData();
|
||||
formData.append("owner", values.owner);
|
||||
formData.append("name", values.name);
|
||||
formData.append("mfaType", values.mfaType);
|
||||
formData.append("type", values.type);
|
||||
formData.append("passcode", values.passcode);
|
||||
return fetch(`${Setting.ServerUrl}/api/mfa/setup/verify`, {
|
||||
method: "POST",
|
||||
@@ -41,7 +41,7 @@ export function MfaSetupVerify(values) {
|
||||
|
||||
export function MfaSetupEnable(values) {
|
||||
const formData = new FormData();
|
||||
formData.append("mfaType", values.mfaType);
|
||||
formData.append("type", values.type);
|
||||
formData.append("owner", values.owner);
|
||||
formData.append("name", values.name);
|
||||
return fetch(`${Setting.ServerUrl}/api/mfa/setup/enable`, {
|
||||
@@ -53,6 +53,7 @@ export function MfaSetupEnable(values) {
|
||||
|
||||
export function DeleteMfa(values) {
|
||||
const formData = new FormData();
|
||||
formData.append("id", values.id);
|
||||
formData.append("owner", values.owner);
|
||||
formData.append("name", values.name);
|
||||
return fetch(`${Setting.ServerUrl}/api/delete-mfa`, {
|
||||
@@ -64,7 +65,7 @@ export function DeleteMfa(values) {
|
||||
|
||||
export function SetPreferredMfa(values) {
|
||||
const formData = new FormData();
|
||||
formData.append("mfaType", values.mfaType);
|
||||
formData.append("id", values.id);
|
||||
formData.append("owner", values.owner);
|
||||
formData.append("name", values.name);
|
||||
return fetch(`${Setting.ServerUrl}/api/set-preferred-mfa`, {
|
||||
|
@@ -25,8 +25,8 @@ export function getGlobalUsers(page, pageSize, field = "", value = "", sortField
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function getUsers(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "", groupName = "") {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-users?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}&groupName=${groupName}`, {
|
||||
export function getUsers(owner, page = "", pageSize = "", field = "", value = "", sortField = "", sortOrder = "", groupId = "") {
|
||||
return fetch(`${Setting.ServerUrl}/api/get-users?owner=${owner}&p=${page}&pageSize=${pageSize}&field=${field}&value=${value}&sortField=${sortField}&sortOrder=${sortOrder}&groupId=${groupId}`, {
|
||||
method: "GET",
|
||||
credentials: "include",
|
||||
headers: {
|
||||
@@ -223,11 +223,11 @@ export function checkUserPassword(values) {
|
||||
}).then(res => res.json());
|
||||
}
|
||||
|
||||
export function removeUserFromGroup({owner, name, groupName}) {
|
||||
export function removeUserFromGroup({owner, name, groupId}) {
|
||||
const formData = new FormData();
|
||||
formData.append("owner", owner);
|
||||
formData.append("name", name);
|
||||
formData.append("groupName", groupName);
|
||||
formData.append("groupId", groupId);
|
||||
return fetch(`${Setting.ServerUrl}/api/remove-user-from-group`, {
|
||||
method: "POST",
|
||||
credentials: "include",
|
||||
|
@@ -127,11 +127,6 @@ export const CropperDivModal = (props) => {
|
||||
setLoading(true);
|
||||
ResourceBackend.getResources(user.owner, user.name, "", "", "", "", "", "")
|
||||
.then((res) => {
|
||||
if (res.status === "error") {
|
||||
Setting.showMessage("error", res.msg);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
setLoading(false);
|
||||
setOptions(getOptions(res));
|
||||
});
|
||||
|
@@ -17,6 +17,7 @@ import i18next from "i18next";
|
||||
import React from "react";
|
||||
import * as UserBackend from "../../backend/UserBackend";
|
||||
import * as Setting from "../../Setting";
|
||||
import * as OrganizationBackend from "../../backend/OrganizationBackend";
|
||||
import * as PasswordChecker from "../PasswordChecker";
|
||||
|
||||
export const PasswordModal = (props) => {
|
||||
@@ -26,7 +27,6 @@ export const PasswordModal = (props) => {
|
||||
const [newPassword, setNewPassword] = React.useState("");
|
||||
const [rePassword, setRePassword] = React.useState("");
|
||||
const {user} = props;
|
||||
const {organization} = props;
|
||||
const {account} = props;
|
||||
|
||||
const [passwordOptions, setPasswordOptions] = React.useState([]);
|
||||
@@ -36,9 +36,18 @@ export const PasswordModal = (props) => {
|
||||
const [rePasswordErrorMessage, setRePasswordErrorMessage] = React.useState("");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (organization) {
|
||||
setPasswordOptions(organization.passwordOptions);
|
||||
}
|
||||
OrganizationBackend.getOrganizations("admin")
|
||||
.then((res) => {
|
||||
const organizations = (res.msg === undefined) ? res : [];
|
||||
// Find the user's corresponding organization
|
||||
const organization = organizations.find((org) => org.name === user.owner);
|
||||
if (organization) {
|
||||
setPasswordOptions(organization.passwordOptions);
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
Setting.showMessage("error", `${i18next.t("general:Failed to connect to server")}: ${error}`);
|
||||
});
|
||||
}, [user.owner]);
|
||||
const showModal = () => {
|
||||
setVisible(true);
|
||||
@@ -77,31 +86,44 @@ export const PasswordModal = (props) => {
|
||||
}
|
||||
setConfirmLoading(true);
|
||||
|
||||
if (organization === null) {
|
||||
Setting.showMessage("error", "organization is null");
|
||||
setConfirmLoading(false);
|
||||
return;
|
||||
}
|
||||
OrganizationBackend.getOrganizations("admin").then((res) => {
|
||||
const organizations = (res.msg === undefined) ? res : [];
|
||||
|
||||
const errorMsg = PasswordChecker.checkPasswordComplexity(newPassword, organization.passwordOptions);
|
||||
if (errorMsg !== "") {
|
||||
Setting.showMessage("error", errorMsg);
|
||||
setConfirmLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
UserBackend.setPassword(user.owner, user.name, oldPassword, newPassword)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("user:Password set successfully"));
|
||||
setVisible(false);
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t(`user:${res.msg}`));
|
||||
// find the users' corresponding organization
|
||||
let organization = null;
|
||||
for (let i = 0; i < organizations.length; i++) {
|
||||
if (organizations[i].name === user.owner) {
|
||||
organization = organizations[i];
|
||||
break;
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
}
|
||||
|
||||
if (organization === null) {
|
||||
Setting.showMessage("error", "organization is null");
|
||||
setConfirmLoading(false);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const errorMsg = PasswordChecker.checkPasswordComplexity(newPassword, organization.passwordOptions);
|
||||
if (errorMsg !== "") {
|
||||
Setting.showMessage("error", errorMsg);
|
||||
setConfirmLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
UserBackend.setPassword(user.owner, user.name, oldPassword, newPassword)
|
||||
.then((res) => {
|
||||
if (res.status === "ok") {
|
||||
Setting.showMessage("success", i18next.t("user:Password set successfully"));
|
||||
setVisible(false);
|
||||
} else {
|
||||
Setting.showMessage("error", i18next.t(`user:${res.msg}`));
|
||||
}
|
||||
})
|
||||
.finally(() => {
|
||||
setConfirmLoading(false);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const hasOldPassword = user.password !== "";
|
||||
|
@@ -17,17 +17,13 @@ import * as Setting from "../../Setting";
|
||||
import React from "react";
|
||||
|
||||
export const CountryCodeSelect = (props) => {
|
||||
const {onChange, style, disabled, initValue} = props;
|
||||
const {onChange, style, disabled} = props;
|
||||
const countryCodes = props.countryCodes ?? [];
|
||||
const [value, setValue] = React.useState("");
|
||||
|
||||
React.useEffect(() => {
|
||||
if (initValue !== undefined) {
|
||||
setValue(initValue);
|
||||
} else {
|
||||
const initValue = countryCodes.length > 0 ? countryCodes[0] : "";
|
||||
handleOnChange(initValue);
|
||||
}
|
||||
const initValue = countryCodes.length > 0 ? countryCodes[0] : "";
|
||||
handleOnChange(initValue);
|
||||
}, []);
|
||||
|
||||
const handleOnChange = (value) => {
|
||||
|
@@ -202,7 +202,6 @@
|
||||
"Delete": "Löschen",
|
||||
"Description": "Beschreibung",
|
||||
"Description - Tooltip": "Detaillierte Beschreibungsinformationen zur Referenz, Casdoor selbst wird es nicht verwenden",
|
||||
"Disable": "Disable",
|
||||
"Display name": "Anzeigename",
|
||||
"Display name - Tooltip": "Ein benutzerfreundlicher, leicht lesbarer Name, der öffentlich in der Benutzeroberfläche angezeigt wird",
|
||||
"Down": "Nach unten",
|
||||
@@ -273,6 +272,7 @@
|
||||
"Permissions - Tooltip": "Berechtigungen, die diesem Benutzer gehören",
|
||||
"Phone": "Telefon",
|
||||
"Phone - Tooltip": "Telefonnummer",
|
||||
"Phone or email": "Phone or email",
|
||||
"Plan": "Plan",
|
||||
"Plan - Tooltip": "Plan - Tooltip",
|
||||
"Plans": "Pläne",
|
||||
@@ -316,7 +316,6 @@
|
||||
"Supported country codes": "Unterstützte Ländercodes",
|
||||
"Supported country codes - Tooltip": "Ländercodes, die von der Organisation unterstützt werden. Diese Codes können als Präfix ausgewählt werden, wenn SMS-Verifizierungscodes gesendet werden",
|
||||
"Sure to delete": "Sicher zu löschen",
|
||||
"Sure to disable": "Sure to disable",
|
||||
"Sure to remove": "Sure to remove",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Synchronisieren",
|
||||
@@ -434,20 +433,16 @@
|
||||
"Multi-factor methods": "Multi-factor methods",
|
||||
"Multi-factor recover": "Multi-factor recover",
|
||||
"Multi-factor recover description": "Multi-factor recover description",
|
||||
"Multi-factor secret": "Multi-factor secret",
|
||||
"Multi-factor secret - Tooltip": "Multi-factor secret - Tooltip",
|
||||
"Multi-factor secret to clipboard successfully": "Multi-factor secret to clipboard successfully",
|
||||
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
|
||||
"Passcode": "Passcode",
|
||||
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
|
||||
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
|
||||
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
|
||||
"Recovery code": "Recovery code",
|
||||
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
|
||||
"SMS/Email message": "SMS/Email message",
|
||||
"Set preferred": "Set preferred",
|
||||
"Setup": "Setup",
|
||||
"Use Authenticator App": "Use Authenticator App",
|
||||
"Use Email": "Use Email",
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
@@ -569,7 +564,6 @@
|
||||
"Copy pricing page URL": "Preisseite URL kopieren",
|
||||
"Edit Pricing": "Edit Pricing",
|
||||
"Free": "Kostenlos",
|
||||
"Failed to get plans": "Es konnten keine Pläne abgerufen werden",
|
||||
"Getting started": "Loslegen",
|
||||
"New Pricing": "New Pricing",
|
||||
"Trial duration": "Testphase Dauer",
|
||||
@@ -821,8 +815,6 @@
|
||||
"Error text": "Fehlermeldung",
|
||||
"Error text - Tooltip": "Fehler Text",
|
||||
"Is hashed": "ist gehasht",
|
||||
"Is read-only": "Is read-only",
|
||||
"Is read-only - Tooltip": "Is read-only - Tooltip",
|
||||
"New Syncer": "Neuer Syncer",
|
||||
"Sync interval": "Synchronisierungsintervall",
|
||||
"Sync interval - Tooltip": "Einheit in Sekunden",
|
||||
|
@@ -202,7 +202,6 @@
|
||||
"Delete": "Delete",
|
||||
"Description": "Description",
|
||||
"Description - Tooltip": "Detailed description information for reference, Casdoor itself will not use it",
|
||||
"Disable": "Disable",
|
||||
"Display name": "Display name",
|
||||
"Display name - Tooltip": "A user-friendly, easily readable name displayed publicly in the UI",
|
||||
"Down": "Down",
|
||||
@@ -273,6 +272,7 @@
|
||||
"Permissions - Tooltip": "Permissions owned by this user",
|
||||
"Phone": "Phone",
|
||||
"Phone - Tooltip": "Phone number",
|
||||
"Phone or email": "Phone or email",
|
||||
"Plan": "Plan",
|
||||
"Plan - Tooltip": "Plan - Tooltip",
|
||||
"Plans": "Plans",
|
||||
@@ -316,7 +316,6 @@
|
||||
"Supported country codes": "Supported country codes",
|
||||
"Supported country codes - Tooltip": "Country codes supported by the organization. These codes can be selected as a prefix when sending SMS verification codes",
|
||||
"Sure to delete": "Sure to delete",
|
||||
"Sure to disable": "Sure to disable",
|
||||
"Sure to remove": "Sure to remove",
|
||||
"Swagger": "Swagger",
|
||||
"Sync": "Sync",
|
||||
@@ -434,20 +433,16 @@
|
||||
"Multi-factor methods": "Multi-factor methods",
|
||||
"Multi-factor recover": "Multi-factor recover",
|
||||
"Multi-factor recover description": "If you are unable to access your device, enter your recovery code to verify your identity",
|
||||
"Multi-factor secret": "Multi-factor secret",
|
||||
"Multi-factor secret - Tooltip": "Multi-factor secret - Tooltip",
|
||||
"Multi-factor secret to clipboard successfully": "Multi-factor secret to clipboard successfully",
|
||||
"Or copy the secret to your Authenticator App": "Or copy the secret to your Authenticator App",
|
||||
"Passcode": "Passcode",
|
||||
"Please bind your email first, the system will automatically uses the mail for multi-factor authentication": "Please bind your email first, the system will automatically uses the mail for multi-factor authentication",
|
||||
"Please bind your phone first, the system automatically uses the phone for multi-factor authentication": "Please bind your phone first, the system automatically uses the phone for multi-factor authentication",
|
||||
"Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code": "Please save this recovery code. Once your device cannot provide an authentication code, you can reset mfa authentication by this recovery code",
|
||||
"Protect your account with Multi-factor authentication": "Protect your account with Multi-factor authentication",
|
||||
"Recovery code": "Recovery code",
|
||||
"Scan the QR code with your Authenticator App": "Scan the QR code with your Authenticator App",
|
||||
"SMS/Email message": "SMS/Email message",
|
||||
"Set preferred": "Set preferred",
|
||||
"Setup": "Setup",
|
||||
"Use Authenticator App": "Use Authenticator App",
|
||||
"Use Email": "Use Email",
|
||||
"Use SMS": "Use SMS",
|
||||
"Use SMS verification code": "Use SMS verification code",
|
||||
"Use a recovery code": "Use a recovery code",
|
||||
"Verification failed": "Verification failed",
|
||||
@@ -569,7 +564,6 @@
|
||||
"Copy pricing page URL": "Copy pricing page URL",
|
||||
"Edit Pricing": "Edit Pricing",
|
||||
"Free": "Free",
|
||||
"Failed to get plans": "Failed to get plans",
|
||||
"Getting started": "Getting started",
|
||||
"New Pricing": "New Pricing",
|
||||
"Trial duration": "Trial duration",
|
||||
@@ -821,8 +815,6 @@
|
||||
"Error text": "Error text",
|
||||
"Error text - Tooltip": "Error text",
|
||||
"Is hashed": "Is hashed",
|
||||
"Is read-only": "Is read-only",
|
||||
"Is read-only - Tooltip": "Is read-only - Tooltip",
|
||||
"New Syncer": "New Syncer",
|
||||
"Sync interval": "Sync interval",
|
||||
"Sync interval - Tooltip": "Unit in seconds",
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user