Compare commits

..

1 Commits

Author SHA1 Message Date
93e9975acc Revert "feat: fix Beego session delete concurrent issue (#3103)"
This reverts commit f21aa9c0d2.
2024-08-07 16:51:29 +08:00
156 changed files with 1851 additions and 4959 deletions

View File

@ -114,12 +114,12 @@ jobs:
wait-on-timeout: 210 wait-on-timeout: 210
working-directory: ./web working-directory: ./web
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
if: failure() if: failure()
with: with:
name: cypress-screenshots name: cypress-screenshots
path: ./web/cypress/screenshots path: ./web/cypress/screenshots
- uses: actions/upload-artifact@v4 - uses: actions/upload-artifact@v3
if: always() if: always()
with: with:
name: cypress-videos name: cypress-videos
@ -147,7 +147,7 @@ jobs:
- name: Release - name: Release
run: yarn global add semantic-release@17.4.4 && semantic-release run: yarn global add semantic-release@17.4.4 && semantic-release
env: env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} GH_TOKEN: ${{ secrets.GH_BOT_TOKEN }}
- name: Fetch Current version - name: Fetch Current version
id: get-current-tag id: get-current-tag

View File

@ -13,7 +13,7 @@
<a href="https://github.com/casdoor/casdoor/releases/latest"> <a href="https://github.com/casdoor/casdoor/releases/latest">
<img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg"> <img alt="GitHub Release" src="https://img.shields.io/github/v/release/casdoor/casdoor.svg">
</a> </a>
<a href="https://hub.docker.com/r/casbin/casdoor"> <a href="https://hub.docker.com/repository/docker/casbin/casdoor">
<img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen"> <img alt="Docker Image Version (latest semver)" src="https://img.shields.io/badge/Docker%20Hub-latest-brightgreen">
</a> </a>
</p> </p>

View File

@ -77,7 +77,6 @@ p, *, *, POST, /api/verify-code, *, *
p, *, *, POST, /api/reset-email-or-phone, *, * p, *, *, POST, /api/reset-email-or-phone, *, *
p, *, *, POST, /api/upload-resource, *, * p, *, *, POST, /api/upload-resource, *, *
p, *, *, GET, /.well-known/openid-configuration, *, * p, *, *, GET, /.well-known/openid-configuration, *, *
p, *, *, GET, /.well-known/webfinger, *, *
p, *, *, *, /.well-known/jwks, *, * p, *, *, *, /.well-known/jwks, *, *
p, *, *, GET, /api/get-saml-login, *, * p, *, *, GET, /api/get-saml-login, *, *
p, *, *, POST, /api/acs, *, * p, *, *, POST, /api/acs, *, *
@ -98,7 +97,6 @@ p, *, *, GET, /api/get-organization-names, *, *
p, *, *, GET, /api/get-all-objects, *, * p, *, *, GET, /api/get-all-objects, *, *
p, *, *, GET, /api/get-all-actions, *, * p, *, *, GET, /api/get-all-actions, *, *
p, *, *, GET, /api/get-all-roles, *, * p, *, *, GET, /api/get-all-roles, *, *
p, *, *, GET, /api/run-casbin-command, *, *
p, *, *, GET, /api/get-invitation-info, *, * p, *, *, GET, /api/get-invitation-info, *, *
p, *, *, GET, /api/faceid-signin-begin, *, * p, *, *, GET, /api/faceid-signin-begin, *, *
` `

View File

@ -26,10 +26,6 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
return NewDefaultCaptchaProvider() return NewDefaultCaptchaProvider()
case "reCAPTCHA": case "reCAPTCHA":
return NewReCaptchaProvider() return NewReCaptchaProvider()
case "reCAPTCHA v2":
return NewReCaptchaProvider()
case "reCAPTCHA v3":
return NewReCaptchaProvider()
case "Aliyun Captcha": case "Aliyun Captcha":
return NewAliyunCaptchaProvider() return NewAliyunCaptchaProvider()
case "hCaptcha": case "hCaptcha":

View File

@ -21,14 +21,11 @@ originFrontend =
staticBaseUrl = "https://cdn.casbin.org" staticBaseUrl = "https://cdn.casbin.org"
isDemoMode = false isDemoMode = false
batchSize = 100 batchSize = 100
enableErrorMask = false
enableGzip = true enableGzip = true
inactiveTimeoutMinutes =
ldapServerPort = 389 ldapServerPort = 389
radiusServerPort = 1812 radiusServerPort = 1812
radiusSecret = "secret" radiusSecret = "secret"
quota = {"organization": -1, "user": -1, "application": -1, "provider": -1} quota = {"organization": -1, "user": -1, "application": -1, "provider": -1}
logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"} logConfig = {"filename": "logs/casdoor.log", "maxdays":99999, "perm":"0770"}
initDataNewOnly = false
initDataFile = "./init_data.json" initDataFile = "./init_data.json"
frontendBaseDir = "../cc_0" frontendBaseDir = "../casdoor"

View File

@ -116,13 +116,6 @@ func (c *ApiController) Signup() {
return return
} }
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
err = object.CheckEntryIp(clientIp, nil, application, organization, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
msg := object.CheckUserSignup(application, organization, &authForm, c.GetAcceptLanguage()) msg := object.CheckUserSignup(application, organization, &authForm, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
@ -207,10 +200,6 @@ func (c *ApiController) Signup() {
Type: userType, Type: userType,
Password: authForm.Password, Password: authForm.Password,
DisplayName: authForm.Name, DisplayName: authForm.Name,
Gender: authForm.Gender,
Bio: authForm.Bio,
Tag: authForm.Tag,
Education: authForm.Education,
Avatar: organization.DefaultAvatar, Avatar: organization.DefaultAvatar,
Email: authForm.Email, Email: authForm.Email,
Phone: authForm.Phone, Phone: authForm.Phone,
@ -245,10 +234,6 @@ func (c *ApiController) Signup() {
} }
} }
if invitation != nil && invitation.SignupGroup != "" {
user.Groups = []string{invitation.SignupGroup}
}
affected, err := object.AddUser(user) affected, err := object.AddUser(user)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())

View File

@ -110,9 +110,6 @@ func (c *ApiController) GetApplication() {
} }
} }
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
object.CheckEntryIp(clientIp, nil, application, nil, c.GetAcceptLanguage())
c.ResponseOk(object.GetMaskedApplication(application, userId)) c.ResponseOk(object.GetMaskedApplication(application, userId))
} }
@ -232,11 +229,6 @@ func (c *ApiController) UpdateApplication() {
return return
} }
if err = object.CheckIpWhitelist(application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application)) c.Data["json"] = wrapActionResponse(object.UpdateApplication(id, &application))
c.ServeJSON() c.ServeJSON()
} }
@ -267,11 +259,6 @@ func (c *ApiController) AddApplication() {
return return
} }
if err = object.CheckIpWhitelist(application.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddApplication(&application)) c.Data["json"] = wrapActionResponse(object.AddApplication(&application))
c.ServeJSON() c.ServeJSON()
} }

View File

@ -55,13 +55,6 @@ func tokenToResponse(token *object.Token) *Response {
func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) { func (c *ApiController) HandleLoggedIn(application *object.Application, user *object.User, form *form.AuthForm) (resp *Response) {
userId := user.GetId() userId := user.GetId()
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
err := object.CheckEntryIp(clientIp, user, application, application.OrganizationObj, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
allowed, err := object.CheckLoginPermission(userId, application) allowed, err := object.CheckLoginPermission(userId, application)
if err != nil { if err != nil {
c.ResponseError(err.Error(), nil) c.ResponseError(err.Error(), nil)
@ -263,9 +256,6 @@ func (c *ApiController) GetApplicationLogin() {
} }
} }
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
object.CheckEntryIp(clientIp, nil, application, nil, c.GetAcceptLanguage())
application = object.GetMaskedApplication(application, "") application = object.GetMaskedApplication(application, "")
if msg != "" { if msg != "" {
c.ResponseError(msg, application) c.ResponseError(msg, application)
@ -473,15 +463,6 @@ func (c *ApiController) Login() {
} }
password := authForm.Password password := authForm.Password
if application.OrganizationObj != nil {
password, err = util.GetUnobfuscatedPassword(application.OrganizationObj.PasswordObfuscatorType, application.OrganizationObj.PasswordObfuscatorKey, authForm.Password)
if err != nil {
c.ResponseError(err.Error())
return
}
}
isSigninViaLdap := authForm.SigninMethod == "LDAP" isSigninViaLdap := authForm.SigninMethod == "LDAP"
var isPasswordWithLdapEnabled bool var isPasswordWithLdapEnabled bool
if authForm.SigninMethod == "Password" { if authForm.SigninMethod == "Password" {
@ -854,7 +835,6 @@ func (c *ApiController) Login() {
} }
if authForm.Passcode != "" { if authForm.Passcode != "" {
user.CountryCode = user.GetCountryCode(user.CountryCode)
mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false)) mfaUtil := object.GetMfaUtil(authForm.MfaType, user.GetPreferredMfaProps(false))
if mfaUtil == nil { if mfaUtil == nil {
c.ResponseError("Invalid multi-factor authentication type") c.ResponseError("Invalid multi-factor authentication type")

View File

@ -1,114 +0,0 @@
// Copyright 2024 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 controllers
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strings"
)
func processArgsToTempFiles(args []string) ([]string, []string, error) {
tempFiles := []string{}
newArgs := []string{}
for i := 0; i < len(args); i++ {
if (args[i] == "-m" || args[i] == "-p") && i+1 < len(args) {
pattern := fmt.Sprintf("casbin_temp_%s_*.conf", args[i])
tempFile, err := os.CreateTemp("", pattern)
if err != nil {
return nil, nil, fmt.Errorf("failed to create temp file: %v", err)
}
_, err = tempFile.WriteString(args[i+1])
if err != nil {
tempFile.Close()
return nil, nil, fmt.Errorf("failed to write to temp file: %v", err)
}
tempFile.Close()
tempFiles = append(tempFiles, tempFile.Name())
newArgs = append(newArgs, args[i], tempFile.Name())
i++
} else {
newArgs = append(newArgs, args[i])
}
}
return tempFiles, newArgs, nil
}
// RunCasbinCommand
// @Title RunCasbinCommand
// @Tag Enforcer API
// @Description Call Casbin CLI commands
// @Success 200 {object} controllers.Response The Response object
// @router /run-casbin-command [get]
func (c *ApiController) RunCasbinCommand() {
language := c.Input().Get("language")
argString := c.Input().Get("args")
if language == "" {
language = "go"
}
// use "casbin-go-cli" by default, can be also "casbin-java-cli", "casbin-node-cli", etc.
// the pre-built binary of "casbin-go-cli" can be found at: https://github.com/casbin/casbin-go-cli/releases
binaryName := fmt.Sprintf("casbin-%s-cli", language)
_, err := exec.LookPath(binaryName)
if err != nil {
c.ResponseError(fmt.Sprintf("executable file: %s not found in PATH", binaryName))
return
}
// RBAC model & policy example:
// https://door.casdoor.com/api/run-casbin-command?language=go&args=["enforce", "-m", "[request_definition]\nr = sub, obj, act\n\n[policy_definition]\np = sub, obj, act\n\n[role_definition]\ng = _, _\n\n[policy_effect]\ne = some(where (p.eft == allow))\n\n[matchers]\nm = g(r.sub, p.sub) %26%26 r.obj == p.obj %26%26 r.act == p.act", "-p", "p, alice, data1, read\np, bob, data2, write\np, data2_admin, data2, read\np, data2_admin, data2, write\ng, alice, data2_admin", "alice", "data1", "read"]
// Casbin CLI usage:
// https://github.com/jcasbin/casbin-java-cli?tab=readme-ov-file#get-started
var args []string
err = json.Unmarshal([]byte(argString), &args)
if err != nil {
c.ResponseError(err.Error())
return
}
tempFiles, processedArgs, err := processArgsToTempFiles(args)
defer func() {
for _, file := range tempFiles {
os.Remove(file)
}
}()
if err != nil {
c.ResponseError(err.Error())
return
}
command := exec.Command(binaryName, processedArgs...)
outputBytes, err := command.CombinedOutput()
if err != nil {
errorString := err.Error()
if outputBytes != nil {
output := string(outputBytes)
errorString = fmt.Sprintf("%s, error: %s", output, err.Error())
}
c.ResponseError(errorString)
return
}
output := string(outputBytes)
output = strings.TrimSuffix(output, "\n")
c.ResponseOk(output)
}

View File

@ -60,6 +60,7 @@ func (c *ApiController) Unlink() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
if application == nil { if application == nil {
c.ResponseError(c.T("link:You can't unlink yourself, you are not a member of any application")) c.ResponseError(c.T("link:You can't unlink yourself, you are not a member of any application"))
return return

View File

@ -22,6 +22,13 @@ import (
"github.com/google/uuid" "github.com/google/uuid"
) )
const (
MfaRecoveryCodesSession = "mfa_recovery_codes"
MfaCountryCodeSession = "mfa_country_code"
MfaDestSession = "mfa_dest"
MfaTotpSecretSession = "mfa_totp_secret"
)
// MfaSetupInitiate // MfaSetupInitiate
// @Title MfaSetupInitiate // @Title MfaSetupInitiate
// @Tag MFA API // @Tag MFA API
@ -65,6 +72,11 @@ func (c *ApiController) MfaSetupInitiate() {
} }
recoveryCode := uuid.NewString() recoveryCode := uuid.NewString()
c.SetSession(MfaRecoveryCodesSession, recoveryCode)
if mfaType == object.TotpType {
c.SetSession(MfaTotpSecretSession, mfaProps.Secret)
}
mfaProps.RecoveryCodes = []string{recoveryCode} mfaProps.RecoveryCodes = []string{recoveryCode}
resp := mfaProps resp := mfaProps
@ -82,9 +94,6 @@ func (c *ApiController) MfaSetupInitiate() {
func (c *ApiController) MfaSetupVerify() { func (c *ApiController) MfaSetupVerify() {
mfaType := c.Ctx.Request.Form.Get("mfaType") mfaType := c.Ctx.Request.Form.Get("mfaType")
passcode := c.Ctx.Request.Form.Get("passcode") passcode := c.Ctx.Request.Form.Get("passcode")
secret := c.Ctx.Request.Form.Get("secret")
dest := c.Ctx.Request.Form.Get("dest")
countryCode := c.Ctx.Request.Form.Get("countryCode")
if mfaType == "" || passcode == "" { if mfaType == "" || passcode == "" {
c.ResponseError("missing auth type or passcode") c.ResponseError("missing auth type or passcode")
@ -95,28 +104,32 @@ func (c *ApiController) MfaSetupVerify() {
MfaType: mfaType, MfaType: mfaType,
} }
if mfaType == object.TotpType { if mfaType == object.TotpType {
if secret == "" { secret := c.GetSession(MfaTotpSecretSession)
if secret == nil {
c.ResponseError("totp secret is missing") c.ResponseError("totp secret is missing")
return return
} }
config.Secret = secret config.Secret = secret.(string)
} else if mfaType == object.SmsType { } else if mfaType == object.SmsType {
if dest == "" { dest := c.GetSession(MfaDestSession)
if dest == nil {
c.ResponseError("destination is missing") c.ResponseError("destination is missing")
return return
} }
config.Secret = dest config.Secret = dest.(string)
if countryCode == "" { countryCode := c.GetSession(MfaCountryCodeSession)
if countryCode == nil {
c.ResponseError("country code is missing") c.ResponseError("country code is missing")
return return
} }
config.CountryCode = countryCode config.CountryCode = countryCode.(string)
} else if mfaType == object.EmailType { } else if mfaType == object.EmailType {
if dest == "" { dest := c.GetSession(MfaDestSession)
if dest == nil {
c.ResponseError("destination is missing") c.ResponseError("destination is missing")
return return
} }
config.Secret = dest config.Secret = dest.(string)
} }
mfaUtil := object.GetMfaUtil(mfaType, config) mfaUtil := object.GetMfaUtil(mfaType, config)
@ -146,10 +159,6 @@ func (c *ApiController) MfaSetupEnable() {
owner := c.Ctx.Request.Form.Get("owner") owner := c.Ctx.Request.Form.Get("owner")
name := c.Ctx.Request.Form.Get("name") name := c.Ctx.Request.Form.Get("name")
mfaType := c.Ctx.Request.Form.Get("mfaType") mfaType := c.Ctx.Request.Form.Get("mfaType")
secret := c.Ctx.Request.Form.Get("secret")
dest := c.Ctx.Request.Form.Get("dest")
countryCode := c.Ctx.Request.Form.Get("secret")
recoveryCodes := c.Ctx.Request.Form.Get("recoveryCodes")
user, err := object.GetUser(util.GetId(owner, name)) user, err := object.GetUser(util.GetId(owner, name))
if err != nil { if err != nil {
@ -167,39 +176,43 @@ func (c *ApiController) MfaSetupEnable() {
} }
if mfaType == object.TotpType { if mfaType == object.TotpType {
if secret == "" { secret := c.GetSession(MfaTotpSecretSession)
if secret == nil {
c.ResponseError("totp secret is missing") c.ResponseError("totp secret is missing")
return return
} }
config.Secret = secret config.Secret = secret.(string)
} else if mfaType == object.EmailType { } else if mfaType == object.EmailType {
if user.Email == "" { if user.Email == "" {
if dest == "" { dest := c.GetSession(MfaDestSession)
if dest == nil {
c.ResponseError("destination is missing") c.ResponseError("destination is missing")
return return
} }
user.Email = dest user.Email = dest.(string)
} }
} else if mfaType == object.SmsType { } else if mfaType == object.SmsType {
if user.Phone == "" { if user.Phone == "" {
if dest == "" { dest := c.GetSession(MfaDestSession)
if dest == nil {
c.ResponseError("destination is missing") c.ResponseError("destination is missing")
return return
} }
user.Phone = dest user.Phone = dest.(string)
if countryCode == "" { countryCode := c.GetSession(MfaCountryCodeSession)
if countryCode == nil {
c.ResponseError("country code is missing") c.ResponseError("country code is missing")
return return
} }
user.CountryCode = countryCode user.CountryCode = countryCode.(string)
} }
} }
recoveryCodes := c.GetSession(MfaRecoveryCodesSession)
if recoveryCodes == "" { if recoveryCodes == nil {
c.ResponseError("recovery codes is missing") c.ResponseError("recovery codes is missing")
return return
} }
config.RecoveryCodes = []string{recoveryCodes} config.RecoveryCodes = []string{recoveryCodes.(string)}
mfaUtil := object.GetMfaUtil(mfaType, config) mfaUtil := object.GetMfaUtil(mfaType, config)
if mfaUtil == nil { if mfaUtil == nil {
@ -213,6 +226,14 @@ func (c *ApiController) MfaSetupEnable() {
return return
} }
c.DelSession(MfaRecoveryCodesSession)
if mfaType == object.TotpType {
c.DelSession(MfaTotpSecretSession)
} else {
c.DelSession(MfaCountryCodeSession)
c.DelSession(MfaDestSession)
}
c.ResponseOk(http.StatusText(http.StatusOK)) c.ResponseOk(http.StatusText(http.StatusOK))
} }

View File

@ -14,11 +14,7 @@
package controllers package controllers
import ( import "github.com/casdoor/casdoor/object"
"strings"
"github.com/casdoor/casdoor/object"
)
// GetOidcDiscovery // GetOidcDiscovery
// @Title GetOidcDiscovery // @Title GetOidcDiscovery
@ -46,31 +42,3 @@ func (c *RootController) GetJwks() {
c.Data["json"] = jwks c.Data["json"] = jwks
c.ServeJSON() c.ServeJSON()
} }
// GetWebFinger
// @Title GetWebFinger
// @Tag OIDC API
// @Param resource query string true "resource"
// @Success 200 {object} object.WebFinger
// @router /.well-known/webfinger [get]
func (c *RootController) GetWebFinger() {
resource := c.Input().Get("resource")
rels := []string{}
host := c.Ctx.Request.Host
for key, value := range c.Input() {
if strings.HasPrefix(key, "rel") {
rels = append(rels, value...)
}
}
webfinger, err := object.GetWebFinger(resource, rels, host)
if err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = webfinger
c.Ctx.Output.ContentType("application/jrd+json")
c.ServeJSON()
}

View File

@ -65,7 +65,7 @@ func (c *ApiController) GetOrganizations() {
c.ResponseOk(organizations) c.ResponseOk(organizations)
} else { } else {
limit := util.ParseInt(limit) limit := util.ParseInt(limit)
count, err := object.GetOrganizationCount(owner, organizationName, field, value) count, err := object.GetOrganizationCount(owner, field, value)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -119,11 +119,6 @@ func (c *ApiController) UpdateOrganization() {
return return
} }
if err = object.CheckIpWhitelist(organization.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.UpdateOrganization(id, &organization)) c.Data["json"] = wrapActionResponse(object.UpdateOrganization(id, &organization))
c.ServeJSON() c.ServeJSON()
} }
@ -143,7 +138,7 @@ func (c *ApiController) AddOrganization() {
return return
} }
count, err := object.GetOrganizationCount("", "", "", "") count, err := object.GetOrganizationCount("", "", "")
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
@ -154,11 +149,6 @@ func (c *ApiController) AddOrganization() {
return return
} }
if err = object.CheckIpWhitelist(organization.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization)) c.Data["json"] = wrapActionResponse(object.AddOrganization(&organization))
c.ServeJSON() c.ServeJSON()
} }

View File

@ -182,10 +182,6 @@ func (c *ApiController) BuyProduct() {
paidUserName := c.Input().Get("userName") paidUserName := c.Input().Get("userName")
owner, _ := util.GetOwnerAndNameFromId(id) owner, _ := util.GetOwnerAndNameFromId(id)
userId := util.GetId(owner, paidUserName) userId := util.GetId(owner, paidUserName)
if paidUserName != "" && !c.IsAdmin() {
c.ResponseError(c.T("general:Only admin user can specify user"))
return
}
if paidUserName == "" { if paidUserName == "" {
userId = c.GetSessionUsername() userId = c.GetSessionUsername()
} }

View File

@ -257,7 +257,7 @@ func (c *ApiController) UploadResource() {
fileType, _ = util.GetOwnerAndNameFromIdNoCheck(mimeType + "/") fileType, _ = util.GetOwnerAndNameFromIdNoCheck(mimeType + "/")
} }
fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 450) fullFilePath = object.GetTruncatedPath(provider, fullFilePath, 175)
if tag != "avatar" && tag != "termsOfUse" && !strings.HasPrefix(tag, "idCard") { if tag != "avatar" && tag != "termsOfUse" && !strings.HasPrefix(tag, "idCard") {
ext := filepath.Ext(filepath.Base(fullFilePath)) ext := filepath.Ext(filepath.Base(fullFilePath))
index := len(fullFilePath) - len(ext) index := len(fullFilePath) - len(ext)

View File

@ -289,16 +289,6 @@ func (c *ApiController) UpdateUser() {
} }
} }
if user.MfaEmailEnabled && user.Email == "" {
c.ResponseError(c.T("user:MFA email is enabled but email is empty"))
return
}
if user.MfaPhoneEnabled && user.Phone == "" {
c.ResponseError(c.T("user:MFA phone is enabled but phone number is empty"))
return
}
if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" { if msg := object.CheckUpdateUser(oldUser, &user, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
@ -364,8 +354,7 @@ func (c *ApiController) AddUser() {
return return
} }
emptyUser := object.User{} msg := object.CheckUsername(user.Name, c.GetAcceptLanguage())
msg := object.CheckUpdateUser(&emptyUser, &user, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
@ -411,12 +400,6 @@ func (c *ApiController) GetEmailAndPhone() {
organization := c.Ctx.Request.Form.Get("organization") organization := c.Ctx.Request.Form.Get("organization")
username := c.Ctx.Request.Form.Get("username") username := c.Ctx.Request.Form.Get("username")
enableErrorMask2 := conf.GetConfigBool("enableErrorMask2")
if enableErrorMask2 {
c.ResponseError("Error")
return
}
user, err := object.GetUserByFields(organization, username) user, err := object.GetUserByFields(organization, username)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
@ -490,12 +473,7 @@ func (c *ApiController) SetPassword() {
c.ResponseError(c.T("general:Missing parameter")) c.ResponseError(c.T("general:Missing parameter"))
return return
} }
if userId != c.GetSession("verifiedUserId") {
c.ResponseError(c.T("general:Wrong userId"))
return
}
c.SetSession("verifiedCode", "") c.SetSession("verifiedCode", "")
c.SetSession("verifiedUserId", "")
} }
targetUser, err := object.GetUser(userId) targetUser, err := object.GetUser(userId)
@ -541,29 +519,11 @@ func (c *ApiController) SetPassword() {
return return
} }
application, err := object.GetApplicationByUser(targetUser)
if err != nil {
c.ResponseError(err.Error())
return
}
if application == nil {
c.ResponseError(fmt.Sprintf(c.T("auth:the application for user %s is not found"), userId))
return
}
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
err = object.CheckEntryIp(clientIp, targetUser, application, organization, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
targetUser.Password = newPassword targetUser.Password = newPassword
targetUser.UpdateUserPassword(organization) targetUser.UpdateUserPassword(organization)
targetUser.NeedUpdatePassword = false targetUser.NeedUpdatePassword = false
targetUser.LastChangePasswordTime = util.GetCurrentTime()
_, err = object.UpdateUser(userId, targetUser, []string{"password", "need_update_password", "password_type", "last_change_password_time"}, false) _, err = object.UpdateUser(userId, targetUser, []string{"password", "need_update_password", "password_type"}, false)
if err != nil { if err != nil {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return

View File

@ -45,22 +45,6 @@ func (c *ApiController) ResponseOk(data ...interface{}) {
// ResponseError ... // ResponseError ...
func (c *ApiController) ResponseError(error string, data ...interface{}) { func (c *ApiController) ResponseError(error string, data ...interface{}) {
enableErrorMask2 := conf.GetConfigBool("enableErrorMask2")
if enableErrorMask2 {
error = c.T("subscription:Error")
resp := &Response{Status: "error", Msg: error}
c.ResponseJsonData(resp, data...)
return
}
enableErrorMask := conf.GetConfigBool("enableErrorMask")
if enableErrorMask {
if strings.HasPrefix(error, "The user: ") && strings.HasSuffix(error, " doesn't exist") || strings.HasPrefix(error, "用户: ") && strings.HasSuffix(error, "不存在") {
error = c.T("check:password or code is incorrect")
}
}
resp := &Response{Status: "error", Msg: error} resp := &Response{Status: "error", Msg: error}
c.ResponseJsonData(resp, data...) c.ResponseJsonData(resp, data...)
} }

View File

@ -132,8 +132,7 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(err.Error()) c.ResponseError(err.Error())
return return
} }
remoteAddr := util.GetIPFromRequest(c.Ctx.Request)
clientIp := util.GetClientIpFromRequest(c.Ctx.Request)
if msg := vform.CheckParameter(form.SendVerifyCode, c.GetAcceptLanguage()); msg != "" { if msg := vform.CheckParameter(form.SendVerifyCode, c.GetAcceptLanguage()); msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
@ -246,6 +245,8 @@ func (c *ApiController) SendVerificationCode() {
if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest { if user != nil && util.GetMaskedEmail(mfaProps.Secret) == vform.Dest {
vform.Dest = mfaProps.Secret vform.Dest = mfaProps.Secret
} }
} else if vform.Method == MfaSetupVerification {
c.SetSession(MfaDestSession, vform.Dest)
} }
provider, err = application.GetEmailProvider(vform.Method) provider, err = application.GetEmailProvider(vform.Method)
@ -258,7 +259,7 @@ func (c *ApiController) SendVerificationCode() {
return return
} }
sendResp = object.SendVerificationCodeToEmail(organization, user, provider, clientIp, vform.Dest) sendResp = object.SendVerificationCodeToEmail(organization, user, provider, remoteAddr, vform.Dest)
case object.VerifyTypePhone: case object.VerifyTypePhone:
if vform.Method == LoginVerification || vform.Method == ForgetVerification { if vform.Method == LoginVerification || vform.Method == ForgetVerification {
if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest { if user != nil && util.GetMaskedPhone(user.Phone) == vform.Dest {
@ -280,6 +281,11 @@ func (c *ApiController) SendVerificationCode() {
vform.CountryCode = user.GetCountryCode(vform.CountryCode) vform.CountryCode = user.GetCountryCode(vform.CountryCode)
} }
} }
if vform.Method == MfaSetupVerification {
c.SetSession(MfaCountryCodeSession, vform.CountryCode)
c.SetSession(MfaDestSession, vform.Dest)
}
} else if vform.Method == MfaAuthVerification { } else if vform.Method == MfaAuthVerification {
mfaProps := user.GetPreferredMfaProps(false) mfaProps := user.GetPreferredMfaProps(false)
if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest { if user != nil && util.GetMaskedPhone(mfaProps.Secret) == vform.Dest {
@ -287,7 +293,6 @@ func (c *ApiController) SendVerificationCode() {
} }
vform.CountryCode = mfaProps.CountryCode vform.CountryCode = mfaProps.CountryCode
vform.CountryCode = user.GetCountryCode(vform.CountryCode)
} }
provider, err = application.GetSmsProvider(vform.Method, vform.CountryCode) provider, err = application.GetSmsProvider(vform.Method, vform.CountryCode)
@ -304,7 +309,7 @@ func (c *ApiController) SendVerificationCode() {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), vform.CountryCode)) c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), vform.CountryCode))
return return
} else { } else {
sendResp = object.SendVerificationCodeToPhone(organization, user, provider, clientIp, phone) sendResp = object.SendVerificationCodeToPhone(organization, user, provider, remoteAddr, phone)
} }
} }
@ -527,6 +532,5 @@ func (c *ApiController) VerifyCode() {
} }
c.SetSession("verifiedCode", authForm.Code) c.SetSession("verifiedCode", authForm.Code)
c.SetSession("verifiedUserId", user.GetId())
c.ResponseOk() c.ResponseOk()
} }

