Compare commits

..

16 Commits

Author SHA1 Message Date
Luckery
0818de85d1 feat: fix username checks when organization.UseEmailAsUsername is enabled (#3329)
* feat: Username support email format

* feat: Only fulfill the first requirement

* fix: Improve code robustness
2024-11-05 20:38:47 +08:00
Yang Luo
457c6098a4 feat: fix MFA empty CountryCode bug and show MFA error better in frontend 2024-11-04 16:17:24 +08:00
Yang Luo
60f979fbb5 feat: fix MfaSetupPage empty bug when user's signup application is empty 2024-11-04 00:04:47 +08:00
Luckery
ff53e44fa6 feat: use virtual select UI in role edit page (#3322) 2024-11-03 20:05:34 +08:00
Yang Luo
1832de47db feat: fix bug in CheckEntryIp() 2024-11-03 20:00:52 +08:00
Yang Luo
535eb0c465 fix: fix IP Whitelist field bug in application edit page 2024-11-03 19:55:59 +08:00
ithilelda
c190634cf3 feat: show Domain field for Qiniu storage provider (#3318)
allow Qiniu Provider to edit the Domain property in the edit page.
2024-10-27 14:10:58 +08:00
Cliff
f7559aa040 feat: set created time if not presented in AddUser() API (#3315) 2024-10-24 23:06:05 +08:00
DacongDA
1e0b709c73 feat: pass signin method to CAS login to fix bug (#3313) 2024-10-24 14:56:12 +08:00
DacongDA
c0800b7fb3 feat: add util.IsValidOrigin() to improve CORS filter (#3301)
* fix: CORS check issue

* fix: promote format

* fix: promote format

* fix: promote format

* fix: promote format

* Update application.go

* Update cors_filter.go

* Update validation.go

---------

Co-authored-by: Yang Luo <hsluoyz@qq.com>
2024-10-20 20:09:21 +08:00
eya46
6fcdad2100 feat: fix bug that fails to login when PasswordObfuscator is enabled (#3299) 2024-10-19 23:09:59 +08:00
Cliff
69d26d5c21 feat: add-user/update-user API should check if username/id/email/phone has duplicated with existing user (#3295) 2024-10-18 22:18:37 +08:00
DacongDA
94e6b5ecb8 feat: fix bug in SetPassword() API (#3296) 2024-10-18 20:50:43 +08:00
DacongDA
95e8bdcd36 feat: add initDataNewOnly to app.conf to skip overriding existing data in initDataFromFile() (#3294)
* feat: support control whether overwrite existing data during initDataFromFile

* feat: change conf var name

* feat: change conf var name
2024-10-18 00:08:08 +08:00
liuaiolos
6f1f93725e feat: fix GetAllActions()'s bug (#3289) 2024-10-16 21:55:06 +08:00
DacongDA
7ae067e369 feat: only admin can specify user in BuyProduct() (#3287)
* fix: balance can be used without login

* fix: balance can be used without login

* fix: fix bug

* fix: fix bug
2024-10-16 00:02:04 +08:00
21 changed files with 231 additions and 69 deletions

View File

@@ -29,5 +29,6 @@ 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 = "../casdoor" frontendBaseDir = "../cc_0"

View File

@@ -854,6 +854,7 @@ 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

@@ -182,6 +182,10 @@ 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

@@ -364,17 +364,13 @@ func (c *ApiController) AddUser() {
return return
} }
msg := object.CheckUsername(user.Name, c.GetAcceptLanguage()) emptyUser := object.User{}
msg := object.CheckUpdateUser(&emptyUser, &user, c.GetAcceptLanguage())
if msg != "" { if msg != "" {
c.ResponseError(msg) c.ResponseError(msg)
return return
} }
if err = object.CheckIpWhitelist(user.IpWhitelist, c.GetAcceptLanguage()); err != nil {
c.ResponseError(err.Error())
return
}
c.Data["json"] = wrapActionResponse(object.AddUser(&user)) c.Data["json"] = wrapActionResponse(object.AddUser(&user))
c.ServeJSON() c.ServeJSON()
} }
@@ -494,7 +490,12 @@ 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)

View File

@@ -294,6 +294,7 @@ 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)
@@ -533,5 +534,6 @@ 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

@@ -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": "The login method: login with LDAP is not enabled for the application", "The login method: login with LDAP is not enabled for the application": "Метод входа в систему: вход с помощью LDAP не включен для приложения",
"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": "Метод входа: вход с помощью SMS не включен для приложения",
"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": "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": "Значение \\\"%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\\\"", "The value \\\"%s\\\" for signup field \\\"%s\\\" doesn't match the signup item regex of the application \\\"%s\\\"": "Значение \\\"%s\\\" поля регистрации \\\"%s\\\" не соответствует регулярному выражению приложения \\\"%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": "The organization: %s should have one application at least", "The organization: %s should have one application at least": "Организация: %s должна иметь хотя бы одно приложение",
"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": "The permission: \\\"%s\\\" doesn't exist" "The permission: \\\"%s\\\" doesn't exist": "Разрешение: \\\"%s\\\" не существует"
}, },
"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": "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": "Пожалуйста, добавьте поставщика SMS в список \\\"Провайдеры\\\" для приложения: %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", "please add an Email provider to the \\\"Providers\\\" list for the application: %s": "Пожалуйста, добавьте поставщика электронной почты в список \\\"Провайдеры\\\" для приложения: %s",
"the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь" "the user does not exist, please sign up first": "Пользователь не существует, пожалуйста, сначала зарегистрируйтесь"
}, },
"webauthn": { "webauthn": {

View File

@@ -723,8 +723,15 @@ func (application *Application) GetId() string {
} }
func (application *Application) IsRedirectUriValid(redirectUri string) bool { func (application *Application) IsRedirectUriValid(redirectUri string) bool {
redirectUris := append([]string{"http://localhost:", "https://localhost:", "http://127.0.0.1:", "http://casdoor-app", ".chromiumapp.org"}, application.RedirectUris...) isValid, err := util.IsValidOrigin(redirectUri)
for _, targetUri := range redirectUris { if err != nil {
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

@@ -520,11 +520,41 @@ 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 {
if msg := CheckUsername(user.Name, lang); msg != "" { // Check if the organization uses email as the username
return msg organization, err := GetOrganizationByUser(oldUser)
if err != nil {
return err.Error()
} }
if organization == nil {
return fmt.Sprintf(i18n.Translate(lang, "The organization: %s does not exist"), oldUser.Owner)
}
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")
} }

View File

@@ -43,6 +43,8 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
if err != nil { if err != nil {
application.IpRestriction = err.Error() + application.Name application.IpRestriction = err.Error() + application.Name
return fmt.Errorf(err.Error() + application.Name) return fmt.Errorf(err.Error() + application.Name)
} else {
application.IpRestriction = ""
} }
if organization == nil && application.OrganizationObj != nil { if organization == nil && application.OrganizationObj != nil {
@@ -55,6 +57,8 @@ func CheckEntryIp(clientIp string, user *User, application *Application, organiz
if err != nil { if err != nil {
organization.IpRestriction = err.Error() + organization.Name organization.IpRestriction = err.Error() + organization.Name
return fmt.Errorf(err.Error() + organization.Name) return fmt.Errorf(err.Error() + organization.Name)
} else {
organization.IpRestriction = ""
} }
} }

View File

@@ -48,12 +48,16 @@ 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)
@@ -269,6 +273,9 @@ 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)
@@ -295,6 +302,9 @@ 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)
@@ -316,6 +326,9 @@ 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)
@@ -342,6 +355,9 @@ 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)
@@ -364,6 +380,9 @@ 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)
@@ -385,6 +404,9 @@ 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)
@@ -406,6 +428,9 @@ 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)
@@ -428,6 +453,9 @@ 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)
@@ -450,6 +478,9 @@ 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)
@@ -472,6 +503,9 @@ 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)
@@ -494,6 +528,9 @@ 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)
@@ -516,6 +553,9 @@ 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)
@@ -538,6 +578,9 @@ 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)
@@ -560,6 +603,9 @@ 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)
@@ -582,6 +628,9 @@ 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)
@@ -603,6 +652,9 @@ 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)
@@ -624,6 +676,9 @@ 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)
@@ -645,6 +700,9 @@ 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)
@@ -666,6 +724,9 @@ 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)
@@ -687,6 +748,9 @@ 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)
@@ -708,6 +772,9 @@ 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)
@@ -743,6 +810,9 @@ 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)
@@ -764,6 +834,9 @@ 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

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

