Compare commits

...

5 Commits

9 changed files with 157 additions and 39 deletions

View File

@@ -1414,7 +1414,7 @@ func (c *ApiController) Callback() {
code := c.GetString("code")
state := c.GetString("state")
frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", code, state)
frontendCallbackUrl := fmt.Sprintf("/callback?code=%s&state=%s", url.QueryEscape(code), url.QueryEscape(state))
c.Ctx.Redirect(http.StatusFound, frontendCallbackUrl)
}

View File

@@ -598,15 +598,11 @@ func (c *ApiController) VerifyCode() {
}
if !passed {
result, err := object.CheckVerificationCode(checkDest, authForm.Code, c.GetAcceptLanguage())
err = object.CheckVerifyCodeWithLimit(user, checkDest, authForm.Code, c.GetAcceptLanguage())
if err != nil {
c.ResponseError(err.Error())
return
}
if result.Code != object.VerificationSuccess {
c.ResponseError(result.Msg)
return
}
err = object.DisableVerificationCode(checkDest)
if err != nil {

View File

@@ -75,8 +75,10 @@ func main() {
object.InitCleanupTokens()
object.InitSiteMap()
object.InitRuleMap()
object.StartMonitorSitesLoop()
if len(object.SiteMap) != 0 {
object.InitRuleMap()
object.StartMonitorSitesLoop()
}
util.SafeGoroutine(func() { object.RunSyncUsersJob() })
util.SafeGoroutine(func() { controllers.InitCLIDownloader() })

View File

@@ -453,20 +453,20 @@ func SyncLdapUsers(owner string, syncUsers []LdapUser, ldapId string) (existUser
}
tag := strings.Join(ou, ".")
for _, syncUser := range syncUsers {
existUuids, err := GetExistUuids(owner, uuids)
if err != nil {
return nil, nil, err
}
existUuids, err := GetExistUuids(owner, uuids)
if err != nil {
return nil, nil, err
}
found := false
if len(existUuids) > 0 {
for _, existUuid := range existUuids {
if syncUser.Uuid == existUuid {
existUsers = append(existUsers, syncUser)
found = true
}
}
existUuidSet := make(map[string]struct{}, len(existUuids))
for _, uuid := range existUuids {
existUuidSet[uuid] = struct{}{}
}
for _, syncUser := range syncUsers {
_, found := existUuidSet[syncUser.Uuid]
if found {
existUsers = append(existUsers, syncUser)
}
if !found {
@@ -713,11 +713,23 @@ func dnToGroupName(owner, dn string) string {
func GetExistUuids(owner string, uuids []string) ([]string, error) {
var existUuids []string
// PostgreSQL only supports up to 65535 parameters per query, so we batch the uuids
const batchSize = 100
tableNamePrefix := conf.GetConfigString("tableNamePrefix")
err := ormer.Engine.Table(tableNamePrefix+"user").Where("owner = ?", owner).Cols("ldap").
In("ldap", uuids).Select("DISTINCT ldap").Find(&existUuids)
if err != nil {
return existUuids, err
for i := 0; i < len(uuids); i += batchSize {
end := i + batchSize
if end > len(uuids) {
end = len(uuids)
}
batch := uuids[i:end]
var batchUuids []string
err := ormer.Engine.Table(tableNamePrefix+"user").Where("owner = ?", owner).Cols("ldap").
In("ldap", batch).Select("DISTINCT ldap").Find(&batchUuids)
if err != nil {
return existUuids, err
}
existUuids = append(existUuids, batchUuids...)
}
return existUuids, nil

View File

@@ -21,7 +21,9 @@ import (
"math/rand"
"net/url"
"regexp"
"strconv"
"strings"
"sync"
"time"
"github.com/casdoor/casdoor/conf"
@@ -35,7 +37,16 @@ type VerifyResult struct {
Msg string
}
var ResetLinkReg *regexp.Regexp
type verifyCodeErrorInfo struct {
wrongTimes int
lastWrongTime time.Time
}
var (
ResetLinkReg *regexp.Regexp
verifyCodeErrorMap = map[string]*verifyCodeErrorInfo{}
verifyCodeErrorMapLock sync.Mutex
)
const (
VerificationSuccess = iota
@@ -330,6 +341,107 @@ func CheckSigninCode(user *User, dest, code, lang string) error {
}
}
// getVerifyCodeErrorKey builds the in-memory key for verify-code failed attempt tracking
func getVerifyCodeErrorKey(user *User, dest string) string {
if user == nil {
return dest
}
return fmt.Sprintf("%s:%s", user.GetId(), dest)
}
func checkVerifyCodeErrorTimes(user *User, dest, lang string) error {
failedSigninLimit, failedSigninFrozenTime, err := GetFailedSigninConfigByUser(user)
if err != nil {
return err
}
key := getVerifyCodeErrorKey(user, dest)
verifyCodeErrorMapLock.Lock()
defer verifyCodeErrorMapLock.Unlock()
errorInfo, ok := verifyCodeErrorMap[key]
if !ok || errorInfo == nil {
return nil
}
if errorInfo.wrongTimes < failedSigninLimit {
return nil
}
minutes := failedSigninFrozenTime - int(time.Now().UTC().Sub(errorInfo.lastWrongTime).Minutes())
if minutes > 0 {
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), minutes)
}
delete(verifyCodeErrorMap, key)
return nil
}
func recordVerifyCodeErrorInfo(user *User, dest, lang string) error {
failedSigninLimit, failedSigninFrozenTime, err := GetFailedSigninConfigByUser(user)
if err != nil {
return err
}
key := getVerifyCodeErrorKey(user, dest)
verifyCodeErrorMapLock.Lock()
defer verifyCodeErrorMapLock.Unlock()
errorInfo, ok := verifyCodeErrorMap[key]
if !ok || errorInfo == nil {
errorInfo = &verifyCodeErrorInfo{}
verifyCodeErrorMap[key] = errorInfo
}
if errorInfo.wrongTimes < failedSigninLimit {
errorInfo.wrongTimes++
}
if errorInfo.wrongTimes >= failedSigninLimit {
errorInfo.lastWrongTime = time.Now().UTC()
}
leftChances := failedSigninLimit - errorInfo.wrongTimes
if leftChances >= 0 {
return fmt.Errorf(i18n.Translate(lang, "check:password or code is incorrect, you have %s remaining chances"), strconv.Itoa(leftChances))
}
return fmt.Errorf(i18n.Translate(lang, "check:You have entered the wrong password or code too many times, please wait for %d minutes and try again"), failedSigninFrozenTime)
}
func resetVerifyCodeErrorTimes(user *User, dest string) {
key := getVerifyCodeErrorKey(user, dest)
verifyCodeErrorMapLock.Lock()
defer verifyCodeErrorMapLock.Unlock()
delete(verifyCodeErrorMap, key)
}
func CheckVerifyCodeWithLimit(user *User, dest, code, lang string) error {
if err := checkVerifyCodeErrorTimes(user, dest, lang); err != nil {
return err
}
result, err := CheckVerificationCode(dest, code, lang)
if err != nil {
return err
}
switch result.Code {
case VerificationSuccess:
resetVerifyCodeErrorTimes(user, dest)
return nil
case wrongCodeError:
return recordVerifyCodeErrorInfo(user, dest, lang)
default:
return errors.New(result.Msg)
}
}
func CheckFaceId(user *User, faceId []float64, lang string) error {
if len(user.FaceIds) == 0 {
return errors.New(i18n.Translate(lang, "check:Face data does not exist, cannot log in"))

View File

@@ -144,9 +144,6 @@
}
var state = getRefinedValue(innerParams.get("state"));
if (state.indexOf("/auth/oauth2/login.php?wantsurl") === 0) {
state = encodeURIComponent(state);
}
if (redirectUri.indexOf("#") !== -1 && state === "") {
state = getRawGetParameter("state", queryString);
}
@@ -373,7 +370,7 @@
if (responseMode === "form_post") {
createFormAndSubmit(oAuthParams.redirectUri, {code: res.data, state: oAuthParams.state});
} else {
window.location.replace(oAuthParams.redirectUri + concatChar + "code=" + res.data + "&state=" + oAuthParams.state);
window.location.replace(oAuthParams.redirectUri + concatChar + "code=" + encodeURIComponent(res.data) + "&state=" + encodeURIComponent(oAuthParams.state));
}
return;
}
@@ -387,7 +384,7 @@
state: oAuthParams.state
});
} else {
window.location.replace(oAuthParams.redirectUri + concatChar + responseType + "=" + res.data + "&state=" + oAuthParams.state + "&token_type=bearer");
window.location.replace(oAuthParams.redirectUri + concatChar + responseType + "=" + encodeURIComponent(res.data) + "&state=" + encodeURIComponent(oAuthParams.state) + "&token_type=bearer");
}
return;
}

View File

@@ -1497,7 +1497,7 @@ class ApplicationEditPage extends React.Component {
</div>
} style={{margin: (Setting.isMobile()) ? "5px" : {}, height: "calc(100vh - 145px - 48px)", overflow: "hidden"}}
styles={{body: {height: "100%"}}} type="inner">
<Layout style={{background: "inherit", height: "100%", overflow: "auto"}}>
<Layout style={{background: "inherit", height: "100%"}}>
{
this.state.menuMode === "horizontal" || !this.state.menuMode ? (
<Header style={{background: "inherit", padding: "0px", position: "sticky", top: 0, height: 38, minHeight: 38}}>
@@ -1548,7 +1548,10 @@ class ApplicationEditPage extends React.Component {
</Menu>
</Sider>) : null
}
<Content style={{padding: "15px"}}>
<Content style={{padding: "15px",
overflowY: "auto",
height: "100%",
paddingBottom: "80px"}}>
{this.renderApplicationForm()}
</Content>
</Layout>

View File

@@ -116,7 +116,7 @@ class AuthCallback extends React.Component {
createFormAndSubmit(oAuthParams?.redirectUri, params);
} else {
const code = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${code}&state=${oAuthParams.state}`);
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}code=${encodeURIComponent(code)}&state=${encodeURIComponent(oAuthParams.state)}`);
}
} else if (responseTypes.includes("token") || responseTypes.includes("id_token")) {
if (res.data3) {
@@ -135,7 +135,7 @@ class AuthCallback extends React.Component {
createFormAndSubmit(oAuthParams?.redirectUri, params);
} else {
const token = res.data;
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${token}&state=${oAuthParams.state}&token_type=bearer`);
Setting.goToLink(`${oAuthParams.redirectUri}${concatChar}${responseType}=${encodeURIComponent(token)}&state=${encodeURIComponent(oAuthParams.state)}&token_type=bearer`);
}
} else if (responseType === "link") {
let from = innerParams.get("from");

View File

@@ -130,10 +130,6 @@ export function getOAuthGetParameters(params) {
}
let state = getRefinedValue(queries.get("state"));
if (state.startsWith("/auth/oauth2/login.php?wantsurl")) {
// state contains URL param encoding for Moodle, URLSearchParams automatically decoded it, so here encode it again
state = encodeURIComponent(state);
}
if (redirectUri.includes("#") && state === "") {
state = getRawGetParameter("state");
}