View File

@ -27,18 +27,7 @@ import (
) )
func deployStaticFiles(provider *object.Provider) { func deployStaticFiles(provider *object.Provider) {
certificate := "" storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint)
if provider.Category == "Storage" && provider.Type == "Casdoor" {
cert, err := object.GetCert(util.GetId(provider.Owner, provider.Cert))
if err != nil {
panic(err)
}
if cert == nil {
panic(err)
}
certificate = cert.Certificate
}
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, provider.Endpoint, certificate, provider.Content)
if err != nil { if err != nil {
panic(err) panic(err)
} }

View File

@ -26,10 +26,6 @@ type AuthForm struct {
Name string `json:"name"` Name string `json:"name"`
FirstName string `json:"firstName"` FirstName string `json:"firstName"`
LastName string `json:"lastName"` LastName string `json:"lastName"`
Gender string `json:"gender"`
Bio string `json:"bio"`
Tag string `json:"tag"`
Education string `json:"education"`
Email string `json:"email"` Email string `json:"email"`
Phone string `json:"phone"` Phone string `json:"phone"`
Affiliation string `json:"affiliation"` Affiliation string `json:"affiliation"`

8
go.mod
View File

@ -9,11 +9,10 @@ require (
github.com/beego/beego v1.12.12 github.com/beego/beego v1.12.12
github.com/beevik/etree v1.1.0 github.com/beevik/etree v1.1.0
github.com/casbin/casbin/v2 v2.77.2 github.com/casbin/casbin/v2 v2.77.2
github.com/casdoor/go-sms-sender v0.25.0 github.com/casdoor/go-sms-sender v0.24.0
github.com/casdoor/gomail/v2 v2.0.1 github.com/casdoor/gomail/v2 v2.0.1
github.com/casdoor/ldapserver v1.2.0
github.com/casdoor/notify v0.45.0 github.com/casdoor/notify v0.45.0
github.com/casdoor/oss v1.8.0 github.com/casdoor/oss v1.6.0
github.com/casdoor/xorm-adapter/v3 v3.1.0 github.com/casdoor/xorm-adapter/v3 v3.1.0
github.com/casvisor/casvisor-go-sdk v1.4.0 github.com/casvisor/casvisor-go-sdk v1.4.0
github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f github.com/dchest/captcha v0.0.0-20200903113550-03f5f0333e1f
@ -21,6 +20,7 @@ require (
github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect github.com/elazarl/go-bindata-assetfs v1.0.1 // indirect
github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3 github.com/elimity-com/scim v0.0.0-20230426070224-941a5eac92f3
github.com/fogleman/gg v1.3.0 github.com/fogleman/gg v1.3.0
github.com/forestmgy/ldapserver v1.1.0
github.com/go-asn1-ber/asn1-ber v1.5.5 github.com/go-asn1-ber/asn1-ber v1.5.5
github.com/go-git/go-git/v5 v5.11.0 github.com/go-git/go-git/v5 v5.11.0
github.com/go-ldap/ldap/v3 v3.4.6 github.com/go-ldap/ldap/v3 v3.4.6
@ -30,7 +30,7 @@ require (
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
github.com/go-webauthn/webauthn v0.6.0 github.com/go-webauthn/webauthn v0.6.0
github.com/golang-jwt/jwt/v4 v4.5.0 github.com/golang-jwt/jwt/v4 v4.5.0
github.com/google/uuid v1.6.0 github.com/google/uuid v1.4.0
github.com/json-iterator/go v1.1.12 github.com/json-iterator/go v1.1.12
github.com/lestrrat-go/jwx v1.2.29 github.com/lestrrat-go/jwx v1.2.29
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9

17
go.sum
View File

@ -1083,20 +1083,16 @@ github.com/casbin/casbin/v2 v2.28.3/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRt
github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg= github.com/casbin/casbin/v2 v2.37.0/go.mod h1:vByNa/Fchek0KZUgG5wEsl7iFsiviAYKRtgrQfcJqHg=
github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM= github.com/casbin/casbin/v2 v2.77.2 h1:yQinn/w9x8AswiwqwtrXz93VU48R1aYTXdHEx4RI3jM=
github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk= github.com/casbin/casbin/v2 v2.77.2/go.mod h1:mzGx0hYW9/ksOSpw3wNjk3NRAroq5VMFYUQ6G43iGPk=
github.com/casdoor/casdoor-go-sdk v0.50.0 h1:bUYbz/MzJuWfLKJbJM0+U0YpYewAur+THp5TKnufWZM=
github.com/casdoor/casdoor-go-sdk v0.50.0/go.mod h1:cMnkCQJgMYpgAlgEx8reSt1AVaDIQLcJ1zk5pzBaz+4=
github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs= github.com/casdoor/go-reddit/v2 v2.1.0 h1:kIbfdJ7AA7H0uTQ8s0q4GGZqSS5V9wVE74RrXyD9XPs=
github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc= github.com/casdoor/go-reddit/v2 v2.1.0/go.mod h1:eagkvwlZ4Hcsuc/uQsLHYEulz5jN65SVSwV/AIE7zsc=
github.com/casdoor/go-sms-sender v0.25.0 h1:eF4cOCSbjVg7+0uLlJQnna/FQ0BWW+Fp/x4cXhzQu1Y= github.com/casdoor/go-sms-sender v0.24.0 h1:LNLsce3EG/87I3JS6UiajF3LlQmdIiCgebEu0IE4wSM=
github.com/casdoor/go-sms-sender v0.25.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0= github.com/casdoor/go-sms-sender v0.24.0/go.mod h1:bOm4H8/YfJmEHjBatEVQFOnAf0OOn1B0Wi5B7zDhws0=
github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w= github.com/casdoor/gomail/v2 v2.0.1 h1:J+FG6x80s9e5lBHUn8Sv0Y56mud34KiWih5YdmudR/w=
github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q= github.com/casdoor/gomail/v2 v2.0.1/go.mod h1:VnGPslEAtpix5FjHisR/WKB1qvZDBaujbikxDe9d+2Q=
github.com/casdoor/ldapserver v1.2.0 h1:HdSYe+ULU6z9K+2BqgTrJKQRR4//ERAXB64ttOun6Ow=
github.com/casdoor/ldapserver v1.2.0/go.mod h1:VwYU2vqQ2pA8sa00PRekH71R2XmgfzMKhmp1XrrDu2s=
github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk= github.com/casdoor/notify v0.45.0 h1:OlaFvcQFjGOgA4mRx07M8AH1gvb5xNo21mcqrVGlLgk=
github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ= github.com/casdoor/notify v0.45.0/go.mod h1:wNHQu0tiDROMBIvz0j3Om3Lhd5yZ+AIfnFb8MYb8OLQ=
github.com/casdoor/oss v1.8.0 h1:uuyKhDIp7ydOtV4lpqhAY23Ban2Ln8La8+QT36CwylM= github.com/casdoor/oss v1.6.0 h1:IOWrGLJ+VO82qS796eaRnzFPPA1Sn3cotYTi7O/VIlQ=
github.com/casdoor/oss v1.8.0/go.mod h1:uaqO7KBI2lnZcnB8rF7O6C2bN7llIbfC5Ql8ex1yR1U= github.com/casdoor/oss v1.6.0/go.mod h1:rJAWA0hLhtu94t6IRpotLUkXO1NWMASirywQYaGizJE=
github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk= github.com/casdoor/xorm-adapter/v3 v3.1.0 h1:NodWayRtSLVSeCvL9H3Hc61k0G17KhV9IymTCNfh3kk=
github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM= github.com/casdoor/xorm-adapter/v3 v3.1.0/go.mod h1:4WTcUw+bTgBylGHeGHzTtBvuTXRS23dtwzFLl9tsgFM=
github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg= github.com/casvisor/casvisor-go-sdk v1.4.0 h1:hbZEGGJ1cwdHFAxeXrMoNw6yha6Oyg2F0qQhBNCN/dg=
@ -1239,6 +1235,8 @@ github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/forestmgy/ldapserver v1.1.0 h1:gvil4nuLhqPEL8SugCkFhRyA0/lIvRdwZSqlrw63ll4=
github.com/forestmgy/ldapserver v1.1.0/go.mod h1:1RZ8lox1QSY7rmbjdmy+sYQXY4Lp7SpGzpdE3+j3IyM=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk= github.com/form3tech-oss/jwt-go v3.2.2+incompatible h1:TcekIExNqud5crz4xD2pavyTgWiPvpYe4Xau31I0PRk=
github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k=
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g= github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
@ -1462,9 +1460,8 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+
github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.2.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4=
github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.0.0-20220520183353-fd19c99a87aa/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8= github.com/googleapis/enterprise-certificate-proxy v0.1.0/go.mod h1:17drOmN3MwGY7t0e+Ei9b45FFGA3fBs3x36SsCg1hq8=
github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg= github.com/googleapis/enterprise-certificate-proxy v0.2.0/go.mod h1:8C0jb7/mgJe/9KK8Lm7X9ctZC2t60YyIpYEI16jx0Qg=

View File

@ -1,167 +1,167 @@
{ {
"account": { "account": {
"Failed to add user": "عدم موفقیت در افزودن کاربر", "Failed to add user": "Failed to add user",
"Get init score failed, error: %w": "عدم موفقیت در دریافت امتیاز اولیه، خطا: %w", "Get init score failed, error: %w": "Get init score failed, error: %w",
"Please sign out first": "لطفاً ابتدا خارج شوید", "Please sign out first": "Please sign out first",
"The application does not allow to sign up new account": "برنامه اجازه ثبت‌نام حساب جدید را نمی‌دهد" "The application does not allow to sign up new account": "The application does not allow to sign up new account"
}, },
"auth": { "auth": {
"Challenge method should be S256": "روش چالش باید S256 باشد", "Challenge method should be S256": "Challenge method should be S256",
"Failed to create user, user information is invalid: %s": "عدم موفقیت در ایجاد کاربر، اطلاعات کاربر نامعتبر است: %s", "Failed to create user, user information is invalid: %s": "Failed to create user, user information is invalid: %s",
"Failed to login in: %s": "عدم موفقیت در ورود: %s", "Failed to login in: %s": "Failed to login in: %s",
"Invalid token": "توکن نامعتبر", "Invalid token": "Invalid token",
"State expected: %s, but got: %s": "وضعیت مورد انتظار: %s، اما دریافت شد: %s", "State expected: %s, but got: %s": "State expected: %s, but got: %s",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "حساب برای ارائه‌دهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبت‌نام به‌عنوان حساب جدید از طریق %%s نیست، لطفاً از روش دیگری برای ثبت‌نام استفاده کنید", "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account via %%s, please use another way to sign up",
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "حساب برای ارائه‌دهنده: %s و نام کاربری: %s (%s) وجود ندارد و مجاز به ثبت‌نام به‌عنوان حساب جدید نیست، لطفاً با پشتیبانی IT خود تماس بگیرید", "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "حساب برای ارائه‌دهنده: %s و نام کاربری: %s (%s) در حال حاضر به حساب دیگری مرتبط است: %s (%s)", "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)",
"The application: %s does not exist": "برنامه: %s وجود ندارد", "The application: %s does not exist": "The application: %s does not exist",
"The login method: login with LDAP is not enabled for the application": "روش ورود: ورود با LDAP برای برنامه فعال نیست", "The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "روش ورود: ورود با پیامک برای برنامه فعال نیست", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "روش ورود: ورود با ایمیل برای برنامه فعال نیست", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with face is not enabled for the application": "روش ورود: ورود با چهره برای برنامه فعال نیست", "The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
"The login method: login with password is not enabled for the application": "روش ورود: ورود با رمز عبور برای برنامه فعال نیست", "The login method: login with password is not enabled for the application": "The login method: login with password is not enabled for the application",
"The organization: %s does not exist": "سازمان: %s وجود ندارد", "The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "ارائه‌دهنده: %s برای برنامه فعال نیست", "The provider: %s is not enabled for the application": "The provider: %s is not enabled for the application",
"Unauthorized operation": "عملیات غیرمجاز", "Unauthorized operation": "Unauthorized operation",
"Unknown authentication type (not password or provider), form = %s": "نوع احراز هویت ناشناخته (نه رمز عبور و نه ارائه‌دهنده)، فرم = %s", "Unknown authentication type (not password or provider), form = %s": "Unknown authentication type (not password or provider), form = %s",
"User's tag: %s is not listed in the application's tags": "برچسب کاربر: %s در برچسب‌های برنامه فهرست نشده است", "User's tag: %s is not listed in the application's tags": "User's tag: %s is not listed in the application's tags",
"paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "کاربر پرداختی %s اشتراک فعال یا در انتظار ندارد و برنامه: %s قیمت‌گذاری پیش‌فرض ندارد" "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing": "paid-user %s does not have active or pending subscription and the application: %s does not have default pricing"
}, },
"cas": { "cas": {
"Service %s and %s do not match": "سرویس %s و %s مطابقت ندارند" "Service %s and %s do not match": "Service %s and %s do not match"
}, },
"check": { "check": {
"Affiliation cannot be blank": "وابستگی نمی‌تواند خالی باشد", "Affiliation cannot be blank": "Affiliation cannot be blank",
"Default code does not match the code's matching rules": "کد پیش‌فرض با قوانین تطبیق کد مطابقت ندارد", "Default code does not match the code's matching rules": "Default code does not match the code's matching rules",
"DisplayName cannot be blank": "نام نمایشی نمی‌تواند خالی باشد", "DisplayName cannot be blank": "DisplayName cannot be blank",
"DisplayName is not valid real name": "نام نمایشی یک نام واقعی معتبر نیست", "DisplayName is not valid real name": "DisplayName is not valid real name",
"Email already exists": "ایمیل قبلاً وجود دارد", "Email already exists": "Email already exists",
"Email cannot be empty": "ایمیل نمی‌تواند خالی باشد", "Email cannot be empty": "Email cannot be empty",
"Email is invalid": "ایمیل نامعتبر است", "Email is invalid": "Email is invalid",
"Empty username.": "نام کاربری خالی است.", "Empty username.": "Empty username.",
"Face data does not exist, cannot log in": "داده‌های چهره وجود ندارد، نمی‌توان وارد شد", "Face data does not exist, cannot log in": "Face data does not exist, cannot log in",
"Face data mismatch": "عدم تطابق داده‌های چهره", "Face data mismatch": "Face data mismatch",
"FirstName cannot be blank": "نام نمی‌تواند خالی باشد", "FirstName cannot be blank": "FirstName cannot be blank",
"Invitation code cannot be blank": "کد دعوت نمی‌تواند خالی باشد", "Invitation code cannot be blank": "Invitation code cannot be blank",
"Invitation code exhausted": "کد دعوت استفاده شده است", "Invitation code exhausted": "Invitation code exhausted",
"Invitation code is invalid": "کد دعوت نامعتبر است", "Invitation code is invalid": "Invitation code is invalid",
"Invitation code suspended": "کد دعوت معلق است", "Invitation code suspended": "Invitation code suspended",
"LDAP user name or password incorrect": "نام کاربری یا رمز عبور LDAP نادرست است", "LDAP user name or password incorrect": "LDAP user name or password incorrect",
"LastName cannot be blank": "نام خانوادگی نمی‌تواند خالی باشد", "LastName cannot be blank": "LastName cannot be blank",
"Multiple accounts with same uid, please check your ldap server": "چندین حساب با uid یکسان، لطفاً سرور LDAP خود را بررسی کنید", "Multiple accounts with same uid, please check your ldap server": "Multiple accounts with same uid, please check your ldap server",
"Organization does not exist": "سازمان وجود ندارد", "Organization does not exist": "Organization does not exist",
"Phone already exists": "تلفن قبلاً وجود دارد", "Phone already exists": "Phone already exists",
"Phone cannot be empty": "تلفن نمی‌تواند خالی باشد", "Phone cannot be empty": "Phone cannot be empty",
"Phone number is invalid": "شماره تلفن نامعتبر است", "Phone number is invalid": "Phone number is invalid",
"Please register using the email corresponding to the invitation code": "لطفاً با استفاده از ایمیل مربوط به کد دعوت ثبت‌نام کنید", "Please register using the email corresponding to the invitation code": "Please register using the email corresponding to the invitation code",
"Please register using the phone corresponding to the invitation code": "لطفاً با استفاده از تلفن مربوط به کد دعوت ثبت‌نام کنید", "Please register using the phone corresponding to the invitation code": "Please register using the phone corresponding to the invitation code",
"Please register using the username corresponding to the invitation code": "لطفاً با استفاده از نام کاربری مربوط به کد دعوت ثبت‌نام کنید", "Please register using the username corresponding to the invitation code": "Please register using the username corresponding to the invitation code",
"Session outdated, please login again": "جلسه منقضی شده است، لطفاً دوباره وارد شوید", "Session outdated, please login again": "Session outdated, please login again",
"The invitation code has already been used": "کد دعوت قبلاً استفاده شده است", "The invitation code has already been used": "The invitation code has already been used",
"The user is forbidden to sign in, please contact the administrator": "ورود کاربر ممنوع است، لطفاً با مدیر تماس بگیرید", "The user is forbidden to sign in, please contact the administrator": "The user is forbidden to sign in, please contact the administrator",
"The user: %s doesn't exist in LDAP server": "کاربر: %s در سرور LDAP وجود ندارد", "The user: %s doesn't exist in LDAP server": "The user: %s doesn't exist in LDAP server",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "نام کاربری فقط می‌تواند حاوی کاراکترهای الفبایی عددی، زیرخط یا خط تیره باشد، نمی‌تواند خط تیره یا زیرخط متوالی داشته باشد، و نمی‌تواند با خط تیره یا زیرخط شروع یا پایان یابد.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.",
"The value \"%s\" for account field \"%s\" doesn't match the account item regex": "مقدار \"%s\" برای فیلد حساب \"%s\" با عبارت منظم مورد حساب مطابقت ندارد", "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \"%s\" for signup field \"%s\" doesn't match the signup item regex of the application \"%s\"": "مقدار \"%s\" برای فیلد ثبت‌نام \"%s\" با عبارت منظم مورد ثبت‌نام برنامه \"%s\" مطابقت ندارد", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "نام کاربری قبلاً وجود دارد", "Username already exists": "Username already exists",
"Username cannot be an email address": "نام کاربری نمی‌تواند یک آدرس ایمیل باشد", "Username cannot be an email address": "Username cannot be an email address",
"Username cannot contain white spaces": "نام کاربری نمی‌تواند حاوی فاصله باشد", "Username cannot contain white spaces": "Username cannot contain white spaces",
"Username cannot start with a digit": "نام کاربری نمی‌تواند با یک رقم شروع شود", "Username cannot start with a digit": "Username cannot start with a digit",
"Username is too long (maximum is 39 characters).": "نام کاربری بیش از حد طولانی است (حداکثر ۳۹ کاراکتر).", "Username is too long (maximum is 39 characters).": "Username is too long (maximum is 39 characters).",
"Username must have at least 2 characters": "نام کاربری باید حداقل ۲ کاراکتر داشته باشد", "Username must have at least 2 characters": "Username must have at least 2 characters",
"You have entered the wrong password or code too many times, please wait for %d minutes and try again": "شما رمز عبور یا کد اشتباه را بیش از حد وارد کرده‌اید، لطفاً %d دقیقه صبر کنید و دوباره تلاش کنید", "You have entered the wrong password or code too many times, please wait for %d minutes and try again": "You have entered the wrong password or code too many times, please wait for %d minutes and try again",
"Your region is not allow to signup by phone": "منطقه شما اجازه ثبت‌نام با تلفن را ندارد", "Your region is not allow to signup by phone": "Your region is not allow to signup by phone",
"password or code is incorrect": "رمز عبور یا کد نادرست است", "password or code is incorrect": "password or code is incorrect",
"password or code is incorrect, you have %d remaining chances": "رمز عبور یا کد نادرست است، شما %d فرصت باقی‌مانده دارید", "password or code is incorrect, you have %d remaining chances": "password or code is incorrect, you have %d remaining chances",
"unsupported password type: %s": "نوع رمز عبور پشتیبانی نشده: %s" "unsupported password type: %s": "unsupported password type: %s"
}, },
"general": { "general": {
"Missing parameter": "پارامتر گمشده", "Missing parameter": "Missing parameter",
"Please login first": "لطفاً ابتدا وارد شوید", "Please login first": "Please login first",
"The organization: %s should have one application at least": "سازمان: %s باید حداقل یک برنامه داشته باشد", "The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "کاربر: %s وجود ندارد", "The user: %s doesn't exist": "The user: %s doesn't exist",
"don't support captchaProvider: ": "از captchaProvider پشتیبانی نمی‌شود: ", "don't support captchaProvider: ": "don't support captchaProvider: ",
"this operation is not allowed in demo mode": "این عملیات در حالت دمو مجاز نیست", "this operation is not allowed in demo mode": "this operation is not allowed in demo mode",
"this operation requires administrator to perform": "این عملیات نیاز به مدیر برای انجام دارد" "this operation requires administrator to perform": "this operation requires administrator to perform"
}, },
"ldap": { "ldap": {
"Ldap server exist": "سرور LDAP وجود دارد" "Ldap server exist": "Ldap server exist"
}, },
"link": { "link": {
"Please link first": "لطفاً ابتدا پیوند دهید", "Please link first": "Please link first",
"This application has no providers": "این برنامه ارائه‌دهنده‌ای ندارد", "This application has no providers": "This application has no providers",
"This application has no providers of type": "این برنامه ارائه‌دهنده‌ای از نوع ندارد", "This application has no providers of type": "This application has no providers of type",
"This provider can't be unlinked": "این ارائه‌دهنده نمی‌تواند لغو پیوند شود", "This provider can't be unlinked": "This provider can't be unlinked",
"You are not the global admin, you can't unlink other users": "شما مدیر جهانی نیستید، نمی‌توانید کاربران دیگر را لغو پیوند کنید", "You are not the global admin, you can't unlink other users": "You are not the global admin, you can't unlink other users",
"You can't unlink yourself, you are not a member of any application": "شما نمی‌توانید خودتان را لغو پیوند کنید، شما عضو هیچ برنامه‌ای نیستید" "You can't unlink yourself, you are not a member of any application": "You can't unlink yourself, you are not a member of any application"
}, },
"organization": { "organization": {
"Only admin can modify the %s.": "فقط مدیر می‌تواند %s را تغییر دهد.", "Only admin can modify the %s.": "Only admin can modify the %s.",
"The %s is immutable.": "%s غیرقابل تغییر است.", "The %s is immutable.": "The %s is immutable.",
"Unknown modify rule %s.": "قانون تغییر ناشناخته %s." "Unknown modify rule %s.": "Unknown modify rule %s."
}, },
"permission": { "permission": {
"The permission: \"%s\" doesn't exist": "مجوز: \"%s\" وجود ندارد" "The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
}, },
"provider": { "provider": {
"Invalid application id": "شناسه برنامه نامعتبر", "Invalid application id": "Invalid application id",
"the provider: %s does not exist": "ارائه‌دهنده: %s وجود ندارد" "the provider: %s does not exist": "the provider: %s does not exist"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "کاربر برای برچسب: آواتار تهی است", "User is nil for tag: avatar": "User is nil for tag: avatar",
"Username or fullFilePath is empty: username = %s, fullFilePath = %s": "نام کاربری یا مسیر کامل فایل خالی است: نام کاربری = %s، مسیر کامل فایل = %s" "Username or fullFilePath is empty: username = %s, fullFilePath = %s": "Username or fullFilePath is empty: username = %s, fullFilePath = %s"
}, },
"saml": { "saml": {
"Application %s not found": "برنامه %s یافت نشد" "Application %s not found": "Application %s not found"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "دسته‌بندی ارائه‌دهنده %s SAML نیست" "provider %s's category is not SAML": "provider %s's category is not SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "پارامترهای خالی برای emailForm: %v", "Empty parameters for emailForm: %v": "Empty parameters for emailForm: %v",
"Invalid Email receivers: %s": "گیرندگان ایمیل نامعتبر: %s", "Invalid Email receivers: %s": "Invalid Email receivers: %s",
"Invalid phone receivers: %s": "گیرندگان تلفن نامعتبر: %s" "Invalid phone receivers: %s": "Invalid phone receivers: %s"
}, },
"storage": { "storage": {
"The objectKey: %s is not allowed": "objectKey: %s مجاز نیست", "The objectKey: %s is not allowed": "The objectKey: %s is not allowed",
"The provider type: %s is not supported": "نوع ارائه‌دهنده: %s پشتیبانی نمی‌شود" "The provider type: %s is not supported": "The provider type: %s is not supported"
}, },
"token": { "token": {
"Grant_type: %s is not supported in this application": "grant_type: %s در این برنامه پشتیبانی نمی‌شود", "Grant_type: %s is not supported in this application": "Grant_type: %s is not supported in this application",
"Invalid application or wrong clientSecret": "برنامه نامعتبر یا clientSecret نادرست", "Invalid application or wrong clientSecret": "Invalid application or wrong clientSecret",
"Invalid client_id": "client_id نامعتبر", "Invalid client_id": "Invalid client_id",
"Redirect URI: %s doesn't exist in the allowed Redirect URI list": "آدرس بازگشت: %s در لیست آدرس‌های بازگشت مجاز وجود ندارد", "Redirect URI: %s doesn't exist in the allowed Redirect URI list": "Redirect URI: %s doesn't exist in the allowed Redirect URI list",
"Token not found, invalid accessToken": "توکن یافت نشد، accessToken نامعتبر" "Token not found, invalid accessToken": "Token not found, invalid accessToken"
}, },
"user": { "user": {
"Display name cannot be empty": "نام نمایشی نمی‌تواند خالی باشد", "Display name cannot be empty": "Display name cannot be empty",
"New password cannot contain blank space.": "رمز عبور جدید نمی‌تواند حاوی فاصله خالی باشد." "New password cannot contain blank space.": "New password cannot contain blank space."
}, },
"user_upload": { "user_upload": {
"Failed to import users": "عدم موفقیت در وارد کردن کاربران" "Failed to import users": "Failed to import users"
}, },
"util": { "util": {
"No application is found for userId: %s": "هیچ برنامه‌ای برای userId: %s یافت نشد", "No application is found for userId: %s": "No application is found for userId: %s",
"No provider for category: %s is found for application: %s": "هیچ ارائه‌دهنده‌ای برای دسته‌بندی: %s برای برنامه: %s یافت نشد", "No provider for category: %s is found for application: %s": "No provider for category: %s is found for application: %s",
"The provider: %s is not found": "ارائه‌دهنده: %s یافت نشد" "The provider: %s is not found": "The provider: %s is not found"
}, },
"verification": { "verification": {
"Invalid captcha provider.": "ارائه‌دهنده کپچا نامعتبر.", "Invalid captcha provider.": "Invalid captcha provider.",
"Phone number is invalid in your region %s": "شماره تلفن در منطقه شما نامعتبر است %s", "Phone number is invalid in your region %s": "Phone number is invalid in your region %s",
"The verification code has not been sent yet!": "کد تأیید هنوز ارسال نشده است!", "The verification code has not been sent yet!": "The verification code has not been sent yet!",
"The verification code has not been sent yet, or has already been used!": "کد تأیید هنوز ارسال نشده است، یا قبلاً استفاده شده است!", "The verification code has not been sent yet, or has already been used!": "The verification code has not been sent yet, or has already been used!",
"Turing test failed.": "تست تورینگ ناموفق بود.", "Turing test failed.": "Turing test failed.",
"Unable to get the email modify rule.": "عدم توانایی در دریافت قانون تغییر ایمیل.", "Unable to get the email modify rule.": "Unable to get the email modify rule.",
"Unable to get the phone modify rule.": "عدم توانایی در دریافت قانون تغییر تلفن.", "Unable to get the phone modify rule.": "Unable to get the phone modify rule.",
"Unknown type": "نوع ناشناخته", "Unknown type": "Unknown type",
"Wrong verification code!": "کد تأیید اشتباه!", "Wrong verification code!": "Wrong verification code!",
"You should verify your code in %d min!": "شما باید کد خود را در %d دقیقه تأیید کنید!", "You should verify your code in %d min!": "You should verify your code in %d min!",
"please add a SMS provider to the \"Providers\" list for the application: %s": "لطفاً یک ارائه‌دهنده پیامک به لیست \"ارائه‌دهندگان\" برای برنامه: %s اضافه کنید", "please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \"Providers\" list for the application: %s": "لطفاً یک ارائه‌دهنده ایمیل به لیست \"ارائه‌دهندگان\" برای برنامه: %s اضافه کنید", "please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "کاربر وجود ندارد، لطفاً ابتدا ثبت‌نام کنید" "the user does not exist, please sign up first": "the user does not exist, please sign up first"
}, },
"webauthn": { "webauthn": {
"Found no credentials for this user": "هیچ اعتباری برای این کاربر یافت نشد", "Found no credentials for this user": "Found no credentials for this user",
"Please call WebAuthnSigninBegin first": "لطفاً ابتدا WebAuthnSigninBegin را فراخوانی کنید" "Please call WebAuthnSigninBegin first": "Please call WebAuthnSigninBegin first"
} }
} }

View File

@ -15,10 +15,10 @@
"The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT", "The account for provider: %s and username: %s (%s) does not exist and is not allowed to sign up as new account, please contact your IT support": "Аккаунт для провайдера: %s и имя пользователя: %s (%s) не существует и не может быть зарегистрирован как новый аккаунт. Пожалуйста, обратитесь в службу поддержки IT",
"The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)", "The account for provider: %s and username: %s (%s) is already linked to another account: %s (%s)": "Аккаунт поставщика: %s и имя пользователя: %s (%s) уже связаны с другим аккаунтом: %s (%s)",
"The application: %s does not exist": "Приложение: %s не существует", "The application: %s does not exist": "Приложение: %s не существует",
"The login method: login with LDAP is not enabled for the application": "Метод входа в систему: вход с помощью LDAP не включен для приложения", "The login method: login with LDAP is not enabled for the application": "The login method: login with LDAP is not enabled for the application",
"The login method: login with SMS is not enabled for the application": "Метод входа: вход с помощью SMS не включен для приложения", "The login method: login with SMS is not enabled for the application": "The login method: login with SMS is not enabled for the application",
"The login method: login with email is not enabled for the application": "Метод входа: вход с помощью электронной почты не включен для приложения", "The login method: login with email is not enabled for the application": "The login method: login with email is not enabled for the application",
"The login method: login with face is not enabled for the application": "Метод входа: вход с помощью лица не включен для приложения", "The login method: login with face is not enabled for the application": "The login method: login with face is not enabled for the application",
"The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения", "The login method: login with password is not enabled for the application": "Метод входа: вход с паролем не включен для приложения",
"The organization: %s does not exist": "The organization: %s does not exist", "The organization: %s does not exist": "The organization: %s does not exist",
"The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения", "The provider: %s is not enabled for the application": "Провайдер: %s не включен для приложения",
@ -53,16 +53,16 @@
"Phone already exists": "Телефон уже существует", "Phone already exists": "Телефон уже существует",
"Phone cannot be empty": "Телефон не может быть пустым", "Phone cannot be empty": "Телефон не может быть пустым",
"Phone number is invalid": "Номер телефона является недействительным", "Phone number is invalid": "Номер телефона является недействительным",
"Please register using the email corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя электронную почту, соответствующую коду приглашения", "Please register using the email corresponding to the invitation code": "Please register using the email corresponding to the invitation code",
"Please register using the phone corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь по телефону, соответствующему коду приглашения", "Please register using the phone corresponding to the invitation code": "Please register using the phone corresponding to the invitation code",
"Please register using the username corresponding to the invitation code": "Пожалуйста, зарегистрируйтесь, используя имя пользователя, соответствующее коду приглашения", "Please register using the username corresponding to the invitation code": "Please register using the username corresponding to the invitation code",
"Session outdated, please login again": "Сессия устарела, пожалуйста, войдите снова", "Session outdated, please login again": "Сессия устарела, пожалуйста, войдите снова",
"The invitation code has already been used": "The invitation code has already been used", "The invitation code has already been used": "The invitation code has already been used",
"The user is forbidden to sign in, please contact the administrator": "Пользователю запрещен вход, пожалуйста, обратитесь к администратору", "The user is forbidden to sign in, please contact the administrator": "Пользователю запрещен вход, пожалуйста, обратитесь к администратору",
"The user: %s doesn't exist in LDAP server": "Пользователь %s не существует на LDAP сервере", "The user: %s doesn't exist in LDAP server": "Пользователь %s не существует на LDAP сервере",
"The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Имя пользователя может состоять только из буквенно-цифровых символов, нижних подчеркиваний или дефисов, не может содержать последовательные дефисы или подчеркивания, а также не может начинаться или заканчиваться на дефис или подчеркивание.", "The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline.": "Имя пользователя может состоять только из буквенно-цифровых символов, нижних подчеркиваний или дефисов, не может содержать последовательные дефисы или подчеркивания, а также не может начинаться или заканчиваться на дефис или подчеркивание.",
"The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "Значение \\\"%s\\\" для поля аккаунта \\\"%s\\\" не соответствует регулярному значению", "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex": "The value \\\"%s\\\" for account field \\\"%s\\\" doesn't match the account item regex",
"The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Значение \\\"%s\\\" поля регистрации \\\"%s\\\" не соответствует регулярному выражению приложения \\\"%s\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"",
"Username already exists": "Имя пользователя уже существует", "Username already exists": "Имя пользователя уже существует",
"Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты", "Username cannot be an email address": "Имя пользователя не может быть адресом электронной почты",
"Username cannot contain white spaces": "Имя пользователя не может содержать пробелы", "Username cannot contain white spaces": "Имя пользователя не может содержать пробелы",
@ -78,11 +78,11 @@
"general": { "general": {
"Missing parameter": "Отсутствующий параметр", "Missing parameter": "Отсутствующий параметр",
"Please login first": "Пожалуйста, сначала войдите в систему", "Please login first": "Пожалуйста, сначала войдите в систему",
"The organization: %s should have one application at least": "Организация: %s должна иметь хотя бы одно приложение", "The organization: %s should have one application at least": "The organization: %s should have one application at least",
"The user: %s doesn't exist": "Пользователь %s не существует", "The user: %s doesn't exist": "Пользователь %s не существует",
"don't support captchaProvider: ": "неподдерживаемый captchaProvider: ", "don't support captchaProvider: ": "неподдерживаемый captchaProvider: ",
"this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме", "this operation is not allowed in demo mode": "эта операция не разрешена в демо-режиме",
"this operation requires administrator to perform": "для выполнения этой операции требуется администратор" "this operation requires administrator to perform": "this operation requires administrator to perform"
}, },
"ldap": { "ldap": {
"Ldap server exist": "LDAP-сервер существует" "Ldap server exist": "LDAP-сервер существует"
@ -101,11 +101,11 @@
"Unknown modify rule %s.": "Неизвестное изменение правила %s." "Unknown modify rule %s.": "Неизвестное изменение правила %s."
}, },
"permission": { "permission": {
"The permission: \\\"%s\\\" doesn't exist": "Разрешение: \\\"%s\\\" не существует" "The permission: \\\"%s\\\" doesn't exist": "The permission: \\\"%s\\\" doesn't exist"
}, },
"provider": { "provider": {
"Invalid application id": "Неверный идентификатор приложения", "Invalid application id": "Неверный идентификатор приложения",
"the provider: %s does not exist": "Провайдер: %s не существует" "the provider: %s does not exist": "провайдер: %s не существует"
}, },
"resource": { "resource": {
"User is nil for tag: avatar": "Пользователь равен нулю для тега: аватар", "User is nil for tag: avatar": "Пользователь равен нулю для тега: аватар",
@ -115,7 +115,7 @@
"Application %s not found": "Приложение %s не найдено" "Application %s not found": "Приложение %s не найдено"
}, },
"saml_sp": { "saml_sp": {
"provider %s's category is not SAML": "Категория провайдера %s не является SAML" "provider %s's category is not SAML": "категория провайдера %s не является SAML"
}, },
"service": { "service": {
"Empty parameters for emailForm: %v": "Пустые параметры для emailForm: %v", "Empty parameters for emailForm: %v": "Пустые параметры для emailForm: %v",
@ -148,7 +148,7 @@
"verification": { "verification": {
"Invalid captcha provider.": "Недействительный поставщик CAPTCHA.", "Invalid captcha provider.": "Недействительный поставщик CAPTCHA.",
"Phone number is invalid in your region %s": "Номер телефона недействителен в вашем регионе %s", "Phone number is invalid in your region %s": "Номер телефона недействителен в вашем регионе %s",
"The verification code has not been sent yet!": "Код проверки еще не отправлен!", "The verification code has not been sent yet!": "The verification code has not been sent yet!",
"The verification code has not been sent yet, or has already been used!": "The verification code has not been sent yet, or has already been used!", "The verification code has not been sent yet, or has already been used!": "The verification code has not been sent yet, or has already been used!",
"Turing test failed.": "Тест Тьюринга не удался.", "Turing test failed.": "Тест Тьюринга не удался.",
"Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.", "Unable to get the email modify rule.": "Невозможно получить правило изменения электронной почты.",
@ -156,8 +156,8 @@
"Unknown type": "Неизвестный тип", "Unknown type": "Неизвестный тип",
"Wrong verification code!": "Неправильный код подтверждения!", "Wrong verification code!": "Неправильный код подтверждения!",
"You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!", "You should verify your code in %d min!": "Вы должны проверить свой код через %d минут!",
"please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика SMS в список \\\"Провайдеры\\\" для приложения: %s", "please add a SMS provider to the \\\"Providers\\\" list for the application: %s": "please add a SMS provider to the \\\"Providers\\\" list for the application: %s",
"please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика электронной почты в список \\\"Провайдеры\\\" для приложения: %s", "please add an Email provider to the \\\"Providers\\\" list for the application: %s": "please add an Email provider to the \\\"Providers\\\" list for the application: %s",
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь" "the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
}, },
"webauthn": { "webauthn": {

View File

@ -200,7 +200,7 @@ func (idp *AlipayIdProvider) postWithBody(body interface{}, targetUrl string) ([
formData.Set("sign", sign) formData.Set("sign", sign)
resp, err := idp.Client.Post(targetUrl, "application/x-www-form-urlencoded;charset=utf-8", strings.NewReader(formData.Encode())) resp, err := idp.Client.PostForm(targetUrl, formData)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -21,7 +21,7 @@ import (
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
ldap "github.com/casdoor/ldapserver" ldap "github.com/forestmgy/ldapserver"
"github.com/lor00x/goldap/message" "github.com/lor00x/goldap/message"
) )
@ -142,7 +142,7 @@ func handleSearch(w ldap.ResponseWriter, m *ldap.Message) {
} }
for _, attr := range attrs { for _, attr := range attrs {
e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user)) e.AddAttribute(message.AttributeDescription(attr), getAttribute(string(attr), user))
if string(attr) == "title" { if string(attr) == "cn" {
e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user)) e.AddAttribute(message.AttributeDescription(attr), getAttribute("title", user))
} }
} }

View File

@ -23,7 +23,7 @@ import (
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
"github.com/lor00x/goldap/message" "github.com/lor00x/goldap/message"
ldap "github.com/casdoor/ldapserver" ldap "github.com/forestmgy/ldapserver"
"github.com/xorm-io/builder" "github.com/xorm-io/builder"
) )

View File

@ -56,7 +56,6 @@ func main() {
beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.StaticFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.AutoSigninFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.CorsFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.TimeoutFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter) beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage) beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
@ -72,7 +71,6 @@ func main() {
beego.BConfig.WebConfig.Session.SessionProviderConfig = conf.GetConfigString("redisEndpoint") beego.BConfig.WebConfig.Session.SessionProviderConfig = conf.GetConfigString("redisEndpoint")
} }
beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30 beego.BConfig.WebConfig.Session.SessionCookieLifeTime = 3600 * 24 * 30
beego.BConfig.WebConfig.Session.SessionGCMaxLifetime = 3600 * 24 * 30
// beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode // beego.BConfig.WebConfig.Session.SessionCookieSameSite = http.SameSiteNoneMode
err := logs.SetLogger(logs.AdapterFile, conf.GetConfigString("logConfig")) err := logs.SetLogger(logs.AdapterFile, conf.GetConfigString("logConfig"))

