Compare commits

...

6 Commits

19 changed files with 321 additions and 145 deletions

View File

@ -15,32 +15,51 @@
package captcha
import (
"encoding/json"
"errors"
"fmt"
"io"
"net/http"
"net/url"
"sort"
"strconv"
"strings"
"time"
"github.com/casdoor/casdoor/util"
openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
openapiutil "github.com/alibabacloud-go/openapi-util/service"
teaUtil "github.com/alibabacloud-go/tea-utils/v2/service"
"github.com/alibabacloud-go/tea/tea"
)
const AliyunCaptchaVerifyUrl = "http://afs.aliyuncs.com"
const AliyunCaptchaVerifyUrl = "captcha.cn-shanghai.aliyuncs.com"
type captchaSuccessResponse struct {
Code int `json:"Code"`
Msg string `json:"Msg"`
type VerifyCaptchaRequest struct {
CaptchaVerifyParam *string `json:"CaptchaVerifyParam,omitempty" xml:"CaptchaVerifyParam,omitempty"`
SceneId *string `json:"SceneId,omitempty" xml:"SceneId,omitempty"`
}
type captchaFailResponse struct {
Code string `json:"Code"`
Message string `json:"Message"`
type VerifyCaptchaResponseBodyResult struct {
VerifyResult *bool `json:"VerifyResult,omitempty" xml:"VerifyResult,omitempty"`
}
type VerifyCaptchaResponseBody struct {
Code *string `json:"Code,omitempty" xml:"Code,omitempty"`
Message *string `json:"Message,omitempty" xml:"Message,omitempty"`
// Id of the request
RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"`
Result *VerifyCaptchaResponseBodyResult `json:"Result,omitempty" xml:"Result,omitempty" type:"Struct"`
Success *bool `json:"Success,omitempty" xml:"Success,omitempty"`
}
type VerifyIntelligentCaptchaResponseBodyResult struct {
VerifyCode *string `json:"VerifyCode,omitempty" xml:"VerifyCode,omitempty"`
VerifyResult *bool `json:"VerifyResult,omitempty" xml:"VerifyResult,omitempty"`
}
type VerifyIntelligentCaptchaResponseBody struct {
Code *string `json:"Code,omitempty" xml:"Code,omitempty"`
Message *string `json:"Message,omitempty" xml:"Message,omitempty"`
// Id of the request
RequestId *string `json:"RequestId,omitempty" xml:"RequestId,omitempty"`
Result *VerifyIntelligentCaptchaResponseBodyResult `json:"Result,omitempty" xml:"Result,omitempty" type:"Struct"`
Success *bool `json:"Success,omitempty" xml:"Success,omitempty"`
}
type VerifyIntelligentCaptchaResponse struct {
Headers map[string]*string `json:"headers,omitempty" xml:"headers,omitempty" require:"true"`
StatusCode *int32 `json:"statusCode,omitempty" xml:"statusCode,omitempty" require:"true"`
Body *VerifyIntelligentCaptchaResponseBody `json:"body,omitempty" xml:"body,omitempty" require:"true"`
}
type AliyunCaptchaProvider struct{}
func NewAliyunCaptchaProvider() *AliyunCaptchaProvider {
@ -48,68 +67,69 @@ func NewAliyunCaptchaProvider() *AliyunCaptchaProvider {
return captcha
}
func contentEscape(str string) string {
str = strings.Replace(str, " ", "%20", -1)
str = url.QueryEscape(str)
return str
}
func (captcha *AliyunCaptchaProvider) VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error) {
config := &openapi.Config{}
func (captcha *AliyunCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
pathData, err := url.ParseQuery(token)
config.Endpoint = tea.String(AliyunCaptchaVerifyUrl)
config.ConnectTimeout = tea.Int(5000)
config.ReadTimeout = tea.Int(5000)
config.AccessKeyId = tea.String(clientId)
config.AccessKeySecret = tea.String(clientSecret)
client := new(openapi.Client)
err := client.Init(config)
if err != nil {
return false, err
}
pathData["Action"] = []string{"AuthenticateSig"}
pathData["Format"] = []string{"json"}
pathData["SignatureMethod"] = []string{"HMAC-SHA1"}
pathData["SignatureNonce"] = []string{strconv.FormatInt(time.Now().UnixNano(), 10)}
pathData["SignatureVersion"] = []string{"1.0"}
pathData["Timestamp"] = []string{time.Now().UTC().Format("2006-01-02T15:04:05Z")}
pathData["Version"] = []string{"2018-01-12"}
request := VerifyCaptchaRequest{CaptchaVerifyParam: tea.String(token), SceneId: tea.String(clientId2)}
var keys []string
for k := range pathData {
keys = append(keys, k)
}
sort.Strings(keys)
sortQuery := ""
for _, k := range keys {
sortQuery += k + "=" + contentEscape(pathData[k][0]) + "&"
}
sortQuery = strings.TrimSuffix(sortQuery, "&")
stringToSign := fmt.Sprintf("GET&%s&%s", url.QueryEscape("/"), url.QueryEscape(sortQuery))
signature := util.GetHmacSha1(clientSecret+"&", stringToSign)
resp, err := http.Get(fmt.Sprintf("%s?%s&Signature=%s", AliyunCaptchaVerifyUrl, sortQuery, url.QueryEscape(signature)))
err = teaUtil.ValidateModel(&request)
if err != nil {
return false, err
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
runtime := &teaUtil.RuntimeOptions{}
body := map[string]interface{}{}
if !tea.BoolValue(teaUtil.IsUnset(request.CaptchaVerifyParam)) {
body["CaptchaVerifyParam"] = request.CaptchaVerifyParam
}
if !tea.BoolValue(teaUtil.IsUnset(request.SceneId)) {
body["SceneId"] = request.SceneId
}
req := &openapi.OpenApiRequest{
Body: openapiutil.ParseToMap(body),
}
params := &openapi.Params{
Action: tea.String("VerifyIntelligentCaptcha"),
Version: tea.String("2023-03-05"),
Protocol: tea.String("HTTPS"),
Pathname: tea.String("/"),
Method: tea.String("POST"),
AuthType: tea.String("AK"),
Style: tea.String("RPC"),
ReqBodyType: tea.String("formData"),
BodyType: tea.String("json"),
}
res := &VerifyIntelligentCaptchaResponse{}
resBody, err := client.CallApi(params, req, runtime)
if err != nil {
return false, err
}
return handleCaptchaResponse(body)
}
func handleCaptchaResponse(body []byte) (bool, error) {
captchaResp := &captchaSuccessResponse{}
err := json.Unmarshal(body, captchaResp)
err = tea.Convert(resBody, &res)
if err != nil {
captchaFailResp := &captchaFailResponse{}
err = json.Unmarshal(body, captchaFailResp)
if err != nil {
return false, err
}
return false, errors.New(captchaFailResp.Message)
return false, err
}
return true, nil
if res.Body.Result.VerifyResult != nil && *res.Body.Result.VerifyResult {
return true, nil
}
return false, nil
}

View File

@ -23,6 +23,6 @@ func NewDefaultCaptchaProvider() *DefaultCaptchaProvider {
return captcha
}
func (captcha *DefaultCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
func (captcha *DefaultCaptchaProvider) VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error) {
return object.VerifyCaptcha(clientSecret, token), nil
}

View File

@ -35,7 +35,7 @@ func NewGEETESTCaptchaProvider() *GEETESTCaptchaProvider {
return captcha
}
func (captcha *GEETESTCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
func (captcha *GEETESTCaptchaProvider) VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error) {
pathData, err := url.ParseQuery(token)
if err != nil {
return false, err

View File

@ -32,7 +32,7 @@ func NewHCaptchaProvider() *HCaptchaProvider {
return captcha
}
func (captcha *HCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
func (captcha *HCaptchaProvider) VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error) {
reqData := url.Values{
"secret": {clientSecret},
"response": {token},

View File

@ -17,7 +17,7 @@ package captcha
import "fmt"
type CaptchaProvider interface {
VerifyCaptcha(token, clientSecret string) (bool, error)
VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error)
}
func GetCaptchaProvider(captchaType string) CaptchaProvider {
@ -43,11 +43,11 @@ func GetCaptchaProvider(captchaType string) CaptchaProvider {
return nil
}
func VerifyCaptchaByCaptchaType(captchaType, token, clientSecret string) (bool, error) {
func VerifyCaptchaByCaptchaType(captchaType, token, clientId, clientSecret, clientId2 string) (bool, error) {
provider := GetCaptchaProvider(captchaType)
if provider == nil {
return false, fmt.Errorf("invalid captcha provider: %s", captchaType)
}
return provider.VerifyCaptcha(token, clientSecret)
return provider.VerifyCaptcha(token, clientId, clientSecret, clientId2)
}

View File

@ -32,7 +32,7 @@ func NewReCaptchaProvider() *ReCaptchaProvider {
return captcha
}
func (captcha *ReCaptchaProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
func (captcha *ReCaptchaProvider) VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error) {
reqData := url.Values{
"secret": {clientSecret},
"response": {token},

View File

@ -32,7 +32,7 @@ func NewCloudflareTurnstileProvider() *CloudflareTurnstileProvider {
return captcha
}
func (captcha *CloudflareTurnstileProvider) VerifyCaptcha(token, clientSecret string) (bool, error) {
func (captcha *CloudflareTurnstileProvider) VerifyCaptcha(token, clientId, clientSecret, clientId2 string) (bool, error) {
reqData := url.Values{
"secret": {clientSecret},
"response": {token},

View File

@ -504,6 +504,8 @@ func (c *ApiController) Login() {
c.ResponseError(fmt.Sprintf(c.T("verification:Phone number is invalid in your region %s"), authForm.CountryCode))
return
}
} else if verificationCodeType == object.VerifyTypeEmail {
checkDest = authForm.Username
}
// check result through Email or Phone
@ -569,7 +571,7 @@ func (c *ApiController) Login() {
}
var isHuman bool
isHuman, err = captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, authForm.ClientSecret)
isHuman, err = captcha.VerifyCaptchaByCaptchaType(authForm.CaptchaType, authForm.CaptchaToken, captchaProvider.ClientId, authForm.ClientSecret, captchaProvider.ClientId2)
if err != nil {
c.ResponseError(err.Error())
return

View File

@ -197,8 +197,8 @@ func (c *ApiController) GetUser() {
return
}
var organization *object.Organization
if user != nil {
var organization *object.Organization
organization, err = object.GetOrganizationByUser(user)
if err != nil {
c.ResponseError(err.Error())
@ -237,6 +237,14 @@ func (c *ApiController) GetUser() {
return
}
if organization != nil && user != nil {
user, err = object.GetFilteredUser(user, c.IsAdmin(), c.IsAdminOrSelf(user), organization.AccountItems)
if err != nil {
c.ResponseError(err.Error())
return
}
}
c.ResponseOk(user)
}

View File

@ -160,7 +160,7 @@ func (c *ApiController) SendVerificationCode() {
if captchaProvider := captcha.GetCaptchaProvider(vform.CaptchaType); captchaProvider == nil {
c.ResponseError(c.T("general:don't support captchaProvider: ") + vform.CaptchaType)
return
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, vform.ClientSecret); err != nil {
} else if isHuman, err := captchaProvider.VerifyCaptcha(vform.CaptchaToken, provider.ClientId, vform.ClientSecret, provider.ClientId2); err != nil {
c.ResponseError(err.Error())
return
} else if !isHuman {
@ -349,7 +349,7 @@ func (c *ApiController) VerifyCaptcha() {
return
}
isValid, err := provider.VerifyCaptcha(vform.CaptchaToken, vform.ClientSecret)
isValid, err := provider.VerifyCaptcha(vform.CaptchaToken, captchaProvider.ClientId, vform.ClientSecret, captchaProvider.ClientId2)
if err != nil {
c.ResponseError(err.Error())
return

2
go.mod
View File

@ -7,6 +7,7 @@ require (
github.com/alexedwards/argon2id v0.0.0-20211130144151-3585854a6387
github.com/alibabacloud-go/darabonba-openapi/v2 v2.1.4
github.com/alibabacloud-go/facebody-20191230/v5 v5.1.2
github.com/alibabacloud-go/openapi-util v0.1.0
github.com/alibabacloud-go/tea v1.3.2
github.com/alibabacloud-go/tea-utils/v2 v2.0.7
github.com/aws/aws-sdk-go v1.45.5
@ -90,7 +91,6 @@ require (
github.com/alibabacloud-go/darabonba-number v1.0.4 // indirect
github.com/alibabacloud-go/debug v1.0.1 // indirect
github.com/alibabacloud-go/endpoint-util v1.1.0 // indirect
github.com/alibabacloud-go/openapi-util v0.1.0 // indirect
github.com/alibabacloud-go/openplatform-20191219/v2 v2.0.1 // indirect
github.com/alibabacloud-go/tea-fileform v1.1.1 // indirect
github.com/alibabacloud-go/tea-oss-sdk v1.1.3 // indirect

View File

@ -63,6 +63,7 @@ func main() {
beego.InsertFilter("*", beego.BeforeRouter, routers.ApiFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.PrometheusFilter)
beego.InsertFilter("*", beego.BeforeRouter, routers.RecordMessage)
beego.InsertFilter("*", beego.BeforeRouter, routers.FieldValidationFilter)
beego.InsertFilter("*", beego.AfterExec, routers.AfterRecordMessage, false)
beego.BConfig.WebConfig.Session.SessionOn = true

View File

@ -661,6 +661,62 @@ func GetMaskedUser(user *User, isAdminOrSelf bool, errs ...error) (*User, error)
return user, nil
}
func GetFilteredUser(user *User, isAdmin bool, isAdminOrSelf bool, accountItems []*AccountItem) (*User, error) {
if accountItems == nil || len(accountItems) == 0 {
return user, nil
}
userFieldMap := map[string]int{}
reflectedUserField := reflect.TypeOf(User{})
for i := 0; i < reflectedUserField.NumField(); i++ {
userFieldMap[strings.ToLower(reflectedUserField.Field(i).Name)] = i
}
reflectedUser := reflect.ValueOf(user).Elem()
for _, accountItem := range accountItems {
if accountItem.ViewRule == "Public" {
continue
} else if accountItem.ViewRule == "Self" && isAdminOrSelf {
continue
} else if accountItem.ViewRule == "Admin" && isAdmin {
continue
}
lowerCaseAccountItemName := strings.ToLower(accountItem.Name)
lowerCaseAccountItemName = strings.ReplaceAll(lowerCaseAccountItemName, " ", "")
switch accountItem.Name {
case "Multi-factor authentication":
lowerCaseAccountItemName = strings.ToLower("PreferredMfaType")
case "User type":
lowerCaseAccountItemName = "type"
case "Country/Region":
lowerCaseAccountItemName = "region"
case "ID card info":
{
infoKeys := []string{"idCardWithPerson", "idCardFront", "idCardWithPerson"}
for _, infoKey := range infoKeys {
if _, ok := user.Properties[infoKey]; ok {
user.Properties[infoKey] = ""
}
}
continue
}
}
fieldIdx, ok := userFieldMap[lowerCaseAccountItemName]
if !ok {
continue
}
reflectedUser.Field(fieldIdx).SetZero()
}
return user, nil
}
func GetMaskedUsers(users []*User, errs ...error) ([]*User, error) {
if len(errs) > 0 && errs[0] != nil {
return nil, errs[0]

View File

@ -263,6 +263,18 @@ func ClearUserOAuthProperties(user *User, providerType string) (bool, error) {
return affected != 0, nil
}
func userVisible(isAdmin bool, item *AccountItem) bool {
if item == nil {
return false
}
if item.ViewRule == "Admin" && !isAdmin {
return false
}
return true
}
func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDisplayNameEmpty bool, lang string) (bool, string) {
organization, err := GetOrganizationByUser(oldUser)
if err != nil {
@ -273,7 +285,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Owner != newUser.Owner {
item := GetAccountItemByName("Organization", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Owner = oldUser.Owner
} else {
itemsChanged = append(itemsChanged, item)
@ -281,7 +293,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Name != newUser.Name {
item := GetAccountItemByName("Name", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Name = oldUser.Name
} else {
itemsChanged = append(itemsChanged, item)
@ -289,7 +301,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Id != newUser.Id {
item := GetAccountItemByName("ID", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Id = oldUser.Id
} else {
itemsChanged = append(itemsChanged, item)
@ -297,7 +309,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.DisplayName != newUser.DisplayName {
item := GetAccountItemByName("Display name", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.DisplayName = oldUser.DisplayName
} else {
if !allowDisplayNameEmpty && newUser.DisplayName == "" {
@ -309,7 +321,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Avatar != newUser.Avatar {
item := GetAccountItemByName("Avatar", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Avatar = oldUser.Avatar
} else {
itemsChanged = append(itemsChanged, item)
@ -317,7 +329,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Type != newUser.Type {
item := GetAccountItemByName("User type", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Type = oldUser.Type
} else {
itemsChanged = append(itemsChanged, item)
@ -326,7 +338,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
// The password is *** when not modified
if oldUser.Password != newUser.Password && newUser.Password != "***" {
item := GetAccountItemByName("Password", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Password = oldUser.Password
} else {
itemsChanged = append(itemsChanged, item)
@ -334,7 +346,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Email != newUser.Email {
item := GetAccountItemByName("Email", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Email = oldUser.Email
} else {
itemsChanged = append(itemsChanged, item)
@ -342,7 +354,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Phone != newUser.Phone {
item := GetAccountItemByName("Phone", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Phone = oldUser.Phone
} else {
itemsChanged = append(itemsChanged, item)
@ -350,7 +362,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.CountryCode != newUser.CountryCode {
item := GetAccountItemByName("Country code", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.CountryCode = oldUser.CountryCode
} else {
itemsChanged = append(itemsChanged, item)
@ -358,7 +370,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Region != newUser.Region {
item := GetAccountItemByName("Country/Region", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Region = oldUser.Region
} else {
itemsChanged = append(itemsChanged, item)
@ -366,7 +378,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Location != newUser.Location {
item := GetAccountItemByName("Location", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Location = oldUser.Location
} else {
itemsChanged = append(itemsChanged, item)
@ -374,7 +386,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Affiliation != newUser.Affiliation {
item := GetAccountItemByName("Affiliation", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Affiliation = oldUser.Affiliation
} else {
itemsChanged = append(itemsChanged, item)
@ -382,7 +394,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Title != newUser.Title {
item := GetAccountItemByName("Title", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Title = oldUser.Title
} else {
itemsChanged = append(itemsChanged, item)
@ -390,7 +402,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Homepage != newUser.Homepage {
item := GetAccountItemByName("Homepage", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Homepage = oldUser.Homepage
} else {
itemsChanged = append(itemsChanged, item)
@ -398,7 +410,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Bio != newUser.Bio {
item := GetAccountItemByName("Bio", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Bio = oldUser.Bio
} else {
itemsChanged = append(itemsChanged, item)
@ -406,7 +418,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.Tag != newUser.Tag {
item := GetAccountItemByName("Tag", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Tag = oldUser.Tag
} else {
itemsChanged = append(itemsChanged, item)
@ -414,7 +426,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.SignupApplication != newUser.SignupApplication {
item := GetAccountItemByName("Signup application", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.SignupApplication = oldUser.SignupApplication
} else {
itemsChanged = append(itemsChanged, item)
@ -423,7 +435,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Gender != newUser.Gender {
item := GetAccountItemByName("Gender", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Gender = oldUser.Gender
} else {
itemsChanged = append(itemsChanged, item)
@ -432,7 +444,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Birthday != newUser.Birthday {
item := GetAccountItemByName("Birthday", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Birthday = oldUser.Birthday
} else {
itemsChanged = append(itemsChanged, item)
@ -441,7 +453,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Education != newUser.Education {
item := GetAccountItemByName("Education", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Education = oldUser.Education
} else {
itemsChanged = append(itemsChanged, item)
@ -450,7 +462,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.IdCard != newUser.IdCard {
item := GetAccountItemByName("ID card", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.IdCard = oldUser.IdCard
} else {
itemsChanged = append(itemsChanged, item)
@ -459,7 +471,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.IdCardType != newUser.IdCardType {
item := GetAccountItemByName("ID card type", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.IdCardType = oldUser.IdCardType
} else {
itemsChanged = append(itemsChanged, item)
@ -467,10 +479,13 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
oldUserPropertiesJson, _ := json.Marshal(oldUser.Properties)
if newUser.Properties == nil {
newUser.Properties = make(map[string]string)
}
newUserPropertiesJson, _ := json.Marshal(newUser.Properties)
if string(oldUserPropertiesJson) != string(newUserPropertiesJson) {
item := GetAccountItemByName("Properties", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Properties = oldUser.Properties
} else {
itemsChanged = append(itemsChanged, item)
@ -479,7 +494,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.PreferredMfaType != newUser.PreferredMfaType {
item := GetAccountItemByName("Multi-factor authentication", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.PreferredMfaType = oldUser.PreferredMfaType
} else {
itemsChanged = append(itemsChanged, item)
@ -490,13 +505,14 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
oldUser.Groups = []string{}
}
oldUserGroupsJson, _ := json.Marshal(oldUser.Groups)
if newUser.Groups == nil {
newUser.Groups = []string{}
}
newUserGroupsJson, _ := json.Marshal(newUser.Groups)
if string(oldUserGroupsJson) != string(newUserGroupsJson) {
item := GetAccountItemByName("Groups", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Groups = oldUser.Groups
} else {
itemsChanged = append(itemsChanged, item)
@ -514,7 +530,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
newUserAddressJson, _ := json.Marshal(newUser.Address)
if string(oldUserAddressJson) != string(newUserAddressJson) {
item := GetAccountItemByName("Address", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Address = oldUser.Address
} else {
itemsChanged = append(itemsChanged, item)
@ -523,7 +539,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if newUser.FaceIds != nil {
item := GetAccountItemByName("Face ID", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.FaceIds = oldUser.FaceIds
} else {
itemsChanged = append(itemsChanged, item)
@ -532,7 +548,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.IsAdmin != newUser.IsAdmin {
item := GetAccountItemByName("Is admin", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.IsAdmin = oldUser.IsAdmin
} else {
itemsChanged = append(itemsChanged, item)
@ -541,7 +557,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.IsForbidden != newUser.IsForbidden {
item := GetAccountItemByName("Is forbidden", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.IsForbidden = oldUser.IsForbidden
} else {
itemsChanged = append(itemsChanged, item)
@ -549,7 +565,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.IsDeleted != newUser.IsDeleted {
item := GetAccountItemByName("Is deleted", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.IsDeleted = oldUser.IsDeleted
} else {
itemsChanged = append(itemsChanged, item)
@ -557,7 +573,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.NeedUpdatePassword != newUser.NeedUpdatePassword {
item := GetAccountItemByName("Need update password", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.NeedUpdatePassword = oldUser.NeedUpdatePassword
} else {
itemsChanged = append(itemsChanged, item)
@ -565,7 +581,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
}
if oldUser.IpWhitelist != newUser.IpWhitelist {
item := GetAccountItemByName("IP whitelist", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.IpWhitelist = oldUser.IpWhitelist
} else {
itemsChanged = append(itemsChanged, item)
@ -574,7 +590,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Balance != newUser.Balance {
item := GetAccountItemByName("Balance", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Balance = oldUser.Balance
} else {
itemsChanged = append(itemsChanged, item)
@ -583,7 +599,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Score != newUser.Score {
item := GetAccountItemByName("Score", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Score = oldUser.Score
} else {
itemsChanged = append(itemsChanged, item)
@ -592,7 +608,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Karma != newUser.Karma {
item := GetAccountItemByName("Karma", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Karma = oldUser.Karma
} else {
itemsChanged = append(itemsChanged, item)
@ -601,7 +617,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Language != newUser.Language {
item := GetAccountItemByName("Language", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Language = oldUser.Language
} else {
itemsChanged = append(itemsChanged, item)
@ -610,7 +626,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Ranking != newUser.Ranking {
item := GetAccountItemByName("Ranking", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Ranking = oldUser.Ranking
} else {
itemsChanged = append(itemsChanged, item)
@ -619,7 +635,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Currency != newUser.Currency {
item := GetAccountItemByName("Currency", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Currency = oldUser.Currency
} else {
itemsChanged = append(itemsChanged, item)
@ -628,7 +644,7 @@ func CheckPermissionForUpdateUser(oldUser, newUser *User, isAdmin bool, allowDis
if oldUser.Hash != newUser.Hash {
item := GetAccountItemByName("Hash", organization)
if item == nil {
if !userVisible(isAdmin, item) {
newUser.Hash = oldUser.Hash
} else {
itemsChanged = append(itemsChanged, item)

View File

@ -0,0 +1,56 @@
// Copyright 2025 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 (
"encoding/json"
"fmt"
"io"
"strings"
"github.com/beego/beego/context"
)
var forbiddenChars = `/?:@#&%=+;`
func FieldValidationFilter(ctx *context.Context) {
if ctx.Input.Method() != "POST" {
return
}
urlPath := ctx.Request.URL.Path
if !(strings.HasPrefix(urlPath, "/api/add-") || strings.HasPrefix(urlPath, "/api/update-")) {
return
}
bodyBytes, err := io.ReadAll(ctx.Request.Body)
if err != nil || len(bodyBytes) == 0 {
return
}
ctx.Request.Body = io.NopCloser(strings.NewReader(string(bodyBytes)))
var requestData map[string]interface{}
if err := json.Unmarshal(bodyBytes, &requestData); err != nil {
return
}
if value, ok := requestData["name"].(string); ok {
if strings.ContainsAny(value, forbiddenChars) {
responseError(ctx, fmt.Sprintf("Field 'name' contains forbidden characters: %q", forbiddenChars))
return
}
}
}

View File

@ -13,7 +13,7 @@
// limitations under the License.
import React from "react";
import {Button, Card, Col, ConfigProvider, Input, InputNumber, Popover, Radio, Result, Row, Select, Space, Switch, Upload} from "antd";
import {Button, Card, Col, ConfigProvider, Input, InputNumber, Popover, Radio, Result, Row, Select, Space, Switch, Upload, message} from "antd";
import {CopyOutlined, HolderOutlined, LinkOutlined, UploadOutlined, UsergroupAddOutlined} from "@ant-design/icons";
import * as ApplicationBackend from "./backend/ApplicationBackend";
import * as CertBackend from "./backend/CertBackend";
@ -279,6 +279,13 @@ class ApplicationEditPage extends React.Component {
</Col>
<Col span={22} >
<Input value={this.state.application.name} disabled={this.state.application.name === "app-built-in"} onChange={e => {
const value = e.target.value;
if (/[/?:@#&%=+;]/.test(value)) {
const invalidChars = "/ ? : @ # & % = + ;";
const messageText = i18next.t("application:Invalid characters in application name") + ":" + " " + invalidChars;
message.error(messageText);
return;
}
this.updateApplicationField("name", e.target.value);
}} />
</Col>

View File

@ -371,11 +371,6 @@ class ProviderEditPage extends React.Component {
{id: "Third-party", name: i18next.t("provider:Third-party")},
]
);
} else if (type === "Aliyun Captcha") {
return [
{id: "nc", name: i18next.t("provider:Sliding Validation")},
{id: "ic", name: i18next.t("provider:Intelligent Validation")},
];
} else {
return [];
}
@ -674,7 +669,7 @@ class ProviderEditPage extends React.Component {
</Col>
</Row>
{
this.state.provider.type !== "WeCom" && this.state.provider.type !== "Infoflow" && this.state.provider.type !== "Aliyun Captcha" ? null : (
this.state.provider.type !== "WeCom" && this.state.provider.type !== "Infoflow" ? null : (
<React.Fragment>
<Row style={{marginTop: "20px"}} >
<Col style={{marginTop: "5px"}} span={2}>

View File

@ -85,22 +85,31 @@ export const CaptchaWidget = (props) => {
break;
}
case "Aliyun Captcha": {
window.AliyunCaptchaConfig = {
region: "cn",
prefix: clientSecret2,
};
const AWSCTimer = setInterval(() => {
if (!window.AWSC) {
loadScript("https://g.alicdn.com/AWSC/AWSC/awsc.js");
if (!window.initAliyunCaptcha) {
loadScript("https://o.alicdn.com/captcha-frontend/aliyunCaptcha/AliyunCaptcha.js");
}
if (window.AWSC) {
if (window.initAliyunCaptcha) {
if (clientSecret2 && clientSecret2 !== "***") {
window.AWSC.use(subType, function(state, module) {
module.init({
appkey: clientSecret2,
scene: clientId2,
renderTo: "captcha",
success: function(data) {
onChange(`SessionId=${data.sessionId}&AccessKeyId=${siteKey}&Scene=${clientId2}&AppKey=${clientSecret2}&Token=${data.token}&Sig=${data.sig}&RemoteIp=192.168.0.1`);
},
});
window.initAliyunCaptcha({
SceneId: clientId2,
mode: "embed",
element: "#captcha",
captchaVerifyCallback: (data) => {
onChange(data.toString());
},
slideStyle: {
width: 320,
height: 40,
},
language: "cn",
immediate: true,
});
}
clearInterval(AWSCTimer);

View File

@ -44,6 +44,12 @@ export const CaptchaModal = (props) => {
}
}, [visible]);
useEffect(() => {
if (captchaToken !== "" && captchaType !== "Default") {
handleOk();
}
}, [captchaToken]);
const handleOk = () => {
onOk?.(captchaType, captchaToken, clientSecret);
};
@ -138,19 +144,18 @@ export const CaptchaModal = (props) => {
if (!regex.test(captchaToken)) {
isOkDisabled = true;
}
} else if (captchaToken === "") {
isOkDisabled = true;
return [
null,
<Button key="ok" disabled={isOkDisabled} type="primary" onClick={handleOk}>{i18next.t("general:OK")}</Button>,
];
}
return [
<Button key="cancel" onClick={handleCancel}>{i18next.t("general:Cancel")}</Button>,
<Button key="ok" disabled={isOkDisabled} type="primary" onClick={handleOk}>{i18next.t("general:OK")}</Button>,
];
return null;
};
return (
<Modal
closable={false}
closable={true}
maskClosable={false}
destroyOnClose={true}
title={i18next.t("general:Captcha")}
@ -160,6 +165,7 @@ export const CaptchaModal = (props) => {
width={350}
footer={renderFooter()}
onCancel={handleCancel}
afterClose={handleCancel}
onOk={handleOk}
>
<div style={{marginTop: "20px", marginBottom: "50px"}}>