View File

@@ -816,6 +816,10 @@ 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

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 (
@@ -52,7 +52,13 @@ func CorsFilter(ctx *context.Context) {
origin = "" origin = ""
} }
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") { 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

@@ -17,6 +17,7 @@ package util
import ( import (
"fmt" "fmt"
"net/mail" "net/mail"
"net/url"
"regexp" "regexp"
"strings" "strings"
@@ -24,10 +25,11 @@ 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() {
@@ -35,6 +37,7 @@ 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 {
@@ -100,3 +103,21 @@ 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

@@ -603,7 +603,7 @@ class ApplicationEditPage extends React.Component {
{Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} : {Setting.getLabel(i18next.t("general:IP whitelist"), i18next.t("general:IP whitelist - Tooltip"))} :
</Col> </Col>
<Col span={22} > <Col span={22} >
<Input placeholder = {this.state.application.organizationObj?.ipWhitelist} value={this.state.application.ipWhiteList} onChange={e => { <Input placeholder = {this.state.application.organizationObj?.ipWhitelist} value={this.state.application.ipWhitelist} onChange={e => {
this.updateApplicationField("ipWhitelist", e.target.value); this.updateApplicationField("ipWhitelist", e.target.value);
}} /> }} />
</Col> </Col>

View File

@@ -908,7 +908,7 @@ class ProviderEditPage extends React.Component {
</Col> </Col>
</Row> </Row>
)} )}
{["Custom HTTP SMS", "Qiniu Cloud Kodo", "Synology", "Casdoor"].includes(this.state.provider.type) ? null : ( {["Custom HTTP SMS", "Synology", "Casdoor"].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"))} :

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={false} mode="multiple" style={{width: "100%"}} value={this.state.role.users} <Select virtual={true} 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

@@ -227,6 +227,22 @@ class LoginPage extends React.Component {
return "password"; return "password";
} }
getCurrentLoginMethod() {
if (this.state.loginMethod === "password") {
return "Password";
} else if (this.state.loginMethod?.includes("verificationCode")) {
return "Verification code";
} else if (this.state.loginMethod === "webAuthn") {
return "WebAuthn";
} else if (this.state.loginMethod === "ldap") {
return "LDAP";
} else if (this.state.loginMethod === "faceId") {
return "Face ID";
} else {
return "Password";
}
}
getPlaceholder() { getPlaceholder() {
switch (this.state.loginMethod) { switch (this.state.loginMethod) {
case "verificationCode": return i18next.t("login:Email or phone"); case "verificationCode": return i18next.t("login:Email or phone");
@@ -262,17 +278,7 @@ class LoginPage extends React.Component {
values["organization"] = this.getApplicationObj().organization; values["organization"] = this.getApplicationObj().organization;
} }
if (this.state.loginMethod === "password") { values["signinMethod"] = this.getCurrentLoginMethod();
values["signinMethod"] = "Password";
} else if (this.state.loginMethod?.includes("verificationCode")) {
values["signinMethod"] = "Verification code";
} else if (this.state.loginMethod === "webAuthn") {
values["signinMethod"] = "WebAuthn";
} else if (this.state.loginMethod === "ldap") {
values["signinMethod"] = "LDAP";
} else if (this.state.loginMethod === "faceId") {
values["signinMethod"] = "Face ID";
}
const oAuthParams = Util.getOAuthGetParameters(); const oAuthParams = Util.getOAuthGetParameters();
values["type"] = oAuthParams?.responseType ?? this.state.type; values["type"] = oAuthParams?.responseType ?? this.state.type;
@@ -409,6 +415,7 @@ class LoginPage extends React.Component {
if (this.state.type === "cas") { if (this.state.type === "cas") {
// CAS // CAS
const casParams = Util.getCasParameters(); const casParams = Util.getCasParameters();
values["signinMethod"] = this.getCurrentLoginMethod();
values["type"] = this.state.type; values["type"] = this.state.type;
AuthBackend.loginCas(values, casParams).then((res) => { AuthBackend.loginCas(values, casParams).then((res) => {
const loginHandler = (res) => { const loginHandler = (res) => {
@@ -437,8 +444,8 @@ class LoginPage extends React.Component {
formValues={values} formValues={values}
authParams={casParams} authParams={casParams}
application={this.getApplicationObj()} application={this.getApplicationObj()}
onFail={() => { onFail={(errorMessage) => {
Setting.showMessage("error", i18next.t("mfa:Verification failed")); Setting.showMessage("error", errorMessage);
}} }}
onSuccess={(res) => loginHandler(res)} onSuccess={(res) => loginHandler(res)}
/>); />);
@@ -506,8 +513,8 @@ class LoginPage extends React.Component {
formValues={values} formValues={values}
authParams={oAuthParams} authParams={oAuthParams}
application={this.getApplicationObj()} application={this.getApplicationObj()}
onFail={() => { onFail={(errorMessage) => {
Setting.showMessage("error", i18next.t("mfa:Verification failed")); Setting.showMessage("error", errorMessage);
}} }}
onSuccess={(res) => loginHandler(res)} onSuccess={(res) => loginHandler(res)}
/>); />);

View File

@@ -37,7 +37,7 @@ class MfaSetupPage extends React.Component {
this.state = { this.state = {
account: props.account, account: props.account,
application: null, application: null,
applicationName: props.account.signupApplication ?? "", applicationName: props.account.signupApplication ?? localStorage.getItem("applicationName") ?? "",
current: location.state?.from !== undefined ? 1 : 0, current: location.state?.from !== undefined ? 1 : 0,
mfaProps: null, mfaProps: null,
mfaType: params.get("mfaType") ?? SmsMfaType, mfaType: params.get("mfaType") ?? SmsMfaType,

View File

@@ -14,6 +14,7 @@
import CryptoJS from "crypto-js"; import CryptoJS from "crypto-js";
import i18next from "i18next"; import i18next from "i18next";
import {Buffer} from "buffer";
export function getRandomKeyForObfuscator(obfuscatorType) { export function getRandomKeyForObfuscator(obfuscatorType) {
if (obfuscatorType === "DES") { if (obfuscatorType === "DES") {

View File

@@ -972,7 +972,7 @@
"Please input your affiliation!": "Пожалуйста, укажите свою принадлежность!", "Please input your affiliation!": "Пожалуйста, укажите свою принадлежность!",
"Please input your display name!": "Пожалуйста, введите своё отображаемое имя!", "Please input your display name!": "Пожалуйста, введите своё отображаемое имя!",
"Please input your first name!": "Пожалуйста, введите свое имя!", "Please input your first name!": "Пожалуйста, введите свое имя!",
"Please input your invitation code!": "Please input your invitation code!", "Please input your invitation code!": "Пожалуйста, введите код приглашения!",
"Please input your last name!": "Введите свою фамилию!", "Please input your last name!": "Введите свою фамилию!",
"Please input your phone number!": "Пожалуйста, введите свой номер телефона!", "Please input your phone number!": "Пожалуйста, введите свой номер телефона!",
"Please input your real name!": "Пожалуйста, введите своё настоящее имя!", "Please input your real name!": "Пожалуйста, введите своё настоящее имя!",
@@ -1163,9 +1163,9 @@
"MFA accounts": "MFA accounts", "MFA accounts": "MFA accounts",
"Managed accounts": "Управляемые аккаунты", "Managed accounts": "Управляемые аккаунты",
"Modify password...": "Изменить пароль...", "Modify password...": "Изменить пароль...",
"Multi-factor authentication": "Multi-factor authentication", "Multi-factor authentication": "Многофакторная аутентификация",
"Need update password": "Need update password", "Need update password": "Необходимо обновить пароль",
"Need update password - Tooltip": "Force user update password after login", "Need update password - Tooltip": "Заставить пользователя обновить пароль после входа в систему",
"New Email": "Новое электронное письмо", "New Email": "Новое электронное письмо",
"New Password": "Новый пароль", "New Password": "Новый пароль",
"New User": "Новый пользователь", "New User": "Новый пользователь",
@@ -1189,26 +1189,26 @@
"Set password...": "Установить пароль...", "Set password...": "Установить пароль...",
"Tag": "Метка", "Tag": "Метка",
"Tag - Tooltip": "Тег пользователя", "Tag - Tooltip": "Тег пользователя",
"The password must contain at least one special character": "The password must contain at least one special character", "The password must contain at least one special character": "Пароль должен содержать хотя бы один специальный символ",
"The password must contain at least one uppercase letter, one lowercase letter and one digit": "The password must contain at least one uppercase letter, one lowercase letter and one digit", "The password must contain at least one uppercase letter, one lowercase letter and one digit": "Пароль должен содержать как минимум одну заглавную букву, одну строчную букву и одну цифру",
"The password must have at least 6 characters": "The password must have at least 6 characters", "The password must have at least 6 characters": "Пароль должен быть минимум 6 символов",
"The password must have at least 8 characters": "The password must have at least 8 characters", "The password must have at least 8 characters": "Пароль должен быть минимум 8 символов",
"The password must not contain any repeated characters": "The password must not contain any repeated characters", "The password must not contain any repeated characters": "Пароль не должен содержать повторяющиеся символы",
"This field value doesn't match the pattern rule": "This field value doesn't match the pattern rule", "This field value doesn't match the pattern rule": "Значение поля не соответствует шаблону",
"Title": "Заголовок", "Title": "Заголовок",
"Title - Tooltip": "Положение в аффилиации", "Title - Tooltip": "Положение в аффилиации",
"Two passwords you typed do not match.": "Два введенных вами пароля не совпадают.", "Two passwords you typed do not match.": "Два введенных вами пароля не совпадают.",
"Unlink": "Отсоединить", "Unlink": "Отсоединить",
"Upload (.xlsx)": "Загрузить (.xlsx)", "Upload (.xlsx)": "Загрузить (.xlsx)",
"Upload ID card back picture": "Upload ID card back picture", "Upload ID card back picture": "Загрузите заднюю сторону удостоверения личности",
"Upload ID card front picture": "Upload ID card front picture", "Upload ID card front picture": "Загрузите переднюю сторону удостоверения личности",
"Upload ID card with person picture": "Upload ID card with person picture", "Upload ID card with person picture": "Загрузите удостоверение личности с фотографией",
"Upload a photo": "Загрузить фото", "Upload a photo": "Загрузить фото",
"User Profile": "User Profile", "User Profile": "Профиль пользователя",
"Values": "Значения", "Values": "Значения",
"Verification code sent": "Код подтверждения отправлен", "Verification code sent": "Код подтверждения отправлен",
"WebAuthn credentials": "WebAuthn удостоверения", "WebAuthn credentials": "WebAuthn удостоверения",
"You have changed the username, please save your change first before modifying the password": "You have changed the username, please save your change first before modifying the password", "You have changed the username, please save your change first before modifying the password": "Имя было изменено, сохраните изменения перед сменой пароля",
"input password": "введите пароль" "input password": "введите пароль"
}, },
"verification": { "verification": {