View File

@ -31,17 +31,15 @@ type SigninMethod struct {
} }
type SignupItem struct { type SignupItem struct {
Name string `json:"name"` Name string `json:"name"`
Visible bool `json:"visible"` Visible bool `json:"visible"`
Required bool `json:"required"` Required bool `json:"required"`
Prompted bool `json:"prompted"` Prompted bool `json:"prompted"`
Type string `json:"type"` CustomCss string `json:"customCss"`
CustomCss string `json:"customCss"` Label string `json:"label"`
Label string `json:"label"` Placeholder string `json:"placeholder"`
Placeholder string `json:"placeholder"` Regex string `json:"regex"`
Options []string `json:"options"` Rule string `json:"rule"`
Regex string `json:"regex"`
Rule string `json:"rule"`
} }
type SigninItem struct { type SigninItem struct {
@ -80,28 +78,24 @@ type Application struct {
EnableSamlCompress bool `json:"enableSamlCompress"` EnableSamlCompress bool `json:"enableSamlCompress"`
EnableSamlC14n10 bool `json:"enableSamlC14n10"` EnableSamlC14n10 bool `json:"enableSamlC14n10"`
EnableSamlPostBinding bool `json:"enableSamlPostBinding"` EnableSamlPostBinding bool `json:"enableSamlPostBinding"`
UseEmailAsSamlNameId bool `json:"useEmailAsSamlNameId"`
EnableWebAuthn bool `json:"enableWebAuthn"` EnableWebAuthn bool `json:"enableWebAuthn"`
EnableLinkWithEmail bool `json:"enableLinkWithEmail"` EnableLinkWithEmail bool `json:"enableLinkWithEmail"`
OrgChoiceMode string `json:"orgChoiceMode"` OrgChoiceMode string `json:"orgChoiceMode"`
SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"` SamlReplyUrl string `xorm:"varchar(100)" json:"samlReplyUrl"`
Providers []*ProviderItem `xorm:"mediumtext" json:"providers"` Providers []*ProviderItem `xorm:"mediumtext" json:"providers"`
SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"` SigninMethods []*SigninMethod `xorm:"varchar(2000)" json:"signinMethods"`
SignupItems []*SignupItem `xorm:"varchar(3000)" json:"signupItems"` SignupItems []*SignupItem `xorm:"varchar(2000)" json:"signupItems"`
SigninItems []*SigninItem `xorm:"mediumtext" json:"signinItems"` SigninItems []*SigninItem `xorm:"mediumtext" json:"signinItems"`
GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"` GrantTypes []string `xorm:"varchar(1000)" json:"grantTypes"`
OrganizationObj *Organization `xorm:"-" json:"organizationObj"` OrganizationObj *Organization `xorm:"-" json:"organizationObj"`
CertPublicKey string `xorm:"-" json:"certPublicKey"` CertPublicKey string `xorm:"-" json:"certPublicKey"`
Tags []string `xorm:"mediumtext" json:"tags"` Tags []string `xorm:"mediumtext" json:"tags"`
SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"` SamlAttributes []*SamlItem `xorm:"varchar(1000)" json:"samlAttributes"`
IsShared bool `json:"isShared"`
IpRestriction string `json:"ipRestriction"`
ClientId string `xorm:"varchar(100)" json:"clientId"` ClientId string `xorm:"varchar(100)" json:"clientId"`
ClientSecret string `xorm:"varchar(100)" json:"clientSecret"` ClientSecret string `xorm:"varchar(100)" json:"clientSecret"`
RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"` RedirectUris []string `xorm:"varchar(1000)" json:"redirectUris"`
TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"` TokenFormat string `xorm:"varchar(100)" json:"tokenFormat"`
TokenSigningMethod string `xorm:"varchar(100)" json:"tokenSigningMethod"`
TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"` TokenFields []string `xorm:"varchar(1000)" json:"tokenFields"`
ExpireInHours int `json:"expireInHours"` ExpireInHours int `json:"expireInHours"`
RefreshExpireInHours int `json:"refreshExpireInHours"` RefreshExpireInHours int `json:"refreshExpireInHours"`
@ -109,7 +103,6 @@ type Application struct {
SigninUrl string `xorm:"varchar(200)" json:"signinUrl"` SigninUrl string `xorm:"varchar(200)" json:"signinUrl"`
ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"` ForgetUrl string `xorm:"varchar(200)" json:"forgetUrl"`
AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"` AffiliationUrl string `xorm:"varchar(100)" json:"affiliationUrl"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"` TermsOfUse string `xorm:"varchar(100)" json:"termsOfUse"`
SignupHtml string `xorm:"mediumtext" json:"signupHtml"` SignupHtml string `xorm:"mediumtext" json:"signupHtml"`
SigninHtml string `xorm:"mediumtext" json:"signinHtml"` SigninHtml string `xorm:"mediumtext" json:"signinHtml"`
@ -130,9 +123,9 @@ func GetApplicationCount(owner, field, value string) (int64, error) {
return session.Count(&Application{}) return session.Count(&Application{})
} }
func GetOrganizationApplicationCount(owner, organization, field, value string) (int64, error) { func GetOrganizationApplicationCount(owner, Organization, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "") session := GetSession(owner, -1, -1, field, value, "", "")
return session.Where("organization = ? or is_shared = ? ", organization, true).Count(&Application{}) return session.Count(&Application{Organization: Organization})
} }
func GetApplications(owner string) ([]*Application, error) { func GetApplications(owner string) ([]*Application, error) {
@ -147,7 +140,7 @@ func GetApplications(owner string) ([]*Application, error) {
func GetOrganizationApplications(owner string, organization string) ([]*Application, error) { func GetOrganizationApplications(owner string, organization string) ([]*Application, error) {
applications := []*Application{} applications := []*Application{}
err := ormer.Engine.Desc("created_time").Where("organization = ? or is_shared = ? ", organization, true).Find(&applications, &Application{}) err := ormer.Engine.Desc("created_time").Find(&applications, &Application{Organization: organization})
if err != nil { if err != nil {
return applications, err return applications, err
} }
@ -169,7 +162,7 @@ func GetPaginationApplications(owner string, offset, limit int, field, value, so
func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Application, error) { func GetPaginationOrganizationApplications(owner, organization string, offset, limit int, field, value, sortField, sortOrder string) ([]*Application, error) {
applications := []*Application{} applications := []*Application{}
session := GetSession(owner, offset, limit, field, value, sortField, sortOrder) session := GetSession(owner, offset, limit, field, value, sortField, sortOrder)
err := session.Where("organization = ? or is_shared = ? ", organization, true).Find(&applications, &Application{}) err := session.Find(&applications, &Application{Organization: organization})
if err != nil { if err != nil {
return applications, err return applications, err
} }
@ -344,18 +337,12 @@ func getApplication(owner string, name string) (*Application, error) {
return nil, nil return nil, nil
} }
realApplicationName, sharedOrg := util.GetSharedOrgFromApp(name) application := Application{Owner: owner, Name: name}
application := Application{Owner: owner, Name: realApplicationName}
existed, err := ormer.Engine.Get(&application) existed, err := ormer.Engine.Get(&application)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if application.IsShared && sharedOrg != "" {
application.Organization = sharedOrg
}
if existed { if existed {
err = extendApplicationWithProviders(&application) err = extendApplicationWithProviders(&application)
if err != nil { if err != nil {
@ -441,18 +428,11 @@ func GetApplicationByUserId(userId string) (application *Application, err error)
func GetApplicationByClientId(clientId string) (*Application, error) { func GetApplicationByClientId(clientId string) (*Application, error) {
application := Application{} application := Application{}
existed, err := ormer.Engine.Where("client_id=?", clientId).Get(&application)
realClientId, sharedOrg := util.GetSharedOrgFromApp(clientId)
existed, err := ormer.Engine.Where("client_id=?", realClientId).Get(&application)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if application.IsShared && sharedOrg != "" {
application.Organization = sharedOrg
}
if existed { if existed {
err = extendApplicationWithProviders(&application) err = extendApplicationWithProviders(&application)
if err != nil { if err != nil {
@ -536,7 +516,7 @@ func GetMaskedApplication(application *Application, userId string) *Application
providerItems := []*ProviderItem{} providerItems := []*ProviderItem{}
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3" || providerItem.Provider.Category == "Captcha" || providerItem.Provider.Category == "SAML") { if providerItem.Provider != nil && (providerItem.Provider.Category == "OAuth" || providerItem.Provider.Category == "Web3" || providerItem.Provider.Category == "Captcha") {
providerItems = append(providerItems, providerItem) providerItems = append(providerItems, providerItem)
} }
} }
@ -646,10 +626,6 @@ func UpdateApplication(id string, application *Application) (bool, error) {
return false, err return false, err
} }
if application.IsShared == true && application.Organization != "built-in" {
return false, fmt.Errorf("only applications belonging to built-in organization can be shared")
}
for _, providerItem := range application.Providers { for _, providerItem := range application.Providers {
providerItem.Provider = nil providerItem.Provider = nil
} }
@ -723,15 +699,8 @@ func (application *Application) GetId() string {
} }
func (application *Application) IsRedirectUriValid(redirectUri string) bool { func (application *Application) IsRedirectUriValid(redirectUri string) bool {
isValid, err := util.IsValidOrigin(redirectUri) redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...)
if err != nil { for _, targetUri := range redirectUris {
panic(err)
}
if isValid {
return true
}
for _, targetUri := range application.RedirectUris {
targetUriRegex := regexp.MustCompile(targetUri) targetUriRegex := regexp.MustCompile(targetUri)
if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) { if targetUriRegex.MatchString(redirectUri) || strings.Contains(redirectUri, targetUri) {
return true return true

View File

@ -381,13 +381,7 @@ func CheckUserPassword(organization string, username string, password string, la
if err != nil { if err != nil {
return nil, err return nil, err
} }
err = checkPasswordExpired(user, lang)
if err != nil {
return nil, err
}
} }
return user, nil return user, nil
} }
@ -526,46 +520,11 @@ func CheckUsername(username string, lang string) string {
return "" return ""
} }
func CheckUsernameWithEmail(username string, lang string) string {
if username == "" {
return i18n.Translate(lang, "check:Empty username.")
} else if len(username) > 39 {
return i18n.Translate(lang, "check:Username is too long (maximum is 39 characters).")
}
// https://stackoverflow.com/questions/58726546/github-username-convention-using-regex
if !util.ReUserNameWithEmail.MatchString(username) {
return i18n.Translate(lang, "check:Username supports email format. Also The username may only contain alphanumeric characters, underlines or hyphens, cannot have consecutive hyphens or underlines, and cannot begin or end with a hyphen or underline. Also pay attention to the email format.")
}
return ""
}
func CheckUpdateUser(oldUser, user *User, lang string) string { func CheckUpdateUser(oldUser, user *User, lang string) string {
if oldUser.Name != user.Name { if oldUser.Name != user.Name {
organizationName := oldUser.Owner if msg := CheckUsername(user.Name, lang); msg != "" {
if organizationName == "" { return msg
organizationName = user.Owner
} }
organization, err := getOrganization("admin", organizationName)
if err != nil {
return err.Error()
}
if organization == nil {
return fmt.Sprintf(i18n.Translate(lang, "auth:The organization: %s does not exist"), organizationName)
}
if organization.UseEmailAsUsername {
if msg := CheckUsernameWithEmail(user.Name, lang); msg != "" {
return msg
}
} else {
if msg := CheckUsername(user.Name, lang); msg != "" {
return msg
}
}
if HasUserByField(user.Owner, "name", user.Name) { if HasUserByField(user.Owner, "name", user.Name) {
return i18n.Translate(lang, "check:Username already exists") return i18n.Translate(lang, "check:Username already exists")
} }
@ -580,11 +539,6 @@ func CheckUpdateUser(oldUser, user *User, lang string) string {
return i18n.Translate(lang, "check:Phone already exists") return i18n.Translate(lang, "check:Phone already exists")
} }
} }
if oldUser.IpWhitelist != user.IpWhitelist {
if err := CheckIpWhitelist(user.IpWhitelist, lang); err != nil {
return err.Error()
}
}
return "" return ""
} }

View File

@ -1,104 +0,0 @@
// Copyright 2024 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 (
"fmt"
"net"
"strings"
"github.com/casdoor/casdoor/i18n"
)
func CheckEntryIp(clientIp string, user *User, application *Application, organization *Organization, lang string) error {
entryIp := net.ParseIP(clientIp)
if entryIp == nil {
return fmt.Errorf(i18n.Translate(lang, "check:Failed to parse client IP: %s"), clientIp)
} else if entryIp.IsLoopback() {
return nil
}
var err error
if user != nil {
err = isEntryIpAllowd(user.IpWhitelist, entryIp, lang)
if err != nil {
return fmt.Errorf(err.Error() + user.Name)
}
}
if application != nil {
err = isEntryIpAllowd(application.IpWhitelist, entryIp, lang)
if err != nil {
application.IpRestriction = err.Error() + application.Name
return fmt.Errorf(err.Error() + application.Name)
} else {
application.IpRestriction = ""
}
if organization == nil && application.OrganizationObj != nil {
organization = application.OrganizationObj
}
}
if organization != nil {
err = isEntryIpAllowd(organization.IpWhitelist, entryIp, lang)
if err != nil {
organization.IpRestriction = err.Error() + organization.Name
return fmt.Errorf(err.Error() + organization.Name)
} else {
organization.IpRestriction = ""
}
}
return nil
}
func isEntryIpAllowd(ipWhitelistStr string, entryIp net.IP, lang string) error {
if ipWhitelistStr == "" {
return nil
}
ipWhitelist := strings.Split(ipWhitelistStr, ",")
for _, ip := range ipWhitelist {
_, ipNet, err := net.ParseCIDR(ip)
if err != nil {
return err
}
if ipNet == nil {
return fmt.Errorf(i18n.Translate(lang, "check:CIDR for IP: %s should not be empty"), entryIp.String())
}
if ipNet.Contains(entryIp) {
return nil
}
}
return fmt.Errorf(i18n.Translate(lang, "check:Your IP address: %s has been banned according to the configuration of: "), entryIp.String())
}
func CheckIpWhitelist(ipWhitelistStr string, lang string) error {
if ipWhitelistStr == "" {
return nil
}
ipWhiteList := strings.Split(ipWhitelistStr, ",")
for _, ip := range ipWhiteList {
if _, _, err := net.ParseCIDR(ip); err != nil {
return fmt.Errorf(i18n.Translate(lang, "check:%s does not meet the CIDR format requirements: %s"), ip, err.Error())
}
}
return nil
}

View File

@ -1,53 +0,0 @@
// Copyright 2024 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 (
"fmt"
"time"
"github.com/casdoor/casdoor/i18n"
"github.com/casdoor/casdoor/util"
)
func checkPasswordExpired(user *User, lang string) error {
organization, err := GetOrganizationByUser(user)
if err != nil {
return err
}
if organization == nil {
return fmt.Errorf(i18n.Translate(lang, "check:Organization does not exist"))
}
passwordExpireDays := organization.PasswordExpireDays
if passwordExpireDays <= 0 {
return nil
}
lastChangePasswordTime := user.LastChangePasswordTime
if lastChangePasswordTime == "" {
if user.CreatedTime == "" {
return fmt.Errorf(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
}
lastChangePasswordTime = user.CreatedTime
}
lastTime := util.String2Time(lastChangePasswordTime)
expireTime := lastTime.AddDate(0, 0, passwordExpireDays)
if time.Now().After(expireTime) {
return fmt.Errorf(i18n.Translate(lang, "check:Your password has expired. Please reset your password by clicking \"Forgot password\""))
}
return nil
}

View File

@ -52,9 +52,6 @@ func GetFailedSigninConfigByUser(user *User) (int, int, error) {
if err != nil { if err != nil {
return 0, 0, err return 0, 0, err
} }
if application == nil {
return 0, 0, fmt.Errorf("the application for user %s is not found", user.GetId())
}
failedSigninLimit := application.FailedSigninLimit failedSigninLimit := application.FailedSigninLimit
if failedSigninLimit == 0 { if failedSigninLimit == 0 {

View File

@ -25,12 +25,6 @@ type Dashboard struct {
ProviderCounts []int `json:"providerCounts"` ProviderCounts []int `json:"providerCounts"`
ApplicationCounts []int `json:"applicationCounts"` ApplicationCounts []int `json:"applicationCounts"`
SubscriptionCounts []int `json:"subscriptionCounts"` SubscriptionCounts []int `json:"subscriptionCounts"`
RoleCounts []int `json:"roleCounts"`
GroupCounts []int `json:"groupCounts"`
ResourceCounts []int `json:"resourceCounts"`
CertCounts []int `json:"certCounts"`
PermissionCounts []int `json:"permissionCounts"`
TransactionCounts []int `json:"transactionCounts"`
} }
func GetDashboard(owner string) (*Dashboard, error) { func GetDashboard(owner string) (*Dashboard, error) {
@ -44,12 +38,6 @@ func GetDashboard(owner string) (*Dashboard, error) {
ProviderCounts: make([]int, 31), ProviderCounts: make([]int, 31),
ApplicationCounts: make([]int, 31), ApplicationCounts: make([]int, 31),
SubscriptionCounts: make([]int, 31), SubscriptionCounts: make([]int, 31),
RoleCounts: make([]int, 31),
GroupCounts: make([]int, 31),
ResourceCounts: make([]int, 31),
CertCounts: make([]int, 31),
PermissionCounts: make([]int, 31),
TransactionCounts: make([]int, 31),
} }
organizations := []Organization{} organizations := []Organization{}
@ -57,15 +45,9 @@ func GetDashboard(owner string) (*Dashboard, error) {
providers := []Provider{} providers := []Provider{}
applications := []Application{} applications := []Application{}
subscriptions := []Subscription{} subscriptions := []Subscription{}
roles := []Role{}
groups := []Group{}
resources := []Resource{}
certs := []Cert{}
permissions := []Permission{}
transactions := []Transaction{}
var wg sync.WaitGroup var wg sync.WaitGroup
wg.Add(11) wg.Add(5)
go func() { go func() {
defer wg.Done() defer wg.Done()
if err := ormer.Engine.Find(&organizations, &Organization{Owner: owner}); err != nil { if err := ormer.Engine.Find(&organizations, &Organization{Owner: owner}); err != nil {
@ -104,50 +86,6 @@ func GetDashboard(owner string) (*Dashboard, error) {
panic(err) panic(err)
} }
}() }()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&roles, &Role{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&groups, &Group{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&resources, &Resource{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&certs, &Cert{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&permissions, &Permission{Owner: owner}); err != nil {
panic(err)
}
}()
go func() {
defer wg.Done()
if err := ormer.Engine.Find(&transactions, &Transaction{Owner: owner}); err != nil {
panic(err)
}
}()
wg.Wait() wg.Wait()
nowTime := time.Now() nowTime := time.Now()
@ -158,12 +96,6 @@ func GetDashboard(owner string) (*Dashboard, error) {
dashboard.ProviderCounts[30-i] = countCreatedBefore(providers, cutTime) dashboard.ProviderCounts[30-i] = countCreatedBefore(providers, cutTime)
dashboard.ApplicationCounts[30-i] = countCreatedBefore(applications, cutTime) dashboard.ApplicationCounts[30-i] = countCreatedBefore(applications, cutTime)
dashboard.SubscriptionCounts[30-i] = countCreatedBefore(subscriptions, cutTime) dashboard.SubscriptionCounts[30-i] = countCreatedBefore(subscriptions, cutTime)
dashboard.RoleCounts[30-i] = countCreatedBefore(roles, cutTime)
dashboard.GroupCounts[30-i] = countCreatedBefore(groups, cutTime)
dashboard.ResourceCounts[30-i] = countCreatedBefore(resources, cutTime)
dashboard.CertCounts[30-i] = countCreatedBefore(certs, cutTime)
dashboard.PermissionCounts[30-i] = countCreatedBefore(permissions, cutTime)
dashboard.TransactionCounts[30-i] = countCreatedBefore(transactions, cutTime)
} }
return dashboard, nil return dashboard, nil
} }
@ -206,48 +138,6 @@ func countCreatedBefore(objects interface{}, before time.Time) int {
count++ count++
} }
} }
case []Role:
for _, r := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", r.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Group:
for _, g := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", g.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Resource:
for _, r := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", r.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Cert:
for _, c := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", c.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Permission:
for _, p := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", p.CreatedTime)
if createdTime.Before(before) {
count++
}
}
case []Transaction:
for _, t := range obj {
createdTime, _ := time.Parse("2006-01-02T15:04:05-07:00", t.CreatedTime)
if createdTime.Before(before) {
count++
}
}
} }
return count return count
} }

View File

@ -48,16 +48,12 @@ type InitData struct {
Transactions []*Transaction `json:"transactions"` Transactions []*Transaction `json:"transactions"`
} }
var initDataNewOnly bool
func InitFromFile() { func InitFromFile() {
initDataFile := conf.GetConfigString("initDataFile") initDataFile := conf.GetConfigString("initDataFile")
if initDataFile == "" { if initDataFile == "" {
return return
} }
initDataNewOnly = conf.GetConfigBool("initDataNewOnly")
initData, err := readInitDataFromFile(initDataFile) initData, err := readInitDataFromFile(initDataFile)
if err != nil { if err != nil {
panic(err) panic(err)
@ -186,9 +182,6 @@ func readInitDataFromFile(filePath string) (*InitData, error) {
if organization.Tags == nil { if organization.Tags == nil {
organization.Tags = []string{} organization.Tags = []string{}
} }
if organization.AccountItems == nil {
organization.AccountItems = []*AccountItem{}
}
} }
for _, application := range data.Applications { for _, application := range data.Applications {
if application.Providers == nil { if application.Providers == nil {
@ -273,9 +266,6 @@ func initDefinedOrganization(organization *Organization) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteOrganization(organization) affected, err := deleteOrganization(organization)
if err != nil { if err != nil {
panic(err) panic(err)
@ -285,9 +275,7 @@ func initDefinedOrganization(organization *Organization) {
} }
} }
organization.CreatedTime = util.GetCurrentTime() organization.CreatedTime = util.GetCurrentTime()
if len(organization.AccountItems) == 0 { organization.AccountItems = getBuiltInAccountItems()
organization.AccountItems = getBuiltInAccountItems()
}
_, err = AddOrganization(organization) _, err = AddOrganization(organization)
if err != nil { if err != nil {
@ -302,9 +290,6 @@ func initDefinedApplication(application *Application) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteApplication(application) affected, err := deleteApplication(application)
if err != nil { if err != nil {
panic(err) panic(err)
@ -326,9 +311,6 @@ func initDefinedUser(user *User) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteUser(user) affected, err := deleteUser(user)
if err != nil { if err != nil {
panic(err) panic(err)
@ -355,9 +337,6 @@ func initDefinedCert(cert *Cert) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteCert(cert) affected, err := DeleteCert(cert)
if err != nil { if err != nil {
panic(err) panic(err)
@ -380,9 +359,6 @@ func initDefinedLdap(ldap *Ldap) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteLdap(ldap) affected, err := DeleteLdap(ldap)
if err != nil { if err != nil {
panic(err) panic(err)
@ -404,9 +380,6 @@ func initDefinedProvider(provider *Provider) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteProvider(provider) affected, err := DeleteProvider(provider)
if err != nil { if err != nil {
panic(err) panic(err)
@ -428,9 +401,6 @@ func initDefinedModel(model *Model) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteModel(model) affected, err := DeleteModel(model)
if err != nil { if err != nil {
panic(err) panic(err)
@ -453,9 +423,6 @@ func initDefinedPermission(permission *Permission) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := deletePermission(permission) affected, err := deletePermission(permission)
if err != nil { if err != nil {
panic(err) panic(err)
@ -478,9 +445,6 @@ func initDefinedPayment(payment *Payment) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeletePayment(payment) affected, err := DeletePayment(payment)
if err != nil { if err != nil {
panic(err) panic(err)
@ -503,9 +467,6 @@ func initDefinedProduct(product *Product) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteProduct(product) affected, err := DeleteProduct(product)
if err != nil { if err != nil {
panic(err) panic(err)
@ -528,9 +489,6 @@ func initDefinedResource(resource *Resource) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteResource(resource) affected, err := DeleteResource(resource)
if err != nil { if err != nil {
panic(err) panic(err)
@ -553,9 +511,6 @@ func initDefinedRole(role *Role) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteRole(role) affected, err := deleteRole(role)
if err != nil { if err != nil {
panic(err) panic(err)
@ -578,9 +533,6 @@ func initDefinedSyncer(syncer *Syncer) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteSyncer(syncer) affected, err := DeleteSyncer(syncer)
if err != nil { if err != nil {
panic(err) panic(err)
@ -603,9 +555,6 @@ func initDefinedToken(token *Token) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteToken(token) affected, err := DeleteToken(token)
if err != nil { if err != nil {
panic(err) panic(err)
@ -628,9 +577,6 @@ func initDefinedWebhook(webhook *Webhook) {
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteWebhook(webhook) affected, err := DeleteWebhook(webhook)
if err != nil { if err != nil {
panic(err) panic(err)
@ -652,9 +598,6 @@ func initDefinedGroup(group *Group) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := deleteGroup(group) affected, err := deleteGroup(group)
if err != nil { if err != nil {
panic(err) panic(err)
@ -676,9 +619,6 @@ func initDefinedAdapter(adapter *Adapter) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteAdapter(adapter) affected, err := DeleteAdapter(adapter)
if err != nil { if err != nil {
panic(err) panic(err)
@ -700,9 +640,6 @@ func initDefinedEnforcer(enforcer *Enforcer) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteEnforcer(enforcer) affected, err := DeleteEnforcer(enforcer)
if err != nil { if err != nil {
panic(err) panic(err)
@ -724,9 +661,6 @@ func initDefinedPlan(plan *Plan) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeletePlan(plan) affected, err := DeletePlan(plan)
if err != nil { if err != nil {
panic(err) panic(err)
@ -748,9 +682,6 @@ func initDefinedPricing(pricing *Pricing) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeletePricing(pricing) affected, err := DeletePricing(pricing)
if err != nil { if err != nil {
panic(err) panic(err)
@ -772,9 +703,6 @@ func initDefinedInvitation(invitation *Invitation) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteInvitation(invitation) affected, err := DeleteInvitation(invitation)
if err != nil { if err != nil {
panic(err) panic(err)
@ -810,9 +738,6 @@ func initDefinedSubscription(subscription *Subscription) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteSubscription(subscription) affected, err := DeleteSubscription(subscription)
if err != nil { if err != nil {
panic(err) panic(err)
@ -834,9 +759,6 @@ func initDefinedTransaction(transaction *Transaction) {
panic(err) panic(err)
} }
if existed != nil { if existed != nil {
if initDataNewOnly {
return
}
affected, err := DeleteTransaction(transaction) affected, err := DeleteTransaction(transaction)
if err != nil { if err != nil {
panic(err) panic(err)

View File

@ -32,7 +32,6 @@ type Ldap struct {
BaseDn string `xorm:"varchar(100)" json:"baseDn"` BaseDn string `xorm:"varchar(100)" json:"baseDn"`
Filter string `xorm:"varchar(200)" json:"filter"` Filter string `xorm:"varchar(200)" json:"filter"`
FilterFields []string `xorm:"varchar(100)" json:"filterFields"` FilterFields []string `xorm:"varchar(100)" json:"filterFields"`
DefaultGroup string `xorm:"varchar(100)" json:"defaultGroup"`
AutoSync int `json:"autoSync"` AutoSync int `json:"autoSync"`
LastSync string `xorm:"varchar(100)" json:"lastSync"` LastSync string `xorm:"varchar(100)" json:"lastSync"`
@ -149,7 +148,7 @@ func UpdateLdap(ldap *Ldap) (bool, error) {
} }
affected, err := ormer.Engine.ID(ldap.Id).Cols("owner", "server_name", "host", affected, err := ormer.Engine.ID(ldap.Id).Cols("owner", "server_name", "host",
"port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync", "default_group").Update(ldap) "port", "enable_ssl", "username", "password", "base_dn", "filter", "filter_fields", "auto_sync").Update(ldap)
if err != nil { if err != nil {
return false, nil return false, nil
} }

View File

@ -339,10 +339,6 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
Ldap: syncUser.Uuid, Ldap: syncUser.Uuid,
} }
if ldap.DefaultGroup != "" {
newUser.Groups = []string{ldap.DefaultGroup}
}
affected, err := AddUser(newUser) affected, err := AddUser(newUser)
if err != nil { if err != nil {
return nil, nil, err return nil, nil, err

View File

@ -44,18 +44,6 @@ type OidcDiscovery struct {
EndSessionEndpoint string `json:"end_session_endpoint"` EndSessionEndpoint string `json:"end_session_endpoint"`
} }
type WebFinger struct {
Subject string `json:"subject"`
Links []WebFingerLink `json:"links"`
Aliases *[]string `json:"aliases,omitempty"`
Properties *map[string]string `json:"properties,omitempty"`
}
type WebFingerLink struct {
Rel string `json:"rel"`
Href string `json:"href"`
}
func isIpAddress(host string) bool { func isIpAddress(host string) bool {
// Attempt to split the host and port, ignoring the error // Attempt to split the host and port, ignoring the error
hostWithoutPort, _, err := net.SplitHostPort(host) hostWithoutPort, _, err := net.SplitHostPort(host)
@ -124,7 +112,7 @@ func GetOidcDiscovery(host string) OidcDiscovery {
ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"}, ResponseModesSupported: []string{"query", "fragment", "login", "code", "link"},
GrantTypesSupported: []string{"password", "authorization_code"}, GrantTypesSupported: []string{"password", "authorization_code"},
SubjectTypesSupported: []string{"public"}, SubjectTypesSupported: []string{"public"},
IdTokenSigningAlgValuesSupported: []string{"RS256", "RS512", "ES256", "ES384", "ES512"}, IdTokenSigningAlgValuesSupported: []string{"RS256"},
ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"}, ScopesSupported: []string{"openid", "email", "profile", "address", "phone", "offline_access"},
ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"}, ClaimsSupported: []string{"iss", "ver", "sub", "aud", "iat", "exp", "id", "type", "displayName", "avatar", "permanentAvatar", "email", "phone", "location", "affiliation", "title", "homepage", "bio", "tag", "region", "language", "score", "ranking", "isOnline", "isAdmin", "isForbidden", "signupApplication", "ldap"},
RequestParameterSupported: true, RequestParameterSupported: true,
@ -172,43 +160,3 @@ func GetJsonWebKeySet() (jose.JSONWebKeySet, error) {
return jwks, nil return jwks, nil
} }
func GetWebFinger(resource string, rels []string, host string) (WebFinger, error) {
wf := WebFinger{}
resourceSplit := strings.Split(resource, ":")
if len(resourceSplit) != 2 {
return wf, fmt.Errorf("invalid resource")
}
resourceType := resourceSplit[0]
resourceValue := resourceSplit[1]
oidcDiscovery := GetOidcDiscovery(host)
switch resourceType {
case "acct":
user, err := GetUserByEmailOnly(resourceValue)
if err != nil {
return wf, err
}
if user == nil {
return wf, fmt.Errorf("user not found")
}
wf.Subject = resource
for _, rel := range rels {
if rel == "http://openid.net/specs/connect/1.0/issuer" {
wf.Links = append(wf.Links, WebFingerLink{
Rel: "http://openid.net/specs/connect/1.0/issuer",
Href: oidcDiscovery.Issuer,
})
}
}
}
return wf, nil
}

View File

@ -56,13 +56,10 @@ type Organization struct {
WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"` WebsiteUrl string `xorm:"varchar(100)" json:"websiteUrl"`
Logo string `xorm:"varchar(200)" json:"logo"` Logo string `xorm:"varchar(200)" json:"logo"`
LogoDark string `xorm:"varchar(200)" json:"logoDark"` LogoDark string `xorm:"varchar(200)" json:"logoDark"`
Favicon string `xorm:"varchar(200)" json:"favicon"` Favicon string `xorm:"varchar(100)" json:"favicon"`
PasswordType string `xorm:"varchar(100)" json:"passwordType"` PasswordType string `xorm:"varchar(100)" json:"passwordType"`
PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"` PasswordSalt string `xorm:"varchar(100)" json:"passwordSalt"`
PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"` PasswordOptions []string `xorm:"varchar(100)" json:"passwordOptions"`
PasswordObfuscatorType string `xorm:"varchar(100)" json:"passwordObfuscatorType"`
PasswordObfuscatorKey string `xorm:"varchar(100)" json:"passwordObfuscatorKey"`
PasswordExpireDays int `json:"passwordExpireDays"`
CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"` CountryCodes []string `xorm:"varchar(200)" json:"countryCodes"`
DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"` DefaultAvatar string `xorm:"varchar(200)" json:"defaultAvatar"`
DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"` DefaultApplication string `xorm:"varchar(100)" json:"defaultApplication"`
@ -72,21 +69,19 @@ type Organization struct {
MasterPassword string `xorm:"varchar(100)" json:"masterPassword"` MasterPassword string `xorm:"varchar(100)" json:"masterPassword"`
DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"` DefaultPassword string `xorm:"varchar(100)" json:"defaultPassword"`
MasterVerificationCode string `xorm:"varchar(100)" json:"masterVerificationCode"` MasterVerificationCode string `xorm:"varchar(100)" json:"masterVerificationCode"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
InitScore int `json:"initScore"` InitScore int `json:"initScore"`
EnableSoftDeletion bool `json:"enableSoftDeletion"` EnableSoftDeletion bool `json:"enableSoftDeletion"`
IsProfilePublic bool `json:"isProfilePublic"` IsProfilePublic bool `json:"isProfilePublic"`
UseEmailAsUsername bool `json:"useEmailAsUsername"` UseEmailAsUsername bool `json:"useEmailAsUsername"`
EnableTour bool `json:"enableTour"` EnableTour bool `json:"enableTour"`
IpRestriction string `json:"ipRestriction"`
MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"` MfaItems []*MfaItem `xorm:"varchar(300)" json:"mfaItems"`
AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"` AccountItems []*AccountItem `xorm:"varchar(5000)" json:"accountItems"`
} }
func GetOrganizationCount(owner, name, field, value string) (int64, error) { func GetOrganizationCount(owner, field, value string) (int64, error) {
session := GetSession(owner, -1, -1, field, value, "", "") session := GetSession(owner, -1, -1, field, value, "", "")
return session.Count(&Organization{Name: name}) return session.Count(&Organization{})
} }
func GetOrganizations(owner string, name ...string) ([]*Organization, error) { func GetOrganizations(owner string, name ...string) ([]*Organization, error) {
@ -324,7 +319,6 @@ func GetDefaultApplication(id string) (*Application, error) {
if defaultApplication == nil { if defaultApplication == nil {
return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication) return nil, fmt.Errorf("The default application: %s does not exist", organization.DefaultApplication)
} else { } else {
defaultApplication.Organization = organization.Name
return defaultApplication, nil return defaultApplication, nil
} }
} }

View File

@ -364,7 +364,7 @@ func GetAllActions(userId string) ([]string, error) {
res := []string{} res := []string{}
for _, enforcer := range enforcers { for _, enforcer := range enforcers {
items := enforcer.GetAllActions() items := enforcer.GetAllObjects()
res = append(res, items...) res = append(res, items...)
} }
return res, nil return res, nil

View File

@ -50,7 +50,7 @@ func maskPassword(recordString string) string {
} }
func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) { func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
clientIp := strings.Replace(util.GetClientIpFromRequest(ctx.Request), ": ", "", -1) ip := strings.Replace(util.GetIPFromRequest(ctx.Request), ": ", "", -1)
action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1) action := strings.Replace(ctx.Request.URL.Path, "/api/", "", -1)
requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"}) requestUri := util.FilterQuery(ctx.Request.RequestURI, []string{"accessToken"})
if len(requestUri) > 1000 { if len(requestUri) > 1000 {
@ -83,7 +83,7 @@ func NewRecord(ctx *context.Context) (*casvisorsdk.Record, error) {
record := casvisorsdk.Record{ record := casvisorsdk.Record{
Name: util.GenerateId(), Name: util.GenerateId(),
CreatedTime: util.GetCurrentTime(), CreatedTime: util.GetCurrentTime(),
ClientIp: clientIp, ClientIp: ip,
User: "", User: "",
Method: ctx.Request.Method, Method: ctx.Request.Method,
RequestUri: requestUri, RequestUri: requestUri,

View File

@ -36,7 +36,7 @@ type Resource struct {
FileType string `xorm:"varchar(100)" json:"fileType"` FileType string `xorm:"varchar(100)" json:"fileType"`
FileFormat string `xorm:"varchar(100)" json:"fileFormat"` FileFormat string `xorm:"varchar(100)" json:"fileFormat"`
FileSize int `json:"fileSize"` FileSize int `json:"fileSize"`
Url string `xorm:"varchar(500)" json:"url"` Url string `xorm:"varchar(255)" json:"url"`
Description string `xorm:"varchar(255)" json:"description"` Description string `xorm:"varchar(255)" json:"description"`
} }

View File

@ -26,7 +26,6 @@ import (
"errors" "errors"
"fmt" "fmt"
"io" "io"
"strings"
"time" "time"
"github.com/beevik/etree" "github.com/beevik/etree"
@ -66,11 +65,7 @@ func NewSamlResponse(application *Application, user *User, host string, certific
assertion.CreateAttr("IssueInstant", now) assertion.CreateAttr("IssueInstant", now)
assertion.CreateElement("saml:Issuer").SetText(host) assertion.CreateElement("saml:Issuer").SetText(host)
subject := assertion.CreateElement("saml:Subject") subject := assertion.CreateElement("saml:Subject")
nameIDValue := user.Name subject.CreateElement("saml:NameID").SetText(user.Name)
if application.UseEmailAsSamlNameId {
nameIDValue = user.Email
}
subject.CreateElement("saml:NameID").SetText(nameIDValue)
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation") subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer") subjectConfirmation.CreateAttr("Method", "urn:oasis:names:tc:SAML:2.0:cm:bearer")
subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData") subjectConfirmationData := subjectConfirmation.CreateElement("saml:SubjectConfirmationData")
@ -189,17 +184,17 @@ type NameIDFormat struct {
} }
type SingleSignOnService struct { type SingleSignOnService struct {
// XMLName xml.Name XMLName xml.Name
Binding string `xml:"Binding,attr"` Binding string `xml:"Binding,attr"`
Location string `xml:"Location,attr"` Location string `xml:"Location,attr"`
} }
type Attribute struct { type Attribute struct {
// XMLName xml.Name // XMLName xml.Name
Xmlns string `xml:"xmlns,attr"`
Name string `xml:"Name,attr"` Name string `xml:"Name,attr"`
NameFormat string `xml:"NameFormat,attr"` NameFormat string `xml:"NameFormat,attr"`
FriendlyName string `xml:"FriendlyName,attr"` FriendlyName string `xml:"FriendlyName,attr"`
Xmlns string `xml:"xmlns,attr"`
Values []string `xml:"AttributeValue"` Values []string `xml:"AttributeValue"`
} }
@ -223,13 +218,10 @@ func GetSamlMeta(application *Application, host string, enablePostBinding bool)
originFrontend, originBackend := getOriginFromHost(host) originFrontend, originBackend := getOriginFromHost(host)
idpLocation := "" idpLocation := ""
idpBinding := ""
if enablePostBinding { if enablePostBinding {
idpLocation = fmt.Sprintf("%s/api/saml/redirect/%s/%s", originBackend, application.Owner, application.Name) idpLocation = fmt.Sprintf("%s/api/saml/redirect/%s/%s", originBackend, application.Owner, application.Name)
idpBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST"
} else { } else {
idpLocation = fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name) idpLocation = fmt.Sprintf("%s/login/saml/authorize/%s/%s", originFrontend, application.Owner, application.Name)
idpBinding = "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect"
} }
d := IdpEntityDescriptor{ d := IdpEntityDescriptor{
@ -262,7 +254,7 @@ func GetSamlMeta(application *Application, host string, enablePostBinding bool)
{Xmlns: "urn:oasis:names:tc:SAML:2.0:assertion", Name: "Name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", FriendlyName: "Name"}, {Xmlns: "urn:oasis:names:tc:SAML:2.0:assertion", Name: "Name", NameFormat: "urn:oasis:names:tc:SAML:2.0:attrname-format:basic", FriendlyName: "Name"},
}, },
SingleSignOnService: SingleSignOnService{ SingleSignOnService: SingleSignOnService{
Binding: idpBinding, Binding: "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect",
Location: idpLocation, Location: idpLocation,
}, },
ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol", ProtocolSupportEnumeration: "urn:oasis:names:tc:SAML:2.0:protocol",
@ -277,38 +269,29 @@ func GetSamlMeta(application *Application, host string, enablePostBinding bool)
func GetSamlResponse(application *Application, user *User, samlRequest string, host string) (string, string, string, error) { func GetSamlResponse(application *Application, user *User, samlRequest string, host string) (string, string, string, error) {
// request type // request type
method := "GET" method := "GET"
samlRequest = strings.ReplaceAll(samlRequest, " ", "+")
// base64 decode // base64 decode
defated, err := base64.StdEncoding.DecodeString(samlRequest) defated, err := base64.StdEncoding.DecodeString(samlRequest)
if err != nil { if err != nil {
return "", "", "", fmt.Errorf("err: Failed to decode SAML request, %s", err.Error()) return "", "", "", fmt.Errorf("err: Failed to decode SAML request, %s", err.Error())
} }
var requestByte []byte // decompress
var buffer bytes.Buffer
rdr := flate.NewReader(bytes.NewReader(defated))
if strings.Contains(string(defated), "xmlns:") { for {
requestByte = defated _, err = io.CopyN(&buffer, rdr, 1024)
} else { if err != nil {
// decompress if err == io.EOF {
var buffer bytes.Buffer break
rdr := flate.NewReader(bytes.NewReader(defated))
for {
_, err = io.CopyN(&buffer, rdr, 1024)
if err != nil {
if err == io.EOF {
break
}
return "", "", "", err
} }
return "", "", "", err
} }
requestByte = buffer.Bytes()
} }
var authnRequest saml.AuthNRequest var authnRequest saml.AuthNRequest
err = xml.Unmarshal(requestByte, &authnRequest) err = xml.Unmarshal(buffer.Bytes(), &authnRequest)
if err != nil { if err != nil {
return "", "", "", fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request, %s", err.Error()) return "", "", "", fmt.Errorf("err: Failed to unmarshal AuthnRequest, please check the SAML request, %s", err.Error())
} }
@ -403,7 +386,7 @@ func GetSamlResponse(application *Application, user *User, samlRequest string, h
} }
// NewSamlResponse11 return a saml1.1 response(not 2.0) // NewSamlResponse11 return a saml1.1 response(not 2.0)
func NewSamlResponse11(application *Application, user *User, requestID string, host string) (*etree.Element, error) { func NewSamlResponse11(user *User, requestID string, host string) (*etree.Element, error) {
samlResponse := &etree.Element{ samlResponse := &etree.Element{
Space: "samlp", Space: "samlp",
Tag: "Response", Tag: "Response",
@ -447,11 +430,7 @@ func NewSamlResponse11(application *Application, user *User, requestID string, h
// nameIdentifier inside subject // nameIdentifier inside subject
nameIdentifier := subject.CreateElement("saml:NameIdentifier") nameIdentifier := subject.CreateElement("saml:NameIdentifier")
// nameIdentifier.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress") // nameIdentifier.CreateAttr("Format", "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress")
if application.UseEmailAsSamlNameId { nameIdentifier.SetText(user.Name)
nameIdentifier.SetText(user.Email)
} else {
nameIdentifier.SetText(user.Name)
}
// subjectConfirmation inside subject // subjectConfirmation inside subject
subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation") subjectConfirmation := subject.CreateElement("saml:SubjectConfirmation")
@ -460,11 +439,7 @@ func NewSamlResponse11(application *Application, user *User, requestID string, h
attributeStatement := assertion.CreateElement("saml:AttributeStatement") attributeStatement := assertion.CreateElement("saml:AttributeStatement")
subjectInAttribute := attributeStatement.CreateElement("saml:Subject") subjectInAttribute := attributeStatement.CreateElement("saml:Subject")
nameIdentifierInAttribute := subjectInAttribute.CreateElement("saml:NameIdentifier") nameIdentifierInAttribute := subjectInAttribute.CreateElement("saml:NameIdentifier")
if application.UseEmailAsSamlNameId { nameIdentifierInAttribute.SetText(user.Name)
nameIdentifierInAttribute.SetText(user.Email)
} else {
nameIdentifierInAttribute.SetText(user.Name)
}
subjectConfirmationInAttribute := subjectInAttribute.CreateElement("saml:SubjectConfirmation") subjectConfirmationInAttribute := subjectInAttribute.CreateElement("saml:SubjectConfirmation")
subjectConfirmationInAttribute.CreateElement("saml:ConfirmationMethod").SetText("urn:oasis:names:tc:SAML:1.0:cm:artifact") subjectConfirmationInAttribute.CreateElement("saml:ConfirmationMethod").SetText("urn:oasis:names:tc:SAML:1.0:cm:artifact")

View File

@ -30,13 +30,6 @@ import (
var isCloudIntranet bool var isCloudIntranet bool
const (
ProviderTypeGoogleCloudStorage = "Google Cloud Storage"
ProviderTypeTencentCloudCOS = "Tencent Cloud COS"
ProviderTypeAzureBlob = "Azure Blob"
ProviderTypeLocalFileSystem = "Local File System"
)
func init() { func init() {
isCloudIntranet = conf.GetConfigBool("isCloudIntranet") isCloudIntranet = conf.GetConfigBool("isCloudIntranet")
} }
@ -87,28 +80,27 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath) objectKey := util.UrlJoin(util.GetUrlPath(provider.Domain), escapedPath)
host := "" host := ""
if provider.Type != ProviderTypeLocalFileSystem { if provider.Type != "Local File System" {
// provider.Domain = "https://cdn.casbin.com/casdoor/" // provider.Domain = "https://cdn.casbin.com/casdoor/"
host = util.GetUrlHost(provider.Domain) host = util.GetUrlHost(provider.Domain)
} else { } else {
// provider.Domain = "http://localhost:8000" or "https://door.casdoor.com" // provider.Domain = "http://localhost:8000" or "https://door.casdoor.com"
host = util.UrlJoin(provider.Domain, "/files") host = util.UrlJoin(provider.Domain, "/files")
} }
if provider.Type == ProviderTypeAzureBlob || provider.Type == ProviderTypeGoogleCloudStorage { if provider.Type == "Azure Blob" {
host = util.UrlJoin(host, provider.Bucket) host = util.UrlJoin(host, provider.Bucket)
} }
fileUrl := "" fileUrl := ""
if host != "" { if host != "" {
// fileUrl = util.UrlJoin(host, escapePath(objectKey)) fileUrl = util.UrlJoin(host, escapePath(objectKey))
fileUrl = util.UrlJoin(host, objectKey)
} }
// if fileUrl != "" && hasTimestamp { if fileUrl != "" && hasTimestamp {
// fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime()) fileUrl = fmt.Sprintf("%s?t=%s", fileUrl, util.GetCurrentUnixTime())
// } }
if provider.Type == ProviderTypeTencentCloudCOS { if provider.Type == "Tencent Cloud COS" {
objectKey = escapePath(objectKey) objectKey = escapePath(objectKey)
} }
@ -117,18 +109,7 @@ func GetUploadFileUrl(provider *Provider, fullFilePath string, hasTimestamp bool
func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) { func getStorageProvider(provider *Provider, lang string) (oss.StorageInterface, error) {
endpoint := getProviderEndpoint(provider) endpoint := getProviderEndpoint(provider)
certificate := "" storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint)
if provider.Category == "Storage" && provider.Type == "Casdoor" {
cert, err := GetCert(util.GetId(provider.Owner, provider.Cert))
if err != nil {
return nil, err
}
if cert == nil {
return nil, fmt.Errorf("no cert for %s", provider.Cert)
}
certificate = cert.Certificate
}
storageProvider, err := storage.GetStorageProvider(provider.Type, provider.ClientId, provider.ClientSecret, provider.RegionId, provider.Bucket, endpoint, certificate, provider.Content)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -154,15 +135,15 @@ func uploadFile(provider *Provider, fullFilePath string, fileBuffer *bytes.Buffe
} }
fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true) fileUrl, objectKey := GetUploadFileUrl(provider, fullFilePath, true)
objectKeyRefined := refineObjectKey(provider, objectKey)
object, err := storageProvider.Put(objectKeyRefined, fileBuffer) objectKeyRefined := objectKey
if err != nil { if provider.Type == "Google Cloud Storage" {
return "", "", err objectKeyRefined = strings.TrimPrefix(objectKeyRefined, "/")
} }
if provider.Type == "Casdoor" { _, err = storageProvider.Put(objectKeyRefined, fileBuffer)
fileUrl = object.Path if err != nil {
return "", "", err
} }
return fileUrl, objectKey, nil return fileUrl, objectKey, nil
@ -203,13 +184,5 @@ func DeleteFile(provider *Provider, objectKey string, lang string) error {
return err return err
} }
objectKeyRefined := refineObjectKey(provider, objectKey) return storageProvider.Delete(objectKey)
return storageProvider.Delete(objectKeyRefined)
}
func refineObjectKey(provider *Provider, objectKey string) string {
if provider.Type == ProviderTypeGoogleCloudStorage {
return strings.TrimPrefix(objectKey, "/")
}
return objectKey
} }

View File

@ -102,6 +102,14 @@ func GetTokenByAccessToken(accessToken string) (*Token, error) {
return nil, err return nil, err
} }
if !existed {
token = Token{AccessToken: accessToken}
existed, err = ormer.Engine.Get(&token)
if err != nil {
return nil, err
}
}
if !existed { if !existed {
return nil, nil return nil, nil
} }
@ -115,6 +123,14 @@ func GetTokenByRefreshToken(refreshToken string) (*Token, error) {
return nil, err return nil, err
} }
if !existed {
token = Token{RefreshToken: refreshToken}
existed, err = ormer.Engine.Get(&token)
if err != nil {
return nil, err
}
}
if !existed { if !existed {
return nil, nil return nil, nil
} }

View File

@ -277,11 +277,12 @@ func GetValidationBySaml(samlRequest string, host string) (string, string, error
if err != nil { if err != nil {
return "", "", err return "", "", err
} }
if application == nil { if application == nil {
return "", "", fmt.Errorf("the application for user %s is not found", userId) return "", "", fmt.Errorf("the application for user %s is not found", userId)
} }
samlResponse, err := NewSamlResponse11(application, user, request.RequestID, host) samlResponse, err := NewSamlResponse11(user, request.RequestID, host)
if err != nil { if err != nil {
return "", "", err return "", "", err
} }

View File

@ -17,7 +17,6 @@ package object
import ( import (
"fmt" "fmt"
"reflect" "reflect"
"strings"
"time" "time"
"github.com/casdoor/casdoor/util" "github.com/casdoor/casdoor/util"
@ -129,7 +128,7 @@ type UserWithoutThirdIdp struct {
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"` LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
SigninWrongTimes int `json:"signinWrongTimes"` SigninWrongTimes int `json:"signinWrongTimes"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"` // ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
} }
type ClaimsShort struct { type ClaimsShort struct {
@ -255,8 +254,6 @@ func getUserWithoutThirdIdp(user *User) *UserWithoutThirdIdp {
LastSigninWrongTime: user.LastSigninWrongTime, LastSigninWrongTime: user.LastSigninWrongTime,
SigninWrongTimes: user.SigninWrongTimes, SigninWrongTimes: user.SigninWrongTimes,
ManagedAccounts: user.ManagedAccounts,
} }
return res return res
@ -368,10 +365,6 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
}, },
} }
if application.IsShared {
claims.Audience = []string{application.ClientId + "-org-" + user.Owner}
}
var token *jwt.Token var token *jwt.Token
var refreshToken *jwt.Token var refreshToken *jwt.Token
@ -379,52 +372,36 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
application.TokenFormat = "JWT" application.TokenFormat = "JWT"
} }
var jwtMethod jwt.SigningMethod
if application.TokenSigningMethod == "RS256" {
jwtMethod = jwt.SigningMethodRS256
} else if application.TokenSigningMethod == "RS512" {
jwtMethod = jwt.SigningMethodRS512
} else if application.TokenSigningMethod == "ES256" {
jwtMethod = jwt.SigningMethodES256
} else if application.TokenSigningMethod == "ES512" {
jwtMethod = jwt.SigningMethodES512
} else if application.TokenSigningMethod == "ES384" {
jwtMethod = jwt.SigningMethodES384
} else {
jwtMethod = jwt.SigningMethodRS256
}
// the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name // the JWT token length in "JWT-Empty" mode will be very short, as User object only has two properties: owner and name
if application.TokenFormat == "JWT" { if application.TokenFormat == "JWT" {
claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims) claimsWithoutThirdIdp := getClaimsWithoutThirdIdp(claims)
token = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp) token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime) claimsWithoutThirdIdp.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsWithoutThirdIdp.TokenType = "refresh-token" claimsWithoutThirdIdp.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwtMethod, claimsWithoutThirdIdp) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsWithoutThirdIdp)
} else if application.TokenFormat == "JWT-Empty" { } else if application.TokenFormat == "JWT-Empty" {
claimsShort := getShortClaims(claims) claimsShort := getShortClaims(claims)
token = jwt.NewWithClaims(jwtMethod, claimsShort) token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime) claimsShort.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsShort.TokenType = "refresh-token" claimsShort.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwtMethod, claimsShort) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsShort)
} else if application.TokenFormat == "JWT-Custom" { } else if application.TokenFormat == "JWT-Custom" {
claimsCustom := getClaimsCustom(claims, application.TokenFields) claimsCustom := getClaimsCustom(claims, application.TokenFields)
token = jwt.NewWithClaims(jwtMethod, claimsCustom) token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsCustom)
refreshClaims := getClaimsCustom(claims, application.TokenFields) refreshClaims := getClaimsCustom(claims, application.TokenFields)
refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime) refreshClaims["exp"] = jwt.NewNumericDate(refreshExpireTime)
refreshClaims["TokenType"] = "refresh-token" refreshClaims["TokenType"] = "refresh-token"
refreshToken = jwt.NewWithClaims(jwtMethod, refreshClaims) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, refreshClaims)
} else if application.TokenFormat == "JWT-Standard" { } else if application.TokenFormat == "JWT-Standard" {
claimsStandard := getStandardClaims(claims) claimsStandard := getStandardClaims(claims)
token = jwt.NewWithClaims(jwtMethod, claimsStandard) token = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
claimsStandard.ExpiresAt = jwt.NewNumericDate(refreshExpireTime) claimsStandard.ExpiresAt = jwt.NewNumericDate(refreshExpireTime)
claimsStandard.TokenType = "refresh-token" claimsStandard.TokenType = "refresh-token"
refreshToken = jwt.NewWithClaims(jwtMethod, claimsStandard) refreshToken = jwt.NewWithClaims(jwt.SigningMethodRS256, claimsStandard)
} else { } else {
return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat) return "", "", "", fmt.Errorf("unknown application TokenFormat: %s", application.TokenFormat)
} }
@ -442,57 +419,34 @@ func generateJwtToken(application *Application, user *User, nonce string, scope
} }
} }
var ( // RSA private key
tokenString string key, err := jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
refreshTokenString string
key interface{}
)
if strings.Contains(application.TokenSigningMethod, "RS") || application.TokenSigningMethod == "" {
// RSA private key
key, err = jwt.ParseRSAPrivateKeyFromPEM([]byte(cert.PrivateKey))
} else if strings.Contains(application.TokenSigningMethod, "ES") {
// ES private key
key, err = jwt.ParseECPrivateKeyFromPEM([]byte(cert.PrivateKey))
} else if strings.Contains(application.TokenSigningMethod, "Ed") {
// Ed private key
key, err = jwt.ParseEdPrivateKeyFromPEM([]byte(cert.PrivateKey))
}
if err != nil { if err != nil {
return "", "", "", err return "", "", "", err
} }
token.Header["kid"] = cert.Name token.Header["kid"] = cert.Name
tokenString, err = token.SignedString(key) tokenString, err := token.SignedString(key)
if err != nil { if err != nil {
return "", "", "", err return "", "", "", err
} }
refreshTokenString, err = refreshToken.SignedString(key) refreshTokenString, err := refreshToken.SignedString(key)
return tokenString, refreshTokenString, name, err return tokenString, refreshTokenString, name, err
} }
func ParseJwtToken(token string, cert *Cert) (*Claims, error) { func ParseJwtToken(token string, cert *Cert) (*Claims, error) {
t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) { t, err := jwt.ParseWithClaims(token, &Claims{}, func(token *jwt.Token) (interface{}, error) {
var ( if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
certificate interface{} return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
err error }
)
if cert.Certificate == "" { if cert.Certificate == "" {
return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert) return nil, fmt.Errorf("the certificate field should not be empty for the cert: %v", cert)
} }
if _, ok := token.Method.(*jwt.SigningMethodRSA); ok { // RSA certificate
// RSA certificate certificate, err := jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
certificate, err = jwt.ParseRSAPublicKeyFromPEM([]byte(cert.Certificate))
} else if _, ok := token.Method.(*jwt.SigningMethodECDSA); ok {
// ES certificate
certificate, err = jwt.ParseECPublicKeyFromPEM([]byte(cert.Certificate))
} else {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -332,9 +332,6 @@ func RefreshToken(grantType string, refreshToken string, scope string, clientId
if err != nil { if err != nil {
return nil, err return nil, err
} }
if user == nil {
return "", fmt.Errorf("The user: %s doesn't exist", util.GetId(application.Organization, token.User))
}
if user.IsForbidden { if user.IsForbidden {
return &TokenError{ return &TokenError{
@ -431,26 +428,22 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if token == nil { if token == nil {
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidGrant, Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("authorization code: [%s] is invalid", code), ErrorDescription: "authorization code is invalid",
}, nil }, nil
} }
if token.CodeIsUsed { if token.CodeIsUsed {
// anti replay attacks // anti replay attacks
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidGrant, Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("authorization code has been used for token: [%s]", token.GetId()), ErrorDescription: "authorization code has been used",
}, nil }, nil
} }
if token.CodeChallenge != "" { if token.CodeChallenge != "" && pkceChallenge(verifier) != token.CodeChallenge {
challengeAnswer := pkceChallenge(verifier) return nil, &TokenError{
if challengeAnswer != token.CodeChallenge { Error: InvalidGrant,
return nil, &TokenError{ ErrorDescription: "verifier is invalid",
Error: InvalidGrant, }, nil
ErrorDescription: fmt.Sprintf("verifier is invalid, challengeAnswer: [%s], token.CodeChallenge: [%s]", challengeAnswer, token.CodeChallenge),
}, nil
}
} }
if application.ClientSecret != clientSecret { if application.ClientSecret != clientSecret {
@ -459,13 +452,13 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if token.CodeChallenge == "" { if token.CodeChallenge == "" {
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidClient, Error: InvalidClient,
ErrorDescription: fmt.Sprintf("client_secret is invalid for application: [%s], token.CodeChallenge: empty", application.GetId()), ErrorDescription: "client_secret is invalid",
}, nil }, nil
} else { } else {
if clientSecret != "" { if clientSecret != "" {
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidClient, Error: InvalidClient,
ErrorDescription: fmt.Sprintf("client_secret is invalid for application: [%s], token.CodeChallenge: [%s]", application.GetId(), token.CodeChallenge), ErrorDescription: "client_secret is invalid",
}, nil }, nil
} }
} }
@ -474,16 +467,15 @@ func GetAuthorizationCodeToken(application *Application, clientSecret string, co
if application.Name != token.Application { if application.Name != token.Application {
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidGrant, Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("the token is for wrong application (client_id), application.Name: [%s], token.Application: [%s]", application.Name, token.Application), ErrorDescription: "the token is for wrong application (client_id)",
}, nil }, nil
} }
nowUnix := time.Now().Unix() if time.Now().Unix() > token.CodeExpireIn {
if nowUnix > token.CodeExpireIn {
// code must be used within 5 minutes // code must be used within 5 minutes
return nil, &TokenError{ return nil, &TokenError{
Error: InvalidGrant, Error: InvalidGrant,
ErrorDescription: fmt.Sprintf("authorization code has expired, nowUnix: [%s], token.CodeExpireIn: [%s]", time.Unix(nowUnix, 0).Format(time.RFC3339), time.Unix(token.CodeExpireIn, 0).Format(time.RFC3339)), ErrorDescription: "authorization code has expired",
}, nil }, nil
} }
return token, nil, nil return token, nil, nil

View File

@ -18,20 +18,16 @@ import (
"fmt" "fmt"
"strings" "strings"
"github.com/casdoor/casdoor/util"
"github.com/golang-jwt/jwt/v4" "github.com/golang-jwt/jwt/v4"
) )
type ClaimsStandard struct { type ClaimsStandard struct {
*UserShort *UserShort
EmailVerified bool `json:"email_verified,omitempty"` Gender string `json:"gender,omitempty"`
PhoneNumber string `json:"phone_number,omitempty"` TokenType string `json:"tokenType,omitempty"`
PhoneNumberVerified bool `json:"phone_number_verified,omitempty"` Nonce string `json:"nonce,omitempty"`
Gender string `json:"gender,omitempty"` Scope string `json:"scope,omitempty"`
TokenType string `json:"tokenType,omitempty"` Address OIDCAddress `json:"address,omitempty"`
Nonce string `json:"nonce,omitempty"`
Scope string `json:"scope,omitempty"`
Address OIDCAddress `json:"address,omitempty"`
jwt.RegisteredClaims jwt.RegisteredClaims
} }
@ -47,14 +43,12 @@ func getStreetAddress(user *User) string {
func getStandardClaims(claims Claims) ClaimsStandard { func getStandardClaims(claims Claims) ClaimsStandard {
res := ClaimsStandard{ res := ClaimsStandard{
UserShort: getShortUser(claims.User), UserShort: getShortUser(claims.User),
EmailVerified: claims.User.EmailVerified,
TokenType: claims.TokenType, TokenType: claims.TokenType,
Nonce: claims.Nonce, Nonce: claims.Nonce,
Scope: claims.Scope, Scope: claims.Scope,
RegisteredClaims: claims.RegisteredClaims, RegisteredClaims: claims.RegisteredClaims,
} }
res.Phone = ""
var scopes []string var scopes []string
if strings.Contains(claims.Scope, ",") { if strings.Contains(claims.Scope, ",") {
@ -68,15 +62,6 @@ func getStandardClaims(claims Claims) ClaimsStandard {
res.Address = OIDCAddress{StreetAddress: getStreetAddress(claims.User)} res.Address = OIDCAddress{StreetAddress: getStreetAddress(claims.User)}
} else if scope == "profile" { } else if scope == "profile" {
res.Gender = claims.User.Gender res.Gender = claims.User.Gender
} else if scope == "phone" && claims.User.Phone != "" {
res.PhoneNumberVerified = true
phoneNumber, ok := util.GetE164Number(claims.User.Phone, claims.User.CountryCode)
if !ok {
res.PhoneNumberVerified = false
} else {
res.PhoneNumber = phoneNumber
}
} }
} }

View File

@ -200,14 +200,12 @@ type User struct {
Permissions []*Permission `json:"permissions"` Permissions []*Permission `json:"permissions"`
Groups []string `xorm:"groups varchar(1000)" json:"groups"` Groups []string `xorm:"groups varchar(1000)" json:"groups"`
LastChangePasswordTime string `xorm:"varchar(100)" json:"lastChangePasswordTime"` LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"`
LastSigninWrongTime string `xorm:"varchar(100)" json:"lastSigninWrongTime"` SigninWrongTimes int `json:"signinWrongTimes"`
SigninWrongTimes int `json:"signinWrongTimes"`
ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"` ManagedAccounts []ManagedAccount `xorm:"managedAccounts blob" json:"managedAccounts"`
MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"` MfaAccounts []MfaAccount `xorm:"mfaAccounts blob" json:"mfaAccounts"`
NeedUpdatePassword bool `json:"needUpdatePassword"` NeedUpdatePassword bool `json:"needUpdatePassword"`
IpWhitelist string `xorm:"varchar(200)" json:"ipWhitelist"`
} }
type Userinfo struct { type Userinfo struct {
@ -691,14 +689,14 @@ func UpdateUser(id string, user *User, columns []string, isAdmin bool) (bool, er
"owner", "display_name", "avatar", "first_name", "last_name", "owner", "display_name", "avatar", "first_name", "last_name",
"location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application", "location", "address", "country_code", "region", "language", "affiliation", "title", "id_card_type", "id_card", "homepage", "bio", "tag", "language", "gender", "birthday", "education", "score", "karma", "ranking", "signup_application",
"is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids", "mfaAccounts", "is_admin", "is_forbidden", "is_deleted", "hash", "is_default_avatar", "properties", "webauthnCredentials", "managedAccounts", "face_ids", "mfaAccounts",
"signin_wrong_times", "last_change_password_time", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled", "signin_wrong_times", "last_signin_wrong_time", "groups", "access_key", "access_secret", "mfa_phone_enabled", "mfa_email_enabled",
"github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs", "github", "google", "qq", "wechat", "facebook", "dingtalk", "weibo", "gitee", "linkedin", "wecom", "lark", "gitlab", "adfs",
"baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon", "baidu", "alipay", "casdoor", "infoflow", "apple", "azuread", "azureadb2c", "slack", "steam", "bilibili", "okta", "douyin", "line", "amazon",
"auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox", "auth0", "battlenet", "bitbucket", "box", "cloudfoundry", "dailymotion", "deezer", "digitalocean", "discord", "dropbox",
"eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup", "eveonline", "fitbit", "gitea", "heroku", "influxcloud", "instagram", "intercom", "kakao", "lastfm", "mailru", "meetup",
"microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud", "microsoftonline", "naver", "nextcloud", "onedrive", "oura", "patreon", "paypal", "salesforce", "shopify", "soundcloud",
"spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo", "spotify", "strava", "stripe", "type", "tiktok", "tumblr", "twitch", "twitter", "typetalk", "uber", "vk", "wepay", "xero", "yahoo",
"yammer", "yandex", "zoom", "custom", "need_update_password", "ip_whitelist", "yammer", "yandex", "zoom", "custom", "need_update_password",
} }
} }
if isAdmin { if isAdmin {
@ -817,10 +815,6 @@ func AddUser(user *User) (bool, error) {
user.UpdateUserPassword(organization) user.UpdateUserPassword(organization)
} }
if user.CreatedTime == "" {
user.CreatedTime = util.GetCurrentTime()
}
err = user.UpdateUserHash() err = user.UpdateUserHash()
if err != nil { if err != nil {
return false, err return false, err
@ -956,17 +950,7 @@ func DeleteUser(user *User) (bool, error) {
return false, err return false, err
} }
organization, err := GetOrganizationByUser(user) return deleteUser(user)
if err != nil {
return false, err
}
if organization != nil && organization.EnableSoftDeletion {
user.IsDeleted = true
user.DeletedTime = util.GetCurrentTime()
return UpdateUser(user.GetId(), user, []string{"is_deleted", "deleted_time"}, false)
} else {
return deleteUser(user)
}
} }
func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) { func GetUserInfo(user *User, scope string, aud string, host string) (*Userinfo, error) {
@ -1154,7 +1138,7 @@ func (user *User) IsApplicationAdmin(application *Application) bool {
return false return false
} }
return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin() || (user.IsAdmin && application.IsShared) return (user.Owner == application.Organization && user.IsAdmin) || user.IsGlobalAdmin()
} }
func (user *User) IsGlobalAdmin() bool { func (user *User) IsGlobalAdmin() bool {

View File

@ -271,213 +271,113 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
if oldUser.Owner != newUser.Owner { if oldUser.Owner != newUser.Owner {
item := GetAccountItemByName("Organization", organization) item := GetAccountItemByName("Organization", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Owner = oldUser.Owner
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Name != newUser.Name { if oldUser.Name != newUser.Name {
item := GetAccountItemByName("Name", organization) item := GetAccountItemByName("Name", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Name = oldUser.Name
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Id != newUser.Id { if oldUser.Id != newUser.Id {
item := GetAccountItemByName("ID", organization) item := GetAccountItemByName("ID", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Id = oldUser.Id
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.DisplayName != newUser.DisplayName { if oldUser.DisplayName != newUser.DisplayName {
item := GetAccountItemByName("Display name", organization) item := GetAccountItemByName("Display name", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.DisplayName = oldUser.DisplayName
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Avatar != newUser.Avatar { if oldUser.Avatar != newUser.Avatar {
item := GetAccountItemByName("Avatar", organization) item := GetAccountItemByName("Avatar", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Avatar = oldUser.Avatar
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Type != newUser.Type { if oldUser.Type != newUser.Type {
item := GetAccountItemByName("User type", organization) item := GetAccountItemByName("User type", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Type = oldUser.Type
} else {
itemsChanged = append(itemsChanged, item)
}
} }
// The password is *** when not modified // The password is *** when not modified
if oldUser.Password != newUser.Password && newUser.Password != "***" { if oldUser.Password != newUser.Password && newUser.Password != "***" {
item := GetAccountItemByName("Password", organization) item := GetAccountItemByName("Password", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Password = oldUser.Password
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Email != newUser.Email { if oldUser.Email != newUser.Email {
item := GetAccountItemByName("Email", organization) item := GetAccountItemByName("Email", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Email = oldUser.Email
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Phone != newUser.Phone { if oldUser.Phone != newUser.Phone {
item := GetAccountItemByName("Phone", organization) item := GetAccountItemByName("Phone", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Phone = oldUser.Phone
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.CountryCode != newUser.CountryCode { if oldUser.CountryCode != newUser.CountryCode {
item := GetAccountItemByName("Country code", organization) item := GetAccountItemByName("Country code", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.CountryCode = oldUser.CountryCode
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Region != newUser.Region { if oldUser.Region != newUser.Region {
item := GetAccountItemByName("Country/Region", organization) item := GetAccountItemByName("Country/Region", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Region = oldUser.Region
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Location != newUser.Location { if oldUser.Location != newUser.Location {
item := GetAccountItemByName("Location", organization) item := GetAccountItemByName("Location", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Location = oldUser.Location
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Affiliation != newUser.Affiliation { if oldUser.Affiliation != newUser.Affiliation {
item := GetAccountItemByName("Affiliation", organization) item := GetAccountItemByName("Affiliation", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Affiliation = oldUser.Affiliation
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Title != newUser.Title { if oldUser.Title != newUser.Title {
item := GetAccountItemByName("Title", organization) item := GetAccountItemByName("Title", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Title = oldUser.Title
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Homepage != newUser.Homepage { if oldUser.Homepage != newUser.Homepage {
item := GetAccountItemByName("Homepage", organization) item := GetAccountItemByName("Homepage", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Homepage = oldUser.Homepage
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Bio != newUser.Bio { if oldUser.Bio != newUser.Bio {
item := GetAccountItemByName("Bio", organization) item := GetAccountItemByName("Bio", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Bio = oldUser.Bio
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Tag != newUser.Tag { if oldUser.Tag != newUser.Tag {
item := GetAccountItemByName("Tag", organization) item := GetAccountItemByName("Tag", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Tag = oldUser.Tag
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.SignupApplication != newUser.SignupApplication { if oldUser.SignupApplication != newUser.SignupApplication {
item := GetAccountItemByName("Signup application", organization) item := GetAccountItemByName("Signup application", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.SignupApplication = oldUser.SignupApplication
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Gender != newUser.Gender { if oldUser.Gender != newUser.Gender {
item := GetAccountItemByName("Gender", organization) item := GetAccountItemByName("Gender", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Gender = oldUser.Gender
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Birthday != newUser.Birthday { if oldUser.Birthday != newUser.Birthday {
item := GetAccountItemByName("Birthday", organization) item := GetAccountItemByName("Birthday", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Birthday = oldUser.Birthday
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Education != newUser.Education { if oldUser.Education != newUser.Education {
item := GetAccountItemByName("Education", organization) item := GetAccountItemByName("Education", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Education = oldUser.Education
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.IdCard != newUser.IdCard { if oldUser.IdCard != newUser.IdCard {
item := GetAccountItemByName("ID card", organization) item := GetAccountItemByName("ID card", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.IdCard = oldUser.IdCard
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.IdCardType != newUser.IdCardType { if oldUser.IdCardType != newUser.IdCardType {
item := GetAccountItemByName("ID card type", organization) item := GetAccountItemByName("ID card type", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.IdCardType = oldUser.IdCardType
} else {
itemsChanged = append(itemsChanged, item)
}
} }
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties) oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
newUserPropertiesJson, _ := json.Marshal(newUser.Properties) newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) { if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
item := GetAccountItemByName("Properties", organization) item := GetAccountItemByName("Properties", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Properties = oldUser.Properties
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.PreferredMfaType != newUser.PreferredMfaType { if oldUser.PreferredMfaType != newUser.PreferredMfaType {
item := GetAccountItemByName("Multi-factor authentication", organization) item := GetAccountItemByName("Multi-factor authentication", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.PreferredMfaType = oldUser.PreferredMfaType
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Groups == nil { if oldUser.Groups == nil {
@ -490,11 +390,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
newUserGroupsJson, _ := json.Marshal(newUser.Groups) newUserGroupsJson, _ := json.Marshal(newUser.Groups)
if string(oldUserGroupsJson) != string(newUserGroupsJson) { if string(oldUserGroupsJson) != string(newUserGroupsJson) {
item := GetAccountItemByName("Groups", organization) item := GetAccountItemByName("Groups", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Groups = oldUser.Groups
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Address == nil { if oldUser.Address == nil {
@ -508,125 +404,65 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, lang str
newUserAddressJson, _ := json.Marshal(newUser.Address) newUserAddressJson, _ := json.Marshal(newUser.Address)
if string(oldUserAddressJson) != string(newUserAddressJson) { if string(oldUserAddressJson) != string(newUserAddressJson) {
item := GetAccountItemByName("Address", organization) item := GetAccountItemByName("Address", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Address = oldUser.Address
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if newUser.FaceIds != nil { if newUser.FaceIds != nil {
item := GetAccountItemByName("Face ID", organization) item := GetAccountItemByName("Face ID", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.FaceIds = oldUser.FaceIds
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.IsAdmin != newUser.IsAdmin { if oldUser.IsAdmin != newUser.IsAdmin {
item := GetAccountItemByName("Is admin", organization) item := GetAccountItemByName("Is admin", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.IsAdmin = oldUser.IsAdmin
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.IsForbidden != newUser.IsForbidden { if oldUser.IsForbidden != newUser.IsForbidden {
item := GetAccountItemByName("Is forbidden", organization) item := GetAccountItemByName("Is forbidden", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.IsForbidden = oldUser.IsForbidden
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.IsDeleted != newUser.IsDeleted { if oldUser.IsDeleted != newUser.IsDeleted {
item := GetAccountItemByName("Is deleted", organization) item := GetAccountItemByName("Is deleted", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.IsDeleted = oldUser.IsDeleted
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.NeedUpdatePassword != newUser.NeedUpdatePassword { if oldUser.NeedUpdatePassword != newUser.NeedUpdatePassword {
item := GetAccountItemByName("Need update password", organization) item := GetAccountItemByName("Need update password", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.NeedUpdatePassword = oldUser.NeedUpdatePassword
} else {
itemsChanged = append(itemsChanged, item)
}
}
if oldUser.IpWhitelist != newUser.IpWhitelist {
item := GetAccountItemByName("IP whitelist", organization)
if item == nil {
newUser.IpWhitelist = oldUser.IpWhitelist
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Balance != newUser.Balance { if oldUser.Balance != newUser.Balance {
item := GetAccountItemByName("Balance", organization) item := GetAccountItemByName("Balance", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Balance = oldUser.Balance
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Score != newUser.Score { if oldUser.Score != newUser.Score {
item := GetAccountItemByName("Score", organization) item := GetAccountItemByName("Score", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Score = oldUser.Score
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Karma != newUser.Karma { if oldUser.Karma != newUser.Karma {
item := GetAccountItemByName("Karma", organization) item := GetAccountItemByName("Karma", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Karma = oldUser.Karma
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Language != newUser.Language { if oldUser.Language != newUser.Language {
item := GetAccountItemByName("Language", organization) item := GetAccountItemByName("Language", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Language = oldUser.Language
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Ranking != newUser.Ranking { if oldUser.Ranking != newUser.Ranking {
item := GetAccountItemByName("Ranking", organization) item := GetAccountItemByName("Ranking", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Ranking = oldUser.Ranking
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Currency != newUser.Currency { if oldUser.Currency != newUser.Currency {
item := GetAccountItemByName("Currency", organization) item := GetAccountItemByName("Currency", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Currency = oldUser.Currency
} else {
itemsChanged = append(itemsChanged, item)
}
} }
if oldUser.Hash != newUser.Hash { if oldUser.Hash != newUser.Hash {
item := GetAccountItemByName("Hash", organization) item := GetAccountItemByName("Hash", organization)
if item == nil { itemsChanged = append(itemsChanged, item)
newUser.Hash = oldUser.Hash
} else {
itemsChanged = append(itemsChanged, item)
}
} }
for _, accountItem := range itemsChanged { for _, accountItem := range itemsChanged {

View File

@ -166,76 +166,19 @@ func AddToVerificationRecord(user *User, provider *Provider, remoteAddr, recordT
return nil return nil
} }
func filterRecordIn24Hours(record *VerificationRecord) *VerificationRecord {
if record == nil {
return nil
}
now := time.Now().Unix()
if now-record.Time > 60*60*24 {
return nil
}
return record
}
func getVerificationRecord(dest string) (*VerificationRecord, error) { func getVerificationRecord(dest string) (*VerificationRecord, error) {
record := &VerificationRecord{} var record VerificationRecord
record.Receiver = dest record.Receiver = dest
has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(record) has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(&record)
if err != nil { if err != nil {
return nil, err return nil, err
} }
record = filterRecordIn24Hours(record)
if record == nil {
has = false
}
if !has {
record = &VerificationRecord{}
record.Receiver = dest
has, err = ormer.Engine.Desc("time").Get(record)
if err != nil {
return nil, err
}
record = filterRecordIn24Hours(record)
if record == nil {
has = false
}
if !has {
return nil, nil
}
return record, nil
}
return record, nil
}
func getUnusedVerificationRecord(dest string) (*VerificationRecord, error) {
record := &VerificationRecord{}
record.Receiver = dest
has, err := ormer.Engine.Desc("time").Where("is_used = false").Get(record)
if err != nil {
return nil, err
}
record = filterRecordIn24Hours(record)
if record == nil {
has = false
}
if !has { if !has {
return nil, nil return nil, nil
} }
return record, nil return &record, nil
} }
func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult, error) { func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult, error) {
@ -244,9 +187,7 @@ func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult
return nil, err return nil, err
} }
if record == nil { if record == nil {
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet!")}, nil return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet, or has already been used!")}, nil
} else if record.IsUsed {
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has already been used!")}, nil
} }
timeoutInMinutes, err := conf.GetConfigInt64("verificationCodeTimeout") timeoutInMinutes, err := conf.GetConfigInt64("verificationCodeTimeout")
@ -255,6 +196,9 @@ func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult
} }
now := time.Now().Unix() now := time.Now().Unix()
if now-record.Time > timeoutInMinutes*60*10 {
return &VerifyResult{noRecordError, i18n.Translate(lang, "verification:The verification code has not been sent yet!")}, nil
}
if now-record.Time > timeoutInMinutes*60 { if now-record.Time > timeoutInMinutes*60 {
return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeoutInMinutes)}, nil return &VerifyResult{timeoutError, fmt.Sprintf(i18n.Translate(lang, "verification:You should verify your code in %d min!"), timeoutInMinutes)}, nil
} }
@ -267,7 +211,7 @@ func CheckVerificationCode(dest string, code string, lang string) (*VerifyResult
} }
func DisableVerificationCode(dest string) error { func DisableVerificationCode(dest string) error {
record, err := getUnusedVerificationRecord(dest) record, err := getVerificationRecord(dest)
if record == nil || err != nil { if record == nil || err != nil {
return nil return nil
} }

View File

@ -56,7 +56,7 @@ func getSubject(ctx *context.Context) (string, string) {
return util.GetOwnerAndNameFromId(username) return util.GetOwnerAndNameFromId(username)
} }
func getObject(ctx *context.Context) (string, string, error) { func getObject(ctx *context.Context) (string, string) {
method := ctx.Request.Method method := ctx.Request.Method
path := ctx.Request.URL.Path path := ctx.Request.URL.Path
@ -65,13 +65,13 @@ func getObject(ctx *context.Context) (string, string, error) {
if ctx.Input.Query("id") == "/" { if ctx.Input.Query("id") == "/" {
adapterId := ctx.Input.Query("adapterId") adapterId := ctx.Input.Query("adapterId")
if adapterId != "" { if adapterId != "" {
return util.GetOwnerAndNameFromIdWithError(adapterId) return util.GetOwnerAndNameFromIdNoCheck(adapterId)
} }
} else { } else {
// query == "?id=built-in/admin" // query == "?id=built-in/admin"
id := ctx.Input.Query("id") id := ctx.Input.Query("id")
if id != "" { if id != "" {
return util.GetOwnerAndNameFromIdWithError(id) return util.GetOwnerAndNameFromIdNoCheck(id)
} }
} }
} }
@ -80,34 +80,34 @@ func getObject(ctx *context.Context) (string, string, error) {
// query == "?id=built-in/admin" // query == "?id=built-in/admin"
id := ctx.Input.Query("id") id := ctx.Input.Query("id")
if id != "" { if id != "" {
return util.GetOwnerAndNameFromIdWithError(id) return util.GetOwnerAndNameFromIdNoCheck(id)
} }
} }
owner := ctx.Input.Query("owner") owner := ctx.Input.Query("owner")
if owner != "" { if owner != "" {
return owner, "", nil return owner, ""
} }
return "", "", nil return "", ""
} else { } else {
if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" { if path == "/api/add-policy" || path == "/api/remove-policy" || path == "/api/update-policy" {
id := ctx.Input.Query("id") id := ctx.Input.Query("id")
if id != "" { if id != "" {
return util.GetOwnerAndNameFromIdWithError(id) return util.GetOwnerAndNameFromIdNoCheck(id)
} }
} }
body := ctx.Input.RequestBody body := ctx.Input.RequestBody
if len(body) == 0 { if len(body) == 0 {
return ctx.Request.Form.Get("owner"), ctx.Request.Form.Get("name"), nil return ctx.Request.Form.Get("owner"), ctx.Request.Form.Get("name")
} }
var obj Object var obj Object
err := json.Unmarshal(body, &obj) err := json.Unmarshal(body, &obj)
if err != nil { if err != nil {
// this is not error // panic(err)
return "", "", nil return "", ""
} }
if path == "/api/delete-resource" { if path == "/api/delete-resource" {
@ -117,7 +117,7 @@ func getObject(ctx *context.Context) (string, string, error) {
} }
} }
return obj.Owner, obj.Name, nil return obj.Owner, obj.Name
} }
} }
@ -183,12 +183,7 @@ func ApiFilter(ctx *context.Context) {
objOwner, objName := "", "" objOwner, objName := "", ""
if urlPath != "/api/get-app-login" && urlPath != "/api/get-resource" { if urlPath != "/api/get-app-login" && urlPath != "/api/get-resource" {
var err error objOwner, objName = getObject(ctx)
objOwner, objName, err = getObject(ctx)
if err != nil {
responseError(ctx, err.Error())
return
}
} }
if strings.HasPrefix(urlPath, "/api/notify-payment") { if strings.HasPrefix(urlPath, "/api/notify-payment") {

View File

@ -16,7 +16,6 @@ package routers
import ( import (
"fmt" "fmt"
"strings"
"github.com/beego/beego/context" "github.com/beego/beego/context"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
@ -24,10 +23,6 @@ import (
) )
func AutoSigninFilter(ctx *context.Context) { func AutoSigninFilter(ctx *context.Context) {
urlPath := ctx.Request.URL.Path
if strings.HasPrefix(urlPath, "/api/login/oauth/access_token") {
return
}
//if getSessionUser(ctx) != "" { //if getSessionUser(ctx) != "" {
// return // return
//} //}
@ -72,17 +67,6 @@ func AutoSigninFilter(ctx *context.Context) {
return return
} }
accessKey := ctx.Input.Query("accessKey")
accessSecret := ctx.Input.Query("accessSecret")
if accessKey != "" && accessSecret != "" {
userId, err := getUsernameByKeys(ctx)
if err != nil {
responseError(ctx, err.Error())
}
setSessionUser(ctx, userId)
}
// "/page?clientId=123&clientSecret=456" // "/page?clientId=123&clientSecret=456"
userId, err := getUsernameByClientIdSecret(ctx) userId, err := getUsernameByClientIdSecret(ctx)
if err != nil { if err != nil {

View File

@ -16,11 +16,11 @@ package routers
import ( import (
"net/http" "net/http"
"strings"
"github.com/beego/beego/context" "github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf" "github.com/casdoor/casdoor/conf"
"github.com/casdoor/casdoor/object" "github.com/casdoor/casdoor/object"
"github.com/casdoor/casdoor/util"
) )
const ( const (
@ -48,17 +48,7 @@ func CorsFilter(ctx *context.Context) {
originHostname := getHostname(origin) originHostname := getHostname(origin)
host := removePort(ctx.Request.Host) host := removePort(ctx.Request.Host)
if origin == "null" { if strings.HasPrefix(origin, "http://localhost") || strings.HasPrefix(origin, "https://localhost") || strings.HasPrefix(origin, "http://127.0.0.1") || strings.HasPrefix(origin, "http://casdoor-app") || strings.Contains(origin, ".chromiumapp.org") {
origin = ""
}
isValid, err := util.IsValidOrigin(origin)
if err != nil {
ctx.ResponseWriter.WriteHeader(http.StatusForbidden)
responseError(ctx, err.Error())
return
}
if isValid {
setCorsHeaders(ctx, origin) setCorsHeaders(ctx, origin)
return return
} }

View File

@ -174,8 +174,6 @@ func initAPI() {
beego.Router("/api/get-all-actions", &controllers.ApiController{}, "GET:GetAllActions") beego.Router("/api/get-all-actions", &controllers.ApiController{}, "GET:GetAllActions")
beego.Router("/api/get-all-roles", &controllers.ApiController{}, "GET:GetAllRoles") beego.Router("/api/get-all-roles", &controllers.ApiController{}, "GET:GetAllRoles")
beego.Router("/api/run-casbin-command", &controllers.ApiController{}, "GET:RunCasbinCommand")
beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions") beego.Router("/api/get-sessions", &controllers.ApiController{}, "GET:GetSessions")
beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession") beego.Router("/api/get-session", &controllers.ApiController{}, "GET:GetSingleSession")
beego.Router("/api/update-session", &controllers.ApiController{}, "POST:UpdateSession") beego.Router("/api/update-session", &controllers.ApiController{}, "POST:UpdateSession")
@ -292,7 +290,6 @@ func initAPI() {
beego.Router("/.well-known/openid-configuration", &controllers.RootController{}, "GET:GetOidcDiscovery") beego.Router("/.well-known/openid-configuration", &controllers.RootController{}, "GET:GetOidcDiscovery")
beego.Router("/.well-known/jwks", &controllers.RootController{}, "*:GetJwks") beego.Router("/.well-known/jwks", &controllers.RootController{}, "*:GetJwks")
beego.Router("/.well-known/webfinger", &controllers.RootController{}, "GET:GetWebFinger")
beego.Router("/cas/:organization/:application/serviceValidate", &controllers.RootController{}, "GET:CasServiceValidate") beego.Router("/cas/:organization/:application/serviceValidate", &controllers.RootController{}, "GET:CasServiceValidate")
beego.Router("/cas/:organization/:application/proxyValidate", &controllers.RootController{}, "GET:CasProxyValidate") beego.Router("/cas/:organization/:application/proxyValidate", &controllers.RootController{}, "GET:CasProxyValidate")

View File

@ -43,10 +43,6 @@ func getWebBuildFolder() string {
return path return path
} }
if util.FileExist(filepath.Join(frontendBaseDir, "index.html")) {
return frontendBaseDir
}
path = filepath.Join(frontendBaseDir, "web/build") path = filepath.Join(frontendBaseDir, "web/build")
return path return path
} }
@ -62,7 +58,7 @@ func fastAutoSignin(ctx *context.Context) (string, error) {
redirectUri := ctx.Input.Query("redirect_uri") redirectUri := ctx.Input.Query("redirect_uri")
scope := ctx.Input.Query("scope") scope := ctx.Input.Query("scope")
state := ctx.Input.Query("state") state := ctx.Input.Query("state")
nonce := ctx.Input.Query("nonce") nonce := ""
codeChallenge := ctx.Input.Query("code_challenge") codeChallenge := ctx.Input.Query("code_challenge")
if clientId == "" || responseType != "code" || redirectUri == "" { if clientId == "" || responseType != "code" || redirectUri == "" {
return "", nil return "", nil

View File

@ -1,64 +0,0 @@
// Copyright 2024 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 routers
import (
"fmt"
"sync"
"time"
"github.com/beego/beego/context"
"github.com/casdoor/casdoor/conf"
)
var (
inactiveTimeoutMinutes int64
requestTimeMap sync.Map
)
func init() {
var err error
inactiveTimeoutMinutes, err = conf.GetConfigInt64("inactiveTimeoutMinutes")
if err != nil {
inactiveTimeoutMinutes = 0
}
}
func timeoutLogout(ctx *context.Context, sessionId string) {
requestTimeMap.Delete(sessionId)
ctx.Input.CruSession.Set("username", "")
ctx.Input.CruSession.Set("accessToken", "")
ctx.Input.CruSession.Delete("SessionData")
responseError(ctx, fmt.Sprintf(T(ctx, "auth:Timeout for inactivity of %d minutes"), inactiveTimeoutMinutes))
}
func TimeoutFilter(ctx *context.Context) {
if inactiveTimeoutMinutes <= 0 {
return
}
owner, name := getSubject(ctx)
if owner == "anonymous" || name == "anonymous" {
return
}
sessionId := ctx.Input.CruSession.SessionID()
currentTime := time.Now()
preRequestTime, has := requestTimeMap.Load(sessionId)
requestTimeMap.Store(sessionId, currentTime)
if has && preRequestTime.(time.Time).Add(time.Minute*time.Duration(inactiveTimeoutMinutes)).Before(currentTime) {
timeoutLogout(ctx, sessionId)
}
}

View File

@ -1,19 +0,0 @@
package storage
import (
"github.com/casdoor/oss"
"github.com/casdoor/oss/casdoor"
)
func NewCasdoorStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string, cert string, content string) oss.StorageInterface {
sp := casdoor.New(&casdoor.Config{
clientId,
clientSecret,
endpoint,
cert,
region,
content,
bucket,
})
return sp
}

View File

@ -16,7 +16,7 @@ package storage
import "github.com/casdoor/oss" import "github.com/casdoor/oss"
func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string, cert string, content string) (oss.StorageInterface, error) { func GetStorageProvider(providerType string, clientId string, clientSecret string, region string, bucket string, endpoint string) (oss.StorageInterface, error) {
switch providerType { switch providerType {
case "Local File System": case "Local File System":
return NewLocalFileSystemStorageProvider(), nil return NewLocalFileSystemStorageProvider(), nil
@ -36,8 +36,6 @@ func GetStorageProvider(providerType string, clientId string, clientSecret strin
return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil return NewGoogleCloudStorageProvider(clientSecret, bucket, endpoint), nil
case "Synology": case "Synology":
return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil return NewSynologyNasStorageProvider(clientId, clientSecret, endpoint), nil
case "Casdoor":
return NewCasdoorStorageProvider(providerType, clientId, clientSecret, region, bucket, endpoint, cert, content), nil
} }
return nil, nil return nil, nil

View File

@ -23,50 +23,50 @@ import (
"github.com/beego/beego/logs" "github.com/beego/beego/logs"
) )
func getIpInfo(clientIp string) string { func GetIPInfo(clientIP string) string {
if clientIp == "" { if clientIP == "" {
return "" return ""
} }
ips := strings.Split(clientIp, ",") ips := strings.Split(clientIP, ",")
res := strings.TrimSpace(ips[0]) res := ""
//res := "" for i := range ips {
//for i := range ips { ip := strings.TrimSpace(ips[i])
// ip := strings.TrimSpace(ips[i]) // desc := GetDescFromIP(ip)
// ipstr := fmt.Sprintf("%s: %s", ip, "") ipstr := fmt.Sprintf("%s: %s", ip, "")
// if i != len(ips)-1 { if i != len(ips)-1 {
// res += ipstr + " -> " res += ipstr + " -> "
// } else { } else {
// res += ipstr res += ipstr
// } }
//} }
return res return res
} }
func GetClientIpFromRequest(req *http.Request) string { func GetIPFromRequest(req *http.Request) string {
clientIp := req.Header.Get("x-forwarded-for") clientIP := req.Header.Get("x-forwarded-for")
if clientIp == "" { if clientIP == "" {
ipPort := strings.Split(req.RemoteAddr, ":") ipPort := strings.Split(req.RemoteAddr, ":")
if len(ipPort) >= 1 && len(ipPort) <= 2 { if len(ipPort) >= 1 && len(ipPort) <= 2 {
clientIp = ipPort[0] clientIP = ipPort[0]
} else if len(ipPort) > 2 { } else if len(ipPort) > 2 {
idx := strings.LastIndex(req.RemoteAddr, ":") idx := strings.LastIndex(req.RemoteAddr, ":")
clientIp = req.RemoteAddr[0:idx] clientIP = req.RemoteAddr[0:idx]
clientIp = strings.TrimLeft(clientIp, "[") clientIP = strings.TrimLeft(clientIP, "[")
clientIp = strings.TrimRight(clientIp, "]") clientIP = strings.TrimRight(clientIP, "]")
} }
} }
return getIpInfo(clientIp) return GetIPInfo(clientIP)
} }
func LogInfo(ctx *context.Context, f string, v ...interface{}) { func LogInfo(ctx *context.Context, f string, v ...interface{}) {
ipString := fmt.Sprintf("(%s) ", GetClientIpFromRequest(ctx.Request)) ipString := fmt.Sprintf("(%s) ", GetIPFromRequest(ctx.Request))
logs.Info(ipString+f, v...) logs.Info(ipString+f, v...)
} }
func LogWarning(ctx *context.Context, f string, v ...interface{}) { func LogWarning(ctx *context.Context, f string, v ...interface{}) {
ipString := fmt.Sprintf("(%s) ", GetClientIpFromRequest(ctx.Request)) ipString := fmt.Sprintf("(%s) ", GetIPFromRequest(ctx.Request))
logs.Warning(ipString+f, v...) logs.Warning(ipString+f, v...)
} }

View File

@ -1,76 +0,0 @@
// Copyright 2024 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 util
import (
"crypto/aes"
"crypto/cipher"
"crypto/des"
"encoding/hex"
"fmt"
)
func unPaddingPkcs7(s []byte) []byte {
length := len(s)
if length == 0 {
return s
}
unPadding := int(s[length-1])
return s[:(length - unPadding)]
}
func decryptDesOrAes(passwordCipher string, block cipher.Block) (string, error) {
passwordCipherBytes, err := hex.DecodeString(passwordCipher)
if err != nil {
return "", err
}
if len(passwordCipherBytes) < block.BlockSize() {
return "", fmt.Errorf("the password ciphertext should contain a random hexadecimal string of length %d at the beginning", block.BlockSize()*2)
}
iv := passwordCipherBytes[:block.BlockSize()]
password := make([]byte, len(passwordCipherBytes)-block.BlockSize())
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(password, passwordCipherBytes[block.BlockSize():])
return string(unPaddingPkcs7(password)), nil
}
func GetUnobfuscatedPassword(passwordObfuscatorType string, passwordObfuscatorKey string, passwordCipher string) (string, error) {
if passwordObfuscatorType == "Plain" || passwordObfuscatorType == "" {
return passwordCipher, nil
} else if passwordObfuscatorType == "DES" || passwordObfuscatorType == "AES" {
key, err := hex.DecodeString(passwordObfuscatorKey)
if err != nil {
return "", err
}
var block cipher.Block
if passwordObfuscatorType == "DES" {
block, err = des.NewCipher(key)
} else {
block, err = aes.NewCipher(key)
}
if err != nil {
return "", err
}
return decryptDesOrAes(passwordCipher, block)
} else {
return "", fmt.Errorf("unsupported password obfuscator type: %s", passwordObfuscatorType)
}
}

View File

@ -131,15 +131,6 @@ func GetOwnerAndNameFromId(id string) (string, string) {
return tokens[0], tokens[1] return tokens[0], tokens[1]
} }
func GetOwnerAndNameFromIdWithError(id string) (string, string, error) {
tokens := strings.Split(id, "/")
if len(tokens) != 2 {
return "", "", errors.New("GetOwnerAndNameFromId() error, wrong token count for ID: " + id)
}
return tokens[0], tokens[1], nil
}
func GetOwnerFromId(id string) string { func GetOwnerFromId(id string) string {
tokens := strings.Split(id, "/") tokens := strings.Split(id, "/")
if len(tokens) != 2 { if len(tokens) != 2 {
@ -163,16 +154,6 @@ func GetOwnerAndNameAndOtherFromId(id string) (string, string, string) {
return tokens[0], tokens[1], tokens[2] return tokens[0], tokens[1], tokens[2]
} }
func GetSharedOrgFromApp(rawName string) (name string, organization string) {
name = rawName
splitName := strings.Split(rawName, "-org-")
if len(splitName) >= 2 {
organization = splitName[len(splitName)-1]
name = splitName[0]
}
return name, organization
}
func GenerateId() string { func GenerateId() string {
return uuid.NewString() return uuid.NewString()
} }

View File

@ -17,7 +17,6 @@ package util
import ( import (
"fmt" "fmt"
"net/mail" "net/mail"
"net/url"
"regexp" "regexp"
"strings" "strings"
@ -25,11 +24,10 @@ import (
) )
var ( var (
rePhone *regexp.Regexp rePhone *regexp.Regexp
ReWhiteSpace *regexp.Regexp ReWhiteSpace *regexp.Regexp
ReFieldWhiteList *regexp.Regexp ReFieldWhiteList *regexp.Regexp
ReUserName *regexp.Regexp ReUserName *regexp.Regexp
ReUserNameWithEmail *regexp.Regexp
) )
func init() { func init() {
@ -37,7 +35,6 @@ func init() {
ReWhiteSpace, _ = regexp.Compile(`\s`) ReWhiteSpace, _ = regexp.Compile(`\s`)
ReFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`) ReFieldWhiteList, _ = regexp.Compile(`^[A-Za-z0-9]+$`)
ReUserName, _ = regexp.Compile("^[a-zA-Z0-9]+([-._][a-zA-Z0-9]+)*$") ReUserName, _ = regexp.Compile("^[a-zA-Z0-9]+([-._][a-zA-Z0-9]+)*$")
ReUserNameWithEmail, _ = regexp.Compile(`^([a-zA-Z0-9]+([-._][a-zA-Z0-9]+)*)|([a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,})$`) // Add support for email formats
} }
func IsEmailValid(email string) bool { func IsEmailValid(email string) bool {
@ -54,9 +51,6 @@ func IsPhoneValid(phone string, countryCode string) bool {
} }
func IsPhoneAllowInRegin(countryCode string, allowRegions []string) bool { func IsPhoneAllowInRegin(countryCode string, allowRegions []string) bool {
if ContainsString(allowRegions, "All") {
return true
}
return ContainsString(allowRegions, countryCode) return ContainsString(allowRegions, countryCode)
} }
@ -103,21 +97,3 @@ func GetCountryCode(prefix string, phone string) (string, error) {
func FilterField(field string) bool { func FilterField(field string) bool {
return ReFieldWhiteList.MatchString(field) return ReFieldWhiteList.MatchString(field)
} }
func IsValidOrigin(origin string) (bool, error) {
urlObj, err := url.Parse(origin)
if err != nil {
return false, err
}
if urlObj == nil {
return false, nil
}
originHostOnly := ""
if urlObj.Host != "" {
originHostOnly = fmt.Sprintf("%s://%s", urlObj.Scheme, urlObj.Hostname())
}
res := originHostOnly == "http://localhost" || originHostOnly == "https://localhost" || originHostOnly == "http://127.0.0.1" || originHostOnly == "http://casdoor-app" || strings.HasSuffix(originHostOnly, ".chromiumapp.org")
return res, nil
}

View File

@ -27,7 +27,6 @@
"copy-to-clipboard": "^3.3.1", "copy-to-clipboard": "^3.3.1",
"core-js": "^3.25.0", "core-js": "^3.25.0",
"craco-less": "^2.0.0", "craco-less": "^2.0.0",
"crypto-js": "^4.2.0",
"echarts": "^5.4.3", "echarts": "^5.4.3",
"ethers": "5.6.9", "ethers": "5.6.9",
"face-api.js": "^0.22.2", "face-api.js": "^0.22.2",

View File

@ -56,11 +56,9 @@ class AdapterListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -344,8 +344,7 @@ class App extends Component {
window.location.pathname.startsWith("/cas") || window.location.pathname.startsWith("/cas") ||
window.location.pathname.startsWith("/select-plan") || window.location.pathname.startsWith("/select-plan") ||
window.location.pathname.startsWith("/buy-plan") || window.location.pathname.startsWith("/buy-plan") ||
window.location.pathname.startsWith("/qrcode") || window.location.pathname.startsWith("/qrcode") ;
window.location.pathname.startsWith("/captcha");
} }
onClick = ({key}) => { onClick = ({key}) => {
@ -362,11 +361,7 @@ class App extends Component {
if (this.isDoorPages()) { if (this.isDoorPages()) {
return ( return (
<ConfigProvider theme={{ <ConfigProvider theme={{
token: { algorithm: Setting.getAlgorithm(["default"]),
colorPrimary: this.state.themeData.colorPrimary,
borderRadius: this.state.themeData.borderRadius,
},
algorithm: Setting.getAlgorithm(this.state.themeAlgorithm),
}}> }}>
<StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}> <StyleProvider hashPriority="high" transformers={[legacyLogicalPropertiesTransformer]}>
<Layout id="parent-area"> <Layout id="parent-area">
@ -376,7 +371,6 @@ class App extends Component {
<EntryPage <EntryPage
account={this.state.account} account={this.state.account}
theme={this.state.themeData} theme={this.state.themeData}
themeAlgorithm={this.state.themeAlgorithm}
updateApplication={(application) => { updateApplication={(application) => {
this.setState({ this.setState({
application: application, application: application,
@ -451,6 +445,7 @@ class App extends Component {
setLogoutState={() => { setLogoutState={() => {
this.setState({ this.setState({
account: null, account: null,
themeAlgorithm: ["default"],
}); });
}} }}
/> />

View File

@ -129,15 +129,6 @@ img {
background-attachment: fixed; background-attachment: fixed;
} }
.loginBackgroundDark {
flex: auto;
display: flex;
align-items: center;
background: #000 no-repeat;
background-size: 100% 100%;
background-attachment: fixed;
}
.ant-menu-horizontal { .ant-menu-horizontal {
border-bottom: none !important; border-bottom: none !important;
} }

View File

@ -46,18 +46,12 @@ require("codemirror/mode/css/css");
const {Option} = Select; const {Option} = Select;
const template = `<style> const template = `<style>
.login-panel { .login-panel{
padding: 40px 70px 0 70px; padding: 40px 70px 0 70px;
border-radius: 10px; border-radius: 10px;
background-color: #ffffff; background-color: #ffffff;
box-shadow: 0 0 30px 20px rgba(0, 0, 0, 0.20); box-shadow: 0 0 30px 20px rgba(0, 0, 0, 0.20);
} }
.login-panel-dark {
padding: 40px 70px 0 70px;
border-radius: 10px;
background-color: #333333;
box-shadow: 0 0 30px 20px rgba(255, 255, 255, 0.20);
}
</style>`; </style>`;
const previewGrid = Setting.isMobile() ? 22 : 11; const previewGrid = Setting.isMobile() ? 22 : 11;
@ -122,6 +116,7 @@ class ApplicationEditPage extends React.Component {
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getApplication(); this.getApplication();
this.getOrganizations(); this.getOrganizations();
this.getProviders();
} }
getApplication() { getApplication() {
@ -150,9 +145,7 @@ class ApplicationEditPage extends React.Component {
application: application, application: application,
}); });
this.getProviders(application); this.getCerts(application.organization);
this.getCerts(application);
this.getSamlMetadata(application.enableSamlPostBinding); this.getSamlMetadata(application.enableSamlPostBinding);
}); });
@ -173,11 +166,7 @@ class ApplicationEditPage extends React.Component {
}); });
} }
getCerts(application) { getCerts(owner) {
let owner = application.organization;
if (application.isShared) {
owner = this.props.owner;
}
CertBackend.getCerts(owner) CertBackend.getCerts(owner)
.then((res) => { .then((res) => {
this.setState({ this.setState({
@ -186,12 +175,8 @@ class ApplicationEditPage extends React.Component {
}); });
} }
getProviders(application) { getProviders() {
let owner = application.organization; ProviderBackend.getProviders(this.state.owner)
if (application.isShared) {
owner = this.props.account.owner;
}
ProviderBackend.getProviders(owner)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
this.setState({ this.setState({
@ -278,16 +263,6 @@ class ApplicationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Is shared"), i18next.t("general:Is shared - Tooltip"))} :
</Col>
<Col span={22} >
<Switch disabled={Setting.isAdminUser()} checked={this.state.application.isShared} onChange={checked => {
this.updateApplicationField("isShared", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} : {Setting.getLabel(i18next.t("general:Logo"), i18next.t("general:Logo - Tooltip"))} :
@ -413,16 +388,6 @@ class ApplicationEditPage extends React.Component {
/> />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token signing method"), i18next.t("application:Token signing method - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.application.tokenSigningMethod === "" ? "RS256" : this.state.application.tokenSigningMethod} onChange={(value => {this.updateApplicationField("tokenSigningMethod", value);})}
options={["RS256", "RS512", "ES256", "ES512", "ES384"].map((item) => Setting.getOption(item, item))}
/>
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("application:Token fields"), i18next.t("application:Token fields - Tooltip"))} : {Setting.getLabel(i18next.t("application:Token fields"), i18next.t("application:Token fields - Tooltip"))} :
@ -598,16 +563,6 @@ class ApplicationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
</Col>
<Col span={22} >
<Input placeholder = {this.state.application.organizationObj?.ipWhitelist} value={this.state.application.ipWhitelist} onChange={e => {
this.updateApplicationField("ipWhitelist", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} : {Setting.getLabel(i18next.t("signup:Terms of Use"), i18next.t("signup:Terms of Use - Tooltip"))} :
@ -719,16 +674,6 @@ class ApplicationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}}>
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Use Email as NameID"), i18next.t("application:Use Email as NameID - Tooltip"))} :
</Col>
<Col span={1}>
<Switch checked={this.state.application.useEmailAsSamlNameId} onChange={checked => {
this.updateApplicationField("useEmailAsSamlNameId", checked);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("application:Enable SAML POST binding"), i18next.t("application:Enable SAML POST binding - Tooltip"))} : {Setting.getLabel(i18next.t("application:Enable SAML POST binding"), i18next.t("application:Enable SAML POST binding - Tooltip"))} :
@ -765,7 +710,7 @@ class ApplicationEditPage extends React.Component {
/> />
<br /> <br />
<Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => { <Button style={{marginBottom: "10px"}} type="primary" shape="round" icon={<CopyOutlined />} onClick={() => {
copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}&enablePostBinding=${this.state.application.enableSamlPostBinding}`); copy(`${window.location.origin}/api/saml/metadata?application=admin/${encodeURIComponent(this.state.applicationName)}&post=${this.state.application.enableSamlPostBinding}`);
Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully")); Setting.showMessage("success", i18next.t("general:Copied to clipboard successfully"));
}} }}
> >
@ -993,7 +938,6 @@ class ApplicationEditPage extends React.Component {
<SigninTable <SigninTable
title={i18next.t("application:Signin items")} title={i18next.t("application:Signin items")}
table={this.state.application.signinItems} table={this.state.application.signinItems}
themeAlgorithm={this.state.themeAlgorithm}
onUpdateTable={(value) => { onUpdateTable={(value) => {
this.updateApplicationField("signinItems", value); this.updateApplicationField("signinItems", value);
}} }}
@ -1045,11 +989,7 @@ class ApplicationEditPage extends React.Component {
redirectUri = "\"ERROR: You must specify at least one Redirect URL in 'Redirect URLs'\""; redirectUri = "\"ERROR: You must specify at least one Redirect URL in 'Redirect URLs'\"";
} }
let clientId = this.state.application.clientId; const signInUrl = `/login/oauth/authorize?client_id=${this.state.application.clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
if (this.state.application.isShared) {
clientId += `-org-${this.props.account.owner}`;
}
const signInUrl = `/login/oauth/authorize?client_id=${clientId}&response_type=code&redirect_uri=${redirectUri}&scope=read&state=casdoor`;
const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"}; const maskStyle = {position: "absolute", top: "0px", left: "0px", zIndex: 10, height: "97%", width: "100%", background: "rgba(0,0,0,0.4)"};
if (!Setting.isPasswordEnabled(this.state.application)) { if (!Setting.isPasswordEnabled(this.state.application)) {
signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize"); signUpUrl = signInUrl.replace("/login/oauth/authorize", "/signup/oauth/authorize");

View File

@ -97,11 +97,9 @@ class ApplicationListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);
@ -125,7 +123,7 @@ class ApplicationListPage extends BaseListPage {
render: (text, record, index) => { render: (text, record, index) => {
return ( return (
<Link to={`/applications/${record.organization}/${text}`}> <Link to={`/applications/${record.organization}/${text}`}>
{Setting.getApplicationDisplayName(record)} {text}
</Link> </Link>
); );
}, },

View File

@ -1,116 +0,0 @@
// Copyright 2021 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React from "react";
import {CaptchaModal} from "./common/modal/CaptchaModal";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as Setting from "./Setting";
class CaptchaPage extends React.Component {
constructor(props) {
super(props);
const params = new URLSearchParams(this.props.location.search);
this.state = {
owner: "admin",
application: null,
clientId: params.get("client_id"),
applicationName: params.get("state"),
redirectUri: params.get("redirect_uri"),
};
}
componentDidMount() {
this.getApplication();
}
onUpdateApplication(application) {
this.setState({
application: application,
});
}
getApplication() {
if (this.state.applicationName === null) {
return null;
}
ApplicationBackend.getApplication(this.state.owner, this.state.applicationName)
.then((res) => {
if (res.status === "error") {
this.onUpdateApplication(null);
this.setState({
msg: res.msg,
});
return ;
}
this.onUpdateApplication(res.data);
});
}
getCaptchaProviderItems(application) {
const providers = application?.providers;
if (providers === undefined || providers === null) {
return null;
}
return providers.filter(providerItem => {
if (providerItem.provider === undefined || providerItem.provider === null) {
return false;
}
return providerItem.provider.category === "Captcha";
});
}
callback(values) {
Setting.goToLink(`${this.state.redirectUri}?code=${values.captchaToken}&type=${values.captchaType}&secret=${values.clientSecret}&applicationId=${values.applicationId}`);
}
renderCaptchaModal(application) {
const captchaProviderItems = this.getCaptchaProviderItems(application);
if (captchaProviderItems === null) {
return null;
}
const alwaysProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Always");
const dynamicProviderItems = captchaProviderItems.filter(providerItem => providerItem.rule === "Dynamic");
const provider = alwaysProviderItems.length > 0
? alwaysProviderItems[0].provider
: dynamicProviderItems[0].provider;
return <CaptchaModal
owner={provider.owner}
name={provider.name}
visible={true}
onOk={(captchaType, captchaToken, clientSecret) => {
const values = {
captchaType: captchaType,
captchaToken: captchaToken,
clientSecret: clientSecret,
applicationId: `${provider.owner}/${provider.name}`,
};
this.callback(values);
}}
onCancel={() => this.callback({captchaType: "none", captchaToken: "", clientSecret: ""})}
isCurrentProvider={true}
/>;
}
render() {
return (
this.renderCaptchaModal(this.state.application)
);
}
}
export default CaptchaPage;

View File

@ -1,97 +0,0 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {useCallback, useEffect, useRef, useState} from "react";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
import "codemirror/mode/properties/properties";
import * as Setting from "./Setting";
import IframeEditor from "./IframeEditor";
import {Tabs} from "antd";
const {TabPane} = Tabs;
const CasbinEditor = ({model, onModelTextChange}) => {
const [activeKey, setActiveKey] = useState("advanced");
const iframeRef = useRef(null);
const [localModelText, setLocalModelText] = useState(model.modelText);
const handleModelTextChange = useCallback((newModelText) => {
if (!Setting.builtInObject(model)) {
setLocalModelText(newModelText);
onModelTextChange(newModelText);
}
}, [model, onModelTextChange]);
const syncModelText = useCallback(() => {
return new Promise((resolve) => {
if (activeKey === "advanced" && iframeRef.current) {
const handleSyncMessage = (event) => {
if (event.data.type === "modelUpdate") {
window.removeEventListener("message", handleSyncMessage);
handleModelTextChange(event.data.modelText);
resolve();
}
};
window.addEventListener("message", handleSyncMessage);
iframeRef.current.getModelText();
} else {
resolve();
}
});
}, [activeKey, handleModelTextChange]);
const handleTabChange = (key) => {
syncModelText().then(() => {
setActiveKey(key);
if (key === "advanced" && iframeRef.current) {
iframeRef.current.updateModelText(localModelText);
}
});
};
useEffect(() => {
setLocalModelText(model.modelText);
}, [model.modelText]);
return (
<div style={{height: "100%", width: "100%", display: "flex", flexDirection: "column"}}>
<Tabs activeKey={activeKey} onChange={handleTabChange} style={{flex: "0 0 auto", marginTop: "-10px"}}>
<TabPane tab="Basic Editor" key="basic" />
<TabPane tab="Advanced Editor" key="advanced" />
</Tabs>
<div style={{flex: "1 1 auto", overflow: "hidden"}}>
{activeKey === "advanced" ? (
<IframeEditor
ref={iframeRef}
initialModelText={localModelText}
onModelTextChange={handleModelTextChange}
style={{width: "100%", height: "100%"}}
/>
) : (
<CodeMirror
value={localModelText}
className="full-height-editor no-horizontal-scroll-editor"
options={{mode: "properties", theme: "default"}}
onBeforeChange={(editor, data, value) => {
handleModelTextChange(value);
}}
/>
)}
</div>
</div>
);
};
export default CasbinEditor;

View File

@ -288,14 +288,14 @@ class CertEditPage extends React.Component {
Setting.showMessage("success", i18next.t("general:Successfully saved")); Setting.showMessage("success", i18next.t("general:Successfully saved"));
this.setState({ this.setState({
certName: this.state.cert.name, certName: this.state.cert.name,
}, () => {
if (exitAfterSave) {
this.props.history.push("/certs");
} else {
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
this.getCert();
}
}); });
if (exitAfterSave) {
this.props.history.push("/certs");
} else {
this.props.history.push(`/certs/${this.state.cert.owner}/${this.state.cert.name}`);
this.getCert();
}
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to save")}: ${res.msg}`);
this.updateCertField("name", this.state.certName); this.updateCertField("name", this.state.certName);

View File

@ -73,11 +73,9 @@ class CertListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -55,11 +55,9 @@ class EnforcerListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -32,9 +32,7 @@ import {authConfig} from "./auth/Auth";
import ProductBuyPage from "./ProductBuyPage"; import ProductBuyPage from "./ProductBuyPage";
import PaymentResultPage from "./PaymentResultPage"; import PaymentResultPage from "./PaymentResultPage";
import QrCodePage from "./QrCodePage"; import QrCodePage from "./QrCodePage";
import CaptchaPage from "./CaptchaPage";
import CustomHead from "./basic/CustomHead"; import CustomHead from "./basic/CustomHead";
import * as Util from "./auth/Util";
class EntryPage extends React.Component { class EntryPage extends React.Component {
constructor(props) { constructor(props) {
@ -95,20 +93,10 @@ class EntryPage extends React.Component {
}); });
}; };
if (this.state.application?.ipRestriction) {
return Util.renderMessageLarge(this, this.state.application.ipRestriction);
}
if (this.state.application?.organizationObj?.ipRestriction) {
return Util.renderMessageLarge(this, this.state.application.organizationObj.ipRestriction);
}
const isDarkMode = this.props.themeAlgorithm.includes("dark");
return ( return (
<React.Fragment> <React.Fragment>
<CustomHead headerHtml={this.state.application?.headerHtml} /> <CustomHead headerHtml={this.state.application?.headerHtml} />
<div className={`${isDarkMode ? "loginBackgroundDark" : "loginBackground"}`} <div className="loginBackground"
style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}> style={{backgroundImage: Setting.inIframe() || Setting.isMobile() ? null : `url(${this.state.application?.formBackgroundUrl})`}}>
<Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")} <Spin size="large" spinning={this.state.application === undefined && this.state.pricing === undefined} tip={i18next.t("login:Loading")}
style={{margin: "0 auto"}} /> style={{margin: "0 auto"}} />
@ -132,10 +120,8 @@ class EntryPage extends React.Component {
<Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} /> <Route exact path="/buy-plan/:owner/:pricingName" render={(props) => <ProductBuyPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} /> <Route exact path="/buy-plan/:owner/:pricingName/result" render={(props) => <PaymentResultPage {...this.props} pricing={this.state.pricing} onUpdatePricing={onUpdatePricing} {...props} />} />
<Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} /> <Route exact path="/qrcode/:owner/:paymentName" render={(props) => <QrCodePage {...this.props} onUpdateApplication={onUpdateApplication} {...props} />} />
<Route exact path="/captcha" render={(props) => <CaptchaPage {...props} />} />
</Switch> </Switch>
</div> </div>
</React.Fragment> </React.Fragment>
); );
} }

View File

@ -84,11 +84,9 @@ class GroupListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -1,66 +0,0 @@
// Copyright 2024 The Casdoor Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, {forwardRef, useEffect, useImperativeHandle, useRef, useState} from "react";
const IframeEditor = forwardRef(({initialModelText, onModelTextChange}, ref) => {
const iframeRef = useRef(null);
const [iframeReady, setIframeReady] = useState(false);
useEffect(() => {
const handleMessage = (event) => {
if (event.origin !== "https://editor.casbin.org") {return;}
if (event.data.type === "modelUpdate") {
onModelTextChange(event.data.modelText);
} else if (event.data.type === "iframeReady") {
setIframeReady(true);
iframeRef.current?.contentWindow.postMessage({
type: "initializeModel",
modelText: initialModelText,
}, "*");
}
};
window.addEventListener("message", handleMessage);
return () => window.removeEventListener("message", handleMessage);
}, [onModelTextChange, initialModelText]);
useImperativeHandle(ref, () => ({
getModelText: () => {
iframeRef.current?.contentWindow.postMessage({type: "getModelText"}, "*");
},
updateModelText: (newModelText) => {
if (iframeReady) {
iframeRef.current?.contentWindow.postMessage({
type: "updateModelText",
modelText: newModelText,
}, "*");
}
},
}));
return (
<iframe
ref={iframeRef}
src="https://editor.casbin.org/model-editor"
frameBorder="0"
width="100%"
height="500px"
title="Casbin Model Editor"
/>
);
});
export default IframeEditor;

View File

@ -20,7 +20,6 @@ import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import copy from "copy-to-clipboard"; import copy from "copy-to-clipboard";
import * as GroupBackend from "./backend/GroupBackend";
const {Option} = Select; const {Option} = Select;
@ -34,7 +33,6 @@ class InvitationEditPage extends React.Component {
invitation: null, invitation: null,
organizations: [], organizations: [],
applications: [], applications: [],
groups: [],
mode: props.location.mode !== undefined ? props.location.mode : "edit", mode: props.location.mode !== undefined ? props.location.mode : "edit",
}; };
} }
@ -43,7 +41,6 @@ class InvitationEditPage extends React.Component {
this.getInvitation(); this.getInvitation();
this.getOrganizations(); this.getOrganizations();
this.getApplicationsByOrganization(this.state.organizationName); this.getApplicationsByOrganization(this.state.organizationName);
this.getGroupsByOrganization(this.state.organizationName);
} }
getInvitation() { getInvitation() {
@ -78,17 +75,6 @@ class InvitationEditPage extends React.Component {
}); });
} }
getGroupsByOrganization(organizationName) {
GroupBackend.getGroups(organizationName)
.then((res) => {
if (res.status === "ok") {
this.setState({
groups: res.data,
});
}
});
}
parseInvitationField(key, value) { parseInvitationField(key, value) {
if ([""].includes(key)) { if ([""].includes(key)) {
value = Setting.myParseInt(value); value = Setting.myParseInt(value);
@ -134,7 +120,7 @@ class InvitationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : {Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account) || isCreatedByPlan} value={this.state.invitation.owner} onChange={(value => {this.updateInvitationField("owner", value); this.getApplicationsByOrganization(value);this.getGroupsByOrganization(value);})}> <Select virtual={false} style={{width: "100%"}} disabled={!Setting.isAdminUser(this.props.account) || isCreatedByPlan} value={this.state.invitation.owner} onChange={(value => {this.updateInvitationField("owner", value); this.getApplicationsByOrganization(value);})}>
{ {
this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>) this.state.organizations.map((organization, index) => <Option key={index} value={organization.name}>{organization.name}</Option>)
} }
@ -218,21 +204,6 @@ class InvitationEditPage extends React.Component {
]} /> ]} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("provider:Signup group"), i18next.t("provider:Signup group - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.invitation.signupGroup} onChange={(value => {this.updateInvitationField("signupGroup", value);})}>
<Option key={""} value={""}>
{i18next.t("general:Default")}
</Option>
{
this.state.groups.map((group, index) => <Option key={index} value={`${group.owner}/${group.name}`}>{group.name}</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"))} : {Setting.getLabel(i18next.t("signup:Username"), i18next.t("signup:Username - Tooltip"))} :

View File

@ -68,11 +68,9 @@ class InvitationListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -13,13 +13,12 @@
// limitations under the License. // limitations under the License.
import React from "react"; import React from "react";
import {Button, Card, Col, Input, InputNumber, Row, Select, Space, Switch} from "antd"; import {Button, Card, Col, Input, InputNumber, Row, Select, Switch} from "antd";
import {EyeInvisibleOutlined, EyeTwoTone, HolderOutlined, UsergroupAddOutlined} from "@ant-design/icons"; import {EyeInvisibleOutlined, EyeTwoTone} from "@ant-design/icons";
import * as LddpBackend from "./backend/LdapBackend"; import * as LddpBackend from "./backend/LdapBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import * as GroupBackend from "./backend/GroupBackend";
const {Option} = Select; const {Option} = Select;
@ -31,14 +30,12 @@ class LdapEditPage extends React.Component {
organizationName: props.match.params.organizationName, organizationName: props.match.params.organizationName,
ldap: null, ldap: null,
organizations: [], organizations: [],
groups: null,
}; };
} }
UNSAFE_componentWillMount() { UNSAFE_componentWillMount() {
this.getLdap(); this.getLdap();
this.getOrganizations(); this.getOrganizations();
this.getGroups();
} }
getLdap() { getLdap() {
@ -63,17 +60,6 @@ class LdapEditPage extends React.Component {
}); });
} }
getGroups() {
GroupBackend.getGroups(this.state.organizationName)
.then((res) => {
if (res.status === "ok") {
this.setState({
groups: res.data,
});
}
});
}
updateLdapField(key, value) { updateLdapField(key, value) {
this.setState((prevState) => { this.setState((prevState) => {
prevState.ldap[key] = value; prevState.ldap[key] = value;
@ -228,31 +214,6 @@ class LdapEditPage extends React.Component {
/> />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Default group"), i18next.t("ldap:Default group - Tooltip"))} :
</Col>
<Col span={21}>
<Select virtual={false} style={{width: "100%"}} value={this.state.ldap.defaultGroup ?? []} onChange={(value => {
this.updateLdapField("defaultGroup", value);
})}
>
<Option key={""} value={""}>
<Space>
{i18next.t("general:Default")}
</Space>
</Option>
{
this.state.groups?.map((group) => <Option key={group.name} value={`${group.owner}/${group.name}`}>
<Space>
{group.type === "Physical" ? <UsergroupAddOutlined /> : <HolderOutlined />}
{group.displayName}
</Space>
</Option>)
}
</Select>
</Col>
</Row>
<Row style={{marginTop: "20px"}}> <Row style={{marginTop: "20px"}}>
<Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}> <Col style={{lineHeight: "32px", textAlign: "right", paddingRight: "25px"}} span={3}>
{Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} : {Setting.getLabel(i18next.t("ldap:Auto Sync"), i18next.t("ldap:Auto Sync - Tooltip"))} :

View File

@ -198,11 +198,11 @@ function ManagementPage(props) {
</div> </div>
</Tooltip> </Tooltip>
<OpenTour /> <OpenTour />
{Setting.isAdminUser(props.account) && (props.uri.indexOf("/trees") === -1) && {Setting.isAdminUser(props.account) && !Setting.isMobile() && (props.uri.indexOf("/trees") === -1) &&
<OrganizationSelect <OrganizationSelect
initValue={Setting.getOrganization()} initValue={Setting.getOrganization()}
withAll={true} withAll={true}
style={{marginRight: "20px", width: "180px", display: !Setting.isMobile() ? "flex" : "none"}} style={{marginRight: "20px", width: "180px", display: "flex"}}
onChange={(value) => { onChange={(value) => {
Setting.setOrganization(value); Setting.setOrganization(value);
}} }}

View File

@ -18,7 +18,11 @@ import * as ModelBackend from "./backend/ModelBackend";
import * as OrganizationBackend from "./backend/OrganizationBackend"; import * as OrganizationBackend from "./backend/OrganizationBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import i18next from "i18next"; import i18next from "i18next";
import ModelEditor from "./CasbinEditor";
import {Controlled as CodeMirror} from "react-codemirror2";
import "codemirror/lib/codemirror.css";
require("codemirror/mode/properties/properties");
const {Option} = Select; const {Option} = Select;
@ -143,10 +147,16 @@ class ModelEditPage extends React.Component {
{Setting.getLabel(i18next.t("model:Model text"), i18next.t("model:Model text - Tooltip"))} : {Setting.getLabel(i18next.t("model:Model text"), i18next.t("model:Model text - Tooltip"))} :
</Col> </Col>
<Col span={22}> <Col span={22}>
<div style={{position: "relative", height: "500px"}} > <div style={{width: "100%"}} >
<ModelEditor <CodeMirror
model={this.state.model} value={this.state.model.modelText}
onModelTextChange={(value) => this.updateModelField("modelText", value)} options={{mode: "properties", theme: "default"}}
onBeforeChange={(editor, data, value) => {
if (Setting.builtInObject(this.state.model)) {
return;
}
this.updateModelField("modelText", value);
}}
/> />
</div> </div>
</Col> </Col>

View File

@ -72,11 +72,9 @@ class ModelListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -19,7 +19,6 @@ import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as LdapBackend from "./backend/LdapBackend"; import * as LdapBackend from "./backend/LdapBackend";
import * as Setting from "./Setting"; import * as Setting from "./Setting";
import * as Conf from "./Conf"; import * as Conf from "./Conf";
import * as Obfuscator from "./auth/Obfuscator";
import i18next from "i18next"; import i18next from "i18next";
import {LinkOutlined} from "@ant-design/icons"; import {LinkOutlined} from "@ant-design/icons";
import LdapTable from "./table/LdapTable"; import LdapTable from "./table/LdapTable";
@ -113,22 +112,6 @@ class OrganizationEditPage extends React.Component {
}); });
} }
updatePasswordObfuscator(key, value) {
const organization = this.state.organization;
if (organization.passwordObfuscatorType === "") {
organization.passwordObfuscatorType = "Plain";
}
if (key === "type") {
organization.passwordObfuscatorType = value;
organization.passwordObfuscatorKey = Obfuscator.getRandomKeyForObfuscator(value);
} else if (key === "key") {
organization.passwordObfuscatorKey = value;
}
this.setState({
organization: organization,
});
}
renderOrganization() { renderOrganization() {
return ( return (
<Card size="small" title={ <Card size="small" title={
@ -311,44 +294,6 @@ class OrganizationEditPage extends React.Component {
/> />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Password obfuscator"), i18next.t("general:Password obfuscator - Tooltip"))} :
</Col>
<Col span={22} >
<Select virtual={false} style={{width: "100%"}}
value={this.state.organization.passwordObfuscatorType}
onChange={(value => {this.updatePasswordObfuscator("type", value);})}>
{
[
{id: "Plain", name: "Plain"},
{id: "AES", name: "AES"},
{id: "DES", name: "DES"},
].map((obfuscatorType, index) => <Option key={index} value={obfuscatorType.id}>{obfuscatorType.name}</Option>)
}
</Select>
</Col>
</Row>
{
(this.state.organization.passwordObfuscatorType === "Plain" || this.state.organization.passwordObfuscatorType === "") ? null : (<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Password obf key"), i18next.t("general:Password obf key - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.organization.passwordObfuscatorKey} onChange={(e) => {this.updatePasswordObfuscator("key", e.target.value);}} />
</Col>
</Row>)
}
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Password expire days"), i18next.t("organization:Password expire days - Tooltip"))} :
</Col>
<Col span={4} >
<InputNumber value={this.state.organization.passwordExpireDays} onChange={value => {
this.updateOrganizationField("passwordExpireDays", value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Supported country codes"), i18next.t("general:Supported country codes - Tooltip"))} : {Setting.getLabel(i18next.t("general:Supported country codes"), i18next.t("general:Supported country codes - Tooltip"))} :
@ -360,7 +305,6 @@ class OrganizationEditPage extends React.Component {
}} }}
filterOption={(input, option) => (option?.text ?? "").toLowerCase().includes(input.toLowerCase())} filterOption={(input, option) => (option?.text ?? "").toLowerCase().includes(input.toLowerCase())}
> >
{Setting.getCountryCodeOption({name: i18next.t("organization:All"), code: "All", phone: 0})}
{ {
Setting.getCountryCodeData().map((country) => Setting.getCountryCodeOption(country)) Setting.getCountryCodeData().map((country) => Setting.getCountryCodeOption(country))
} }
@ -416,7 +360,7 @@ class OrganizationEditPage extends React.Component {
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})} <Select virtual={false} style={{width: "100%"}} value={this.state.organization.defaultApplication} onChange={(value => {this.updateOrganizationField("defaultApplication", value);})}
options={this.state.applications?.map((item) => Setting.getOption(Setting.getApplicationDisplayName(item.name), item.name)) options={this.state.applications?.map((item) => Setting.getOption(item.name, item.name))
} /> } />
</Col> </Col>
</Row> </Row>
@ -462,16 +406,6 @@ class OrganizationEditPage extends React.Component {
}} /> }} />
</Col> </Col>
</Row> </Row>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.organization.ipWhitelist} onChange={e => {
this.updateOrganizationField("ipWhitelist", e.target.value);
}} />
</Col>
</Row>
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 19 : 2}>
{Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} : {Setting.getLabel(i18next.t("organization:Init score"), i18next.t("organization:Init score - Tooltip"))} :
@ -594,12 +528,6 @@ class OrganizationEditPage extends React.Component {
const organization = Setting.deepCopy(this.state.organization); const organization = Setting.deepCopy(this.state.organization);
organization.accountItems = organization.accountItems?.filter(accountItem => accountItem.name !== "Please select an account item"); organization.accountItems = organization.accountItems?.filter(accountItem => accountItem.name !== "Please select an account item");
const passwordObfuscatorErrorMessage = Obfuscator.checkPasswordObfuscator(organization.passwordObfuscatorType, organization.passwordObfuscatorKey);
if (passwordObfuscatorErrorMessage.length > 0) {
Setting.showMessage("error", passwordObfuscatorErrorMessage);
return;
}
OrganizationBackend.updateOrganization(this.state.organization.owner, this.state.organizationName, organization) OrganizationBackend.updateOrganization(this.state.organization.owner, this.state.organizationName, organization)
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {

View File

@ -35,9 +35,6 @@ class OrganizationListPage extends BaseListPage {
passwordType: "plain", passwordType: "plain",
PasswordSalt: "", PasswordSalt: "",
passwordOptions: [], passwordOptions: [],
passwordObfuscatorType: "Plain",
passwordObfuscatorKey: "",
passwordExpireDays: 0,
countryCodes: ["US"], countryCodes: ["US"],
defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`, defaultAvatar: `${Setting.StaticBaseUrl}/img/casbin.svg`,
defaultApplication: "", defaultApplication: "",
@ -118,11 +115,11 @@ class OrganizationListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
data: Setting.deleteRow(this.state.data, i),
pagination: { pagination: {
...this.state.pagination, ...this.state.pagination,
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current, total: this.state.pagination.total - 1},
},
}); });
window.dispatchEvent(new Event("storageOrganizationsChanged")); window.dispatchEvent(new Event("storageOrganizationsChanged"));
} else { } else {

View File

@ -70,11 +70,9 @@ class PaymentListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -69,11 +69,9 @@ class PermissionListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -63,11 +63,9 @@ class PlanListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -59,11 +59,9 @@ class PricingListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -123,22 +123,6 @@ class ProductBuyPage extends React.Component {
return "$"; return "$";
} else if (product?.currency === "CNY") { } else if (product?.currency === "CNY") {
return "¥"; return "¥";
} else if (product?.currency === "EUR") {
return "€";
} else if (product?.currency === "JPY") {
return "¥";
} else if (product?.currency === "GBP") {
return "£";
} else if (product?.currency === "AUD") {
return "A$";
} else if (product?.currency === "CAD") {
return "C$";
} else if (product?.currency === "CHF") {
return "CHF";
} else if (product?.currency === "HKD") {
return "HK$";
} else if (product?.currency === "SGD") {
return "S$";
} else { } else {
return "(Unknown currency)"; return "(Unknown currency)";
} }

View File

@ -209,14 +209,6 @@ class ProductEditPage extends React.Component {
[ [
{id: "USD", name: "USD"}, {id: "USD", name: "USD"},
{id: "CNY", name: "CNY"}, {id: "CNY", name: "CNY"},
{id: "EUR", name: "EUR"},
{id: "JPY", name: "JPY"},
{id: "GBP", name: "GBP"},
{id: "AUD", name: "AUD"},
{id: "CAD", name: "CAD"},
{id: "CHF", name: "CHF"},
{id: "HKD", name: "HKD"},
{id: "SGD", name: "SGD"},
].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>) ].map((item, index) => <Option key={index} value={item.id}>{item.name}</Option>)
} }
</Select> </Select>

View File

@ -65,11 +65,9 @@ class ProductListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -843,7 +843,7 @@ class ProviderEditPage extends React.Component {
) )
} }
{ {
this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && (this.state.provider.type !== "Casdoor" && this.state.category !== "Storage") && this.state.provider.type !== "Okta" ? null : ( this.state.provider.type !== "ADFS" && this.state.provider.type !== "AzureAD" && this.state.provider.type !== "AzureADB2C" && this.state.provider.type !== "Casdoor" && this.state.provider.type !== "Okta" ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
@ -870,7 +870,7 @@ class ProviderEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
)} )}
{["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : ( {["Custom HTTP SMS", "Local File System", "MinIO", "Tencent Cloud COS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} : {Setting.getLabel(i18next.t("provider:Endpoint (Intranet)"), i18next.t("provider:Region endpoint for Intranet"))} :
@ -885,9 +885,7 @@ class ProviderEditPage extends React.Component {
{["Custom HTTP SMS", "Local File System"].includes(this.state.provider.type) ? null : ( {["Custom HTTP SMS", "Local File System"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{["Casdoor"].includes(this.state.provider.type) ? {Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
Setting.getLabel(i18next.t("general:Provider"), i18next.t("provider:Provider - Tooltip"))
: Setting.getLabel(i18next.t("provider:Bucket"), i18next.t("provider:Bucket - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.provider.bucket} onChange={e => { <Input value={this.state.provider.bucket} onChange={e => {
@ -908,7 +906,7 @@ class ProviderEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
)} )}
{["Custom HTTP SMS", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : ( {["Custom HTTP SMS", "Google Cloud Storage", "Qiniu Cloud Kodo", "Synology"].includes(this.state.provider.type) ? null : (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Domain"), i18next.t("provider:Domain - Tooltip"))} :
@ -920,24 +918,10 @@ class ProviderEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
)} )}
{["Casdoor"].includes(this.state.provider.type) ? ( {["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}> <Col style={{marginTop: "5px"}} span={2}>
{Setting.getLabel(i18next.t("general:Organization"), i18next.t("general:Organization - Tooltip"))} : {Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
</Col>
<Col span={22} >
<Input value={this.state.provider.content} onChange={e => {
this.updateProviderField("content", e.target.value);
}} />
</Col>
</Row>
) : null}
{["AWS S3", "Tencent Cloud COS", "Qiniu Cloud Kodo", "Casdoor"].includes(this.state.provider.type) ? (
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>
{["Casdoor"].includes(this.state.provider.type) ?
Setting.getLabel(i18next.t("general:Application"), i18next.t("general:Application - Tooltip")) :
Setting.getLabel(i18next.t("provider:Region ID"), i18next.t("provider:Region ID - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input value={this.state.provider.regionId} onChange={e => { <Input value={this.state.provider.regionId} onChange={e => {
@ -1314,7 +1298,7 @@ class ProviderEditPage extends React.Component {
) : null ) : null
} }
{ {
(this.state.provider.type === "Alipay" || this.state.provider.type === "WeChat Pay" || this.state.provider.type === "Casdoor") ? ( (this.state.provider.type === "Alipay" || this.state.provider.type === "WeChat Pay") ? (
<Row style={{marginTop: "20px"}} > <Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}> <Col style={{marginTop: "5px"}} span={(Setting.isMobile()) ? 22 : 2}>
{Setting.getLabel(i18next.t("general:Cert"), i18next.t("general:Cert - Tooltip"))} : {Setting.getLabel(i18next.t("general:Cert"), i18next.t("general:Cert - Tooltip"))} :

View File

@ -76,11 +76,9 @@ class ProviderListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -40,11 +40,9 @@ class ResourceListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -187,7 +187,7 @@ class RoleEditPage extends React.Component {
{Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} : {Setting.getLabel(i18next.t("role:Sub users"), i18next.t("role:Sub users - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Select virtual={true} mode="multiple" style={{width: "100%"}} value={this.state.role.users} <Select virtual={false} mode="multiple" style={{width: "100%"}} value={this.state.role.users}
onChange={(value => {this.updateRoleField("users", value);})} onChange={(value => {this.updateRoleField("users", value);})}
options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))} options={this.state.users.map((user) => Setting.getOption(`${user.owner}/${user.name}`, `${user.owner}/${user.name}`))}
/> />

View File

@ -61,11 +61,9 @@ class RoleListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -27,11 +27,9 @@ class SessionListPage extends BaseListPage {
.then((res) => { .then((res) => {
if (res.status === "ok") { if (res.status === "ok") {
Setting.showMessage("success", i18next.t("general:Successfully deleted")); Setting.showMessage("success", i18next.t("general:Successfully deleted"));
this.fetch({ this.setState({
pagination: { data: Setting.deleteRow(this.state.data, i),
...this.state.pagination, pagination: {total: this.state.pagination.total - 1},
current: this.state.pagination.current > 1 && this.state.data.length === 1 ? this.state.pagination.current - 1 : this.state.pagination.current,
},
}); });
} else { } else {
Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`); Setting.showMessage("error", `${i18next.t("general:Failed to delete")}: ${res.msg}`);

View File

@ -229,10 +229,6 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_synology.png`, logo: `${StaticBaseUrl}/img/social_synology.png`,
url: "https://www.synology.com/en-global/dsm/feature/file_sharing", url: "https://www.synology.com/en-global/dsm/feature/file_sharing",
}, },
"Casdoor": {
logo: `${StaticBaseUrl}/img/casdoor.png`,
url: "https://casdoor.org/docs/provider/storage/overview",
},
}, },
SAML: { SAML: {
"Aliyun IDaaS": { "Aliyun IDaaS": {
@ -287,14 +283,6 @@ export const OtherProviderInfo = {
logo: `${StaticBaseUrl}/img/social_recaptcha.png`, logo: `${StaticBaseUrl}/img/social_recaptcha.png`,
url: "https://www.google.com/recaptcha", url: "https://www.google.com/recaptcha",
}, },
"reCAPTCHA v2": {
logo: `${StaticBaseUrl}/img/social_recaptcha.png`,
url: "https://www.google.com/recaptcha",
},
"reCAPTCHA v3": {
logo: `${StaticBaseUrl}/img/social_recaptcha.png`,
url: "https://www.google.com/recaptcha",
},
"hCaptcha": { "hCaptcha": {
logo: `${StaticBaseUrl}/img/social_hcaptcha.png`, logo: `${StaticBaseUrl}/img/social_hcaptcha.png`,
url: "https://www.hcaptcha.com", url: "https://www.hcaptcha.com",
@ -418,9 +406,6 @@ export function getCountryCode(country) {
} }
export function getCountryCodeData(countryCodes = phoneNumber.getCountries()) { export function getCountryCodeData(countryCodes = phoneNumber.getCountries()) {
if (countryCodes?.includes("All")) {
countryCodes = phoneNumber.getCountries();
}
return countryCodes?.map((countryCode) => { return countryCodes?.map((countryCode) => {
if (phoneNumber.isSupportedCountry(countryCode)) { if (phoneNumber.isSupportedCountry(countryCode)) {
const name = initCountries().getName(countryCode, getLanguage()); const name = initCountries().getName(countryCode, getLanguage());
@ -439,10 +424,10 @@ export function getCountryCodeOption(country) {
<Option key={country.code} value={country.code} label={`+${country.phone}`} text={`${country.name}, ${country.code}, ${country.phone}`} > <Option key={country.code} value={country.code} label={`+${country.phone}`} text={`${country.name}, ${country.code}, ${country.phone}`} >
<div style={{display: "flex", justifyContent: "space-between", marginRight: "10px"}}> <div style={{display: "flex", justifyContent: "space-between", marginRight: "10px"}}>
<div> <div>
{country.code === "All" ? null : getCountryImage(country)} {getCountryImage(country)}
{`${country.name}`} {`${country.name}`}
</div> </div>
{country.code === "All" ? null : `+${country.phone}`} {`+${country.phone}`}
</div> </div>
</Option> </Option>
); );
@ -1077,7 +1062,6 @@ export function getProviderTypeOptions(category) {
{id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"}, {id: "Qiniu Cloud Kodo", name: "Qiniu Cloud Kodo"},
{id: "Google Cloud Storage", name: "Google Cloud Storage"}, {id: "Google Cloud Storage", name: "Google Cloud Storage"},
{id: "Synology", name: "Synology"}, {id: "Synology", name: "Synology"},
{id: "Casdoor", name: "Casdoor"},
] ]
); );
} else if (category === "SAML") { } else if (category === "SAML") {
@ -1099,8 +1083,7 @@ export function getProviderTypeOptions(category) {
} else if (category === "Captcha") { } else if (category === "Captcha") {
return ([ return ([
{id: "Default", name: "Default"}, {id: "Default", name: "Default"},
{id: "reCAPTCHA v2", name: "reCAPTCHA v2"}, {id: "reCAPTCHA", name: "reCAPTCHA"},
{id: "reCAPTCHA v3", name: "reCAPTCHA v3"},
{id: "hCaptcha", name: "hCaptcha"}, {id: "hCaptcha", name: "hCaptcha"},
{id: "Aliyun Captcha", name: "Aliyun Captcha"}, {id: "Aliyun Captcha", name: "Aliyun Captcha"},
{id: "GEETEST", name: "GEETEST"}, {id: "GEETEST", name: "GEETEST"},
@ -1171,7 +1154,7 @@ export function renderLogo(application) {
function isSigninMethodEnabled(application, signinMethod) { function isSigninMethodEnabled(application, signinMethod) {
if (application && application.signinMethods) { if (application && application.signinMethods) {
return application.signinMethods.filter(item => item.name === signinMethod && item.rule !== "Hide-Password").length > 0; return application.signinMethods.filter(item => item.name === signinMethod).length > 0;
} else { } else {
return false; return false;
} }
@ -1388,13 +1371,6 @@ export function getApplicationName(application) {
return `${application?.owner}/${application?.name}`; return `${application?.owner}/${application?.name}`;
} }
export function getApplicationDisplayName(application) {
if (application.isShared) {
return `${application.name}(Shared)`;
}
return application.name;
}
export function getRandomName() { export function getRandomName() {
return Math.random().toString(36).slice(-6); return Math.random().toString(36).slice(-6);
} }
@ -1553,27 +1529,7 @@ export function getCurrencyText(product) {
return i18next.t("product:USD"); return i18next.t("product:USD");
} else if (product?.currency === "CNY") { } else if (product?.currency === "CNY") {
return i18next.t("product:CNY"); return i18next.t("product:CNY");
} else if (product?.currency === "EUR") {
return i18next.t("product:EUR");
} else if (product?.currency === "JPY") {
return i18next.t("product:JPY");
} else if (product?.currency === "GBP") {
return i18next.t("product:GBP");
} else if (product?.currency === "AUD") {
return i18next.t("product:AUD");
} else if (product?.currency === "CAD") {
return i18next.t("product:CAD");
} else if (product?.currency === "CHF") {
return i18next.t("product:CHF");
} else if (product?.currency === "HKD") {
return i18next.t("product:HKD");
} else if (product?.currency === "SGD") {
return i18next.t("product:SGD");
} else { } else {
return "(Unknown currency)"; return "(Unknown currency)";
} }
} }
export function isDarkTheme(themeAlgorithm) {
return themeAlgorithm && themeAlgorithm.includes("dark");
}

Some files were not shown because too many files have changed in this diff